PaaS 篇(七):自建 MinIO 分布式纠删码 S3 集群,并做容错演练

PaaS 篇(七):自建 MinIO 分布式纠删码 S3 集群,并做容错演练
PaaS 篇七自建 MinIO 分布式纠删码 S3 集群并做容错演练系列《基于华为云 FlexusX 四节点集群的云计算全栈实操》—— PaaS 篇实测环境华为云 FlexusX 8vCPU/16GiB × 4 节点Ubuntu 24.04.4 LTS本文所有性能数字、状态输出均来自真实实验结果文件results/12_minio.txt与results/13_minio_failover.txt未做任何修饰。一、引子对象存储是 PaaS 层最容易被当成黑盒的服务上传下载调个 SDK 就完事没人关心它怎么抗磁盘坏、怎么跨节点分布。但作为工程师一旦你的业务要把对象存储当数据底座训练样本、日志归档、镜像仓库后端你就必须搞清楚两件事它到底是怎么容忍磁盘/节点故障的自建和直接用云厂商的 OBS/OSS/S3 到底差在哪本篇我在华为云 FlexusX 上用 Docker 拉起一套 MinIO 分布式纠删码集群6 块盘横跨 3 个节点然后用docker stop干掉一个节点验证掉 1 个节点、2 块盘时集群还能不能读写、数据是否完整。结论先放这能而且字节级完整。二、背景与理论对照《深入浅出云计算》PaaS 篇《深入浅出云计算》在 PaaS 篇里把对象存储归类为无需关心底层文件系统的海量 KV 存储核心三要素Bucket、Object、Key。但书里对高可用是怎么来的着墨有限这里补上工程视角。2.1 多副本 vs 纠删码对象存储抗丢数据靠两种方式方式原理空间放大容忍失败多副本如 3 副本同一份数据存 N 份3×任意 2 份丢失仍可服务纠删码 EC:M把数据切成 K 个数据块 M 个校验块共 KM 块任意 M 块丢失可重建(KM)/K任意 M 块丢失仍可服务我的集群是6 块盘、EC:3即 K3、M3数据切成 3 块、算出 3 块校验总共 6 块条带erasure stripe size 6。允许任意 3 块盘同时离线而不丢数据。相比 3 副本方案6 盘只存 2 盘有效数据纠删码把 6 盘全部用上空间利用率从 33% 提升到 100%有效容量 总容量代价是重建时要算力XOR/Reed-Solomon。观点对象存储不是存文件是把一份对象打散成条带撒到一堆盘上再让校验块兜底。理解这一点你才看得懂后面6 drives online, EC:3那行状态输出。2.2 MinIO 的分布式形态MinIO 单机模式就是单盘分布式模式是把多个http://host:port/path地址作为盘喂给minio server它自动按纠删码集合Erasure Set分组。我这里 6 个路径正好凑成 1 个 Erasure Setstripe size 6。三、架构图示┌─────────────────────────────────────────┐ S3 客户端 / mc │ MinIO 分布式集群 (EC:3) │ (host nethost) ──────▶│ http://192.168.0.252:9000 (n1) │ │ ├─ /data1 ├─ /data2 │ │ http://192.168.0.241:9000 (n3) │ │ ├─ /data1 ├─ /data2 │ │ http://192.168.0.150:9000 (n4) │ │ ├─ /data1 ├─ /data2 │ └─────────────────────────────────────────┘ 同 VPC 192.168.0.0/24 内网互通 对象写入 → 切 3 数据块 3 校验块 → 散列到 6 块盘每节点 2 块 容错演练docker stop n4 → 2 盘离线剩 4 盘仍可重建EC:3 余量充足真实踩坑背景原计划四节点全上n1/n2/n3/n4但node2124.70.93.52因并发 SSH 触发pam_faillock锁定了 root短时间无法批量部署。MinIO 集群因此改为n1/n3/n4 三节点搭建。这反而是个好案例——详见文末踩坑与排障。四、环境与准备节点弹性公网IP私有IP规格系统在集群中node1113.47.6.41192.168.0.2528vCPU/16GiBUbuntu 24.04.4 LTSMinIO 节点 ✅node2124.70.93.52192.168.0.648vCPU/16GiBUbuntu 24.04.4 LTS被锁未参与 ❌node31.94.220.182192.168.0.2418vCPU/16GiBUbuntu 24.04.4 LTSMinIO 节点 ✅node4124.70.102.139192.168.0.1508vCPU/16GiBUbuntu 24.04.4 LTSMinIO 节点 ✅四节点同属一个 VPC 子网192.168.0.0/24内网互通均已安装 Docker 并配置华为云镜像加速。准备动作每个 MinIO 节点执行一次# 创建两块盘的数据目录实际挂载点可按需替换这里是本地目录模拟盘mkdir-p/root/minio/data1 /root/minio/data2五、实操步骤完整可复现命令5.1 启动分布式 MinIO三节点各执行一次脚本scripts/minio_start.sh的核心就是一条命令——把 6 个host:port/path一股脑交给minio server它会自己发现彼此并组成分布式集。dockerrm-fminio2/dev/null||truemkdir-p/root/minio/data1 /root/minio/data2dockerrun-d--nameminio--restartalways--nethost\-eMINIO_ROOT_USERminioadmin-eMINIO_ROOT_PASSWORDMinio2026pass\-v/root/minio/data1:/data1-v/root/minio/data2:/data2\minio/minio server\http://192.168.0.252:9000/data1 http://192.168.0.252:9000/data2\http://192.168.0.241:9000/data1 http://192.168.0.241:9000/data2\http://192.168.0.150:9000/data1 http://192.168.0.150:9000/data2\--console-address:9001/dev/null21echominio container started on$(hostname)要点解释--nethostMinIO 节点间要做健康检查与数据同步必须能直接走宿主机内网 IP 互访。host网络模式避免了端口映射带来的 NAT 二次封装也省去ports映射的维护成本。后面 mc 客户端同样用--nethost原因一样。6 个http://.../data{N}是盘而非节点MinIO 以盘为单位做条带。--console-address :9001把 Web 控制台单独绑在 9001和 API 的 9000 分开。5.2 mc 客户端操作为什么用容器执行 mc我全程不在宿主机装 mc 二进制而是用minio/mc镜像临时起容器。关键在于必须用--nethost否则容器默认桥接网络里127.0.0.1:9000指向的是容器自己连不到宿主机的 MinIO。# 建立别名指向本机 n1 的 MinIO 端点集群内部会自动纠删码路由MCV(){dockerrun--rm--nethost-v/root/.mc:/root/.mc--entrypointmcminio/mc:latest$;}MCValiassetcl http://127.0.0.1:9000 minioadmin Minio2026pass常用 mc 操作MCV admin info cl# 查看集群/盘/纠删码状态MCV mb cl/demo-bucket# 建桶MCVcp/tmp/blob.bin cl/demo-bucket/blob.bin# 上传MCVlscl/demo-bucket# 列对象MCVcatcl/demo-bucket/hello.txt# 读对象内容MCVstatcl/demo-bucket/blob.bin# 看对象元数据 ETag六、真实输出贴实测未篡改6.1 健康态6 盘在线EC:3来自results/12_minio.txt集群刚起 2 分钟时的admin info● 192.168.0.150:9000 Uptime: 2 minutes Version: 2025-09-07T16:13:09Z Network: 3/3 OK Drives: 2/2 OK ● 192.168.0.241:9000 Uptime: 2 minutes Network: 3/3 OK Drives: 2/2 OK ● 192.168.0.252:9000 Uptime: 2 minutes Network: 3/3 OK Drives: 2/2 OK ┌──────┬────────────────────────┬─────────────────────┬──────────────┐ │ Pool │ Drives Usage │ Erasure stripe size │ Erasure sets │ │ 1st │ 14.5% (total: 113 GiB) │ 6 │ 1 │ └──────┴────────────────────────┴─────────────────────┴──────────────┘ 6 drives online, 0 drives offline, EC:3Erasure stripe size 6 印证了3 数据 3 校验的条带结构EC:3即最多容忍 3 盘离线。上传 20MB 随机二进制对象的速度/tmp/blob.bin - cl/demo-bucket/blob.bin ┌───────────┬─────────────┬──────────┬──────────────┐ │ Total │ Transferred │ Duration │ Speed │ │ 20.00 MiB │ 20.00 MiB │ 00m00s │ 244.21 MiB/s │ └───────────┴─────────────┴──────────┴──────────────┘文本对象读取与 stat$ mc cat cl/demo-bucket/hello.txt Hello Object Storage from Huawei Cloud FlexusX cluster Sun Jul 26 08:09:29 UTC 2026 $ mc stat cl/demo-bucket/blob.bin Name : blob.bin Date : 2026-07-26 08:09:29 UTC Size : 20 MiB ETag : 316f5c3cf34e6206e74aaeb58001dd98-2 Type : file Metadata : Content-Type: application/octet-stream注意 ETag 末尾的-2MinIO 对纠删码对象会在 MD5 后追加-NN 为分片数/part 数这是它区别于标准 S3 单 MD5 的实现细节做兼容性校验时要留意。6.2 故障演练停掉 n42 盘离线docker stop minio干掉 n4 后results/13_minio_failover.txt● 192.168.0.150:9000 Uptime: offline Drives: 0/2 OK ● 192.168.0.241:9000 Uptime: 6 minutes Network: 2/3 OK Drives: 2/2 OK ● 192.168.0.252:9000 Uptime: 7 minutes Network: 2/3 OK Drives: 2/2 OK 20 MiB Used, 1 Bucket, 2 Objects 1 node offline, 4 drives online, 2 drives offline, EC:3结论EC:3 允许最多 3 盘离线当前仅 2 盘离线集群保持可用没进只读、没宕机。故障态下读已有文本对象——在线重建成功$ mc cat cl/demo-bucket/hello.txt Hello Object Storage from Huawei Cloud FlexusX cluster Sun Jul 26 08:09:29 UTC 2026故障态下下载 20MB 对象并做字节级校验cl/demo-bucket/blob.bin - /tmp/blob_dl.bin Total 20.00 MiB | Duration 00m00s | Speed 463.13 MiB/s $ ls -l /tmp/blob_dl.bin -rw-r--r-- 1 root root 20971520 Jul 26 16:14 /tmp/blob_dl.bin ( 20MiB字节数精确一致) $ mc stat cl/demo-bucket/blob.bin ETag: 316f5c3cf34e6206e74aaeb58001dd98-2 Size: 20 MiB下载速度 463.13 MiB/s 反而比健康态的 244.21 MiB/s 还高——因为只剩 4 块盘参与重建、参与竞争的盘少了且重建读的是条带子集这个反直觉现象值得记一笔。ETag 与字节数20971520和健康态完全一致证明数据完整性无损。故障降级期间写入新对象并读回$ mc cp /tmp/during_fail.txt cl/demo-bucket/during_fail.txt Total 64 B | Speed 4.68 KiB/s 写入成功 $ mc cat cl/demo-bucket/during_fail.txt Sun Jul 26 04:14:45 PM CST 2026 written-during-2-drives-offline降级期间集群仍可写新对象也能正确读回。6.3 故障恢复start n4自愈【故障恢复】n4 docker start minio 恢复后集群状态 6 drives online, 0 drives offline, EC:3 节点重新上线后自动纳管集群自愈至健康状态无需人工干预MinIO 自动把 n4 的 2 块盘重新纳入条带并补齐数据。七、深度解读重点7.1 纠删码到底在故障时干了什么当 n4 的 2 块盘消失原本 6 块的条带只剩 4 块。因为 EC:3 的语义是任意 3 块可重建剩下 4 块≥ K3足够还原出任意一块缺失数据。读blob.bin时MinIO 从存活的 4 块中读出 3 个有效分片用 Reed-Solomon 解出被存在 n4 上的那 1 个数据/校验分片拼回完整 20MB。这一切对客户端透明你只会看到速度变化不会看到报错。7.2 为什么可读也可写比只读不写更有价值多副本挂掉一个副本通常只能保证还能读写要等副本恢复或降级处理。而纠删码在剩余盘数 ≥ K时读写都不受限——因为每次写都会重新计算校验块并分布到所有存活盘。这也是为什么我特意验证了降级期间写入 during_fail.txt 并读回这一步它证明了集群在容灾态下仍具备完整服务能力而非仅能应急读。7.3 性能数字背后的工程含义场景上传 20MB 速度下载 20MB 速度健康6 盘244.21 MiB/s—降级4 盘n4 离线4.68 KiB/s小文件463.13 MiB/s上传那行 4.68 KiB/s 是 64B 的during_fail.txt小文件单位看着吓人实则总量极小不具参考意义真正有信息量的是下载从 244 涨到 463 MiB/s——说明重建读路径上参与竞争的盘少了、条带更集中不是性能退化的信号。这个细节提醒我们看对象存储性能一定要区分小文件元数据路径和大对象数据路径别被小文件的荒诞带宽误导。八、踩坑与排障真实的坑8.1 mc 精简镜像没有 grep我最初把整段测试塞进一个minio/mc容器的sh -c ...里想用mc admin info cl | grep -iE Erasure...过滤。结果[stderr] sh: line 28: grep: command not foundminio/mc是基于精简基础镜像的里面没有 grep、awk 这类 GNU 工具。解决把grep/过滤交给宿主机 bash每条 mc 命令用独立容器执行见scripts/minio_failover_test.sh的MCV()函数。8.2 内联 shell 含中英文括号导致 syntax error更早的版本里我在sh -c的多行脚本里写了中文括号注释如预期 4 online / 2 offlineshell 把中文全角括号当成了语法结构直接syntax error。教训写进容器执行的脚本保持纯 ASCII注释拿到宿主机 bash 层输出。最终方案是每个MCV调用只跑一条 mc 命令echo 说明文字由 n1 宿主机的 bash 负责。8.3 node2 被 pam_faillock 锁定的运维教训本集群的四节点变三节点不是设计是事故编写批量部署脚本时对 node2 的并发 SSH 连接次数过多触发了 Ubuntu 默认的pam_faillockroot 被锁。这暴露两个问题自动化脚本要有重试/并发上限别用for循环对同主机猛打 SSH。生产环境关键节点要预留带外通道如云控制台的 VNC/串口否则 root 被 pam 锁死时你连进去解鎖的门都没有。最终 MinIO 用 n1/n3/n4 三节点照样把容错演练跑通反而说明良好设计的分布式系统对单点缺失有韧性——但这韧性不该被用来为运维疏忽买单。九、自建 MinIO vs 云 OSS/OBS/S3成本与场景维度自建 MinIO本实验云 OBS/OSS/S3存储成本仅付 FlexusX 实例 云盘费无流量/请求费按量存储费 公网流出费 API 请求费可用性 SLA靠自己运维EC:3 容忍 3 盘坏但无官方 SLA通常 99.9%~99.99% SLA厂商兜底运维负担你要管升级、监控、扩盘、故障切换基本零运维公网访问需自己配反代/HTTPS/鉴权原生 HTTPS 细粒度 ACL CDN适用性内网大数据底座、合规数据不出域、成本敏感且量大公网分发、托管静态站、与云上其他 PaaS 联动我的观点数据量大、主要在 VPC 内流转、且对数据不出私有环境有合规要求时自建 MinIO 长期成本明显更低且控制权完全在自己手里但若要公网分发、要省心、要 SLA直接用云厂商对象存储。别为了显得技术强而自建一个你没精力运维的对象存储——它出问题时背锅的是你。十、小结本篇在华为云 FlexusX 三节点上跑通了 MinIO 分布式纠删码集群6 盘、EC:3并用真实故障注入证明了健康态 6 盘在线20MB 对象上传 244.21 MiB/sETag316f5c3cf34e6206e74aaeb58001dd98-2停掉 1 节点2 盘离线后集群仍可读、可写、可下载20MB 下载 463.13 MiB/s、字节数精确20971520docker start后自动自愈回 6 盘在线。纠删码不是魔法它用算力换空间在容忍同样多故障的前提下比多副本省一大笔盘。但省下的盘钱要拿运维精力来填。参考脚本本篇命令均来自仓库scripts/scripts/minio_start.sh—— 三节点分布式启动scripts/minio_test.sh—— S3 功能与纠删码验证scripts/minio_failover_test.sh—— 容错演练实测结果results/12_minio.txt、results/13_minio_failover.txt下一篇八我们切到另一个 PaaS 核心PostgreSQL 16 流复制主从架构实战。