凌晨 2 点,你的手机响了。监控告警:“MinIO 集群存储使用率 85%,预计 7 天后满盘。”
你瞬间清醒——存储满了,业务就停摆。但怎么扩容?加一块硬盘?加一台服务器?还是把整个集群推倒重来?
如果你正在用 MinIO 分布式集群,这篇文章能救你。因为 MinIO 的扩容逻辑和传统的存储系统完全不同:你不能简单地"加一块盘",而是必须加一套完整的 Server Pool。
我会把 MinIO 的扩容机制、容量规划方法论、实操步骤和避坑指南,一次性讲清楚。
为什么不能只加一块盘?
在传统的 RAID 或单机存储系统里,扩容很简单:买个新硬盘,插上去,扩展卷,搞定。
但在 MinIO 的分布式架构里,这个做法行不通。原因有三:
1. 纠删码的约束
MinIO 用纠删码(Erasure Code)保护数据。数据被切分成多个块,分散存储在不同节点上。如果你只加一块盘,新盘无法融入已有的纠删码组,数据分布会乱套。
2. 节点对等原则
MinIO 的分布式架构要求所有节点"对等"——每个节点承担相同的角色,存储相同数量的数据。加一台服务器只配一块盘,会破坏这种对等性,导致负载不均。
3. 元数据一致性
MinIO 的元数据(对象位置、版本、策略等)分布在所有节点上。局部扩容会导致元数据不一致,轻则性能下降,重则数据丢失。
结论:MinIO 的扩容单位不是"盘",也不是"节点",而是"Server Pool"(服务器池)。一套 Pool 是一个完整的、独立的存储单元,包含自己的节点、磁盘、纠删码组和元数据。
Server Pool 是什么?
你可以把 MinIO 集群想象成一个Server Pool 的联盟:
┌─────────────────────────────────────────────┐
│ MinIO 集群 │
│ ┌─────────────────────────────────────┐ │
│ │ Server Pool 1 (旧) │ │
│ │ Node 1 Node 2 Node 3 Node 4 │ │
│ │ ├4TB┤ ├4TB┤ ├4TB┤ ├4TB┤ │ │
│ │ 总容量: 16TB (可用 ~10TB) │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ Server Pool 2 (新) │ │
│ │ Node 5 Node 6 Node 7 Node 8 │ │
│ │ ├8TB┤ ├8TB┤ ├8TB┤ ├8TB┤ │ │
│ │ 总容量: 32TB (可用 ~20TB) │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ 总可用容量: ~30TB │
└─────────────────────────────────────────────┘
Server Pool 的特点:
- 每个 Pool 是独立的:有自己的节点、磁盘、纠删码配置
- Pool 之间数据不迁移:旧数据留在旧 Pool,新数据优先写入新 Pool
- Pool 之间对等:多个 Pool 共同对外提供统一的 S3 API 接口
- 扩容就是加 Pool:不需要改动已有的 Pool,风险可控
容量规划:扩容前必须算清楚的账
扩容不是"满了再加",而是提前规划。下面这套方法论,是我经历过多次生产扩容后的总结。
第一步:摸清当前数据增长趋势
# 查看当前 Bucket 的数据量和对象数
mc ls --summarize myminio/
# 查看每个 Bucket 的月度增长(如果有历史监控数据)
# 或者用脚本统计
for bucket in $(mc ls myminio/ | awk '{print $5}'); do
echo "=== $bucket ==="
mc ls --summarize myminio/$bucket
done
关键指标:
- 当前总容量:X TB
- 月增长率:Y%(建议取过去 6 个月的平均值)
- 峰值增长率:Z%(双 11、年底等业务高峰)
第二步:预测未来容量需求
简单公式:
未来容量 = 当前容量 × (1 + 月增长率)^N
其中 N 是规划周期(月)。建议按 12 个月规划。
示例:
- 当前容量:10 TB
- 月增长率:8%
- 规划周期:12 个月
- 未来容量 = 10 × (1.08)^12 ≈ 25 TB
第三步:计算冗余预留
预留包括三部分:
- 安全水位:磁盘使用率不超过 80%(留 20% 缓冲)
- 峰值缓冲:预留 30% 应对业务高峰
- Pool 对齐:MinIO 的 Pool 容量通常是 4 的倍数(4 节点 × N 块盘)
总需求 = 未来容量 × 1.25(安全水位) × 1.3(峰值缓冲)
接上例:25 TB × 1.25 × 1.3 ≈ 40 TB
第四步:换算成硬件配置
假设单节点配置 4 块盘,EC:2(可用空间 50%):
总可用容量 = 节点数 × 单盘容量 × 盘数 × 纠删码利用率
40 TB = 4 节点 × 单盘容量 × 4 盘 × 0.5
单盘容量 = 40 / (4 × 4 × 0.5) = 5 TB
结论:新 Pool 需要 4 节点,每节点 4 块 5TB 盘(或 8TB 盘,留更多余量)。
新 Pool 的硬件配置要求
MinIO 对新 Pool 有一个硬性要求:必须与旧 Pool “同构"或"兼容”。
什么是"同构"?
| 维度 | 旧 Pool | 新 Pool | 要求 |
|---|---|---|---|
| 节点数 | 4 | 4 | 必须相同 |
| 每节点盘数 | 4 | 4 | 必须相同 |
| 单盘容量 | 4TB | 8TB | 可以不同 |
| 纠删码配置 | EC:2 | EC:2 | 必须相同 |
| 网络带宽 | 万兆 | 万兆 | 建议相同 |
核心原则:节点数、盘数、纠删码配置必须一致。单盘容量可以不同(新盘更大),但盘数和节点数不能变。
为什么必须同构?
- 数据分布算法:MinIO 的纠删码计算基于节点数和盘数。如果新旧 Pool 结构不同,数据分布算法会出错
- 负载均衡:Pool 之间需要均匀分配请求。节点数不同会导致请求倾斜
- 故障恢复:节点故障时,纠删码恢复需要固定的数学模型。结构不一致会导致恢复失败
如果旧 Pool 是 4 节点,我想扩到 8 节点怎么办?
不能直接扩到 8 节点。MinIO 不支持在现有 Pool 上增加节点。
正确做法:
- 新建一个 8 节点的 Pool 2
- 把 Pool 1 的数据迁移到 Pool 2
- 退役 Pool 1
或者:
- 新建一个 4 节点的 Pool 2
- 两个 Pool 并存(总容量 = Pool 1 + Pool 2)
扩容实操:从零添加一个新 Server Pool
假设你的集群已经有一个 Pool 1(4 节点,每节点 4 块 4TB 盘,EC:2),现在要添加 Pool 2(4 节点,每节点 4 块 8TB 盘,EC:2)。
第一步:准备新 Pool 的硬件
新 Pool 的节点配置:
| 节点 | 内网 IP | 磁盘配置 |
|---|---|---|
| minio-05 | 192.168.1.105 | 4 × 8TB SSD |
| minio-06 | 192.168.1.106 | 4 × 8TB SSD |
| minio-07 | 192.168.1.107 | 4 × 8TB SSD |
| minio-08 | 192.168.1.108 | 4 × 8TB SSD |
系统准备(参照第 14 篇文章):
# 1. 格式化磁盘为 XFS
mkfs.xfs -f /dev/sdb
mkfs.xfs -f /dev/sdc
mkfs.xfs -f /dev/sdd
mkfs.xfs -f /dev/sde
# 2. 挂载到 /data/minio/disk{1..4}
mkdir -p /data/minio/disk{1,2,3,4}
mount /dev/sdb /data/minio/disk1
mount /dev/sdc /data/minio/disk2
mount /dev/sdd /data/minio/disk3
mount /dev/sde /data/minio/disk4
# 3. 写入 /etc/fstab
echo '/dev/sdb /data/minio/disk1 xfs defaults,noatime 0 2' >> /etc/fstab
echo '/dev/sdc /data/minio/disk2 xfs defaults,noatime 0 2' >> /etc/fstab
echo '/dev/sdd /data/minio/disk3 xfs defaults,noatime 0 2' >> /etc/fstab
echo '/dev/sde /data/minio/disk4 xfs defaults,noatime 0 2' >> /etc/fstab
# 4. 创建 minio 用户
useradd -r -s /sbin/nologin minio
chown -R minio:minio /data/minio
# 5. 下载 MinIO 二进制(与 Pool 1 相同版本)
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
mv minio /usr/local/bin/
第二步:编写新 Pool 的启动脚本
每个新节点的 MINIO_VOLUMES 配置:
# /etc/default/minio-pool2
# Pool 2 的节点列表
MINIO_VOLUMES="http://minio-0{5...8}/data/minio/disk{1...4}"
# 注意:Pool 1 的节点也需要加入这个列表!
# 正确的写法是列出所有 Pool 的所有节点
等等,这里有个关键细节。
MinIO 扩容时,新 Pool 的启动命令里必须包含所有已有 Pool 的节点信息。MinIO 通过启动命令中的节点列表来识别哪些节点属于哪个 Pool。
正确的 MINIO_VOLUMES 写法:
# 所有 Pool 的节点都要列出来
MINIO_VOLUMES="http://minio-0{1...4}/data/minio/disk{1...4} http://minio-0{5...8}/data/minio/disk{1...4}"
# 或者用展开写法
MINIO_VOLUMES="http://minio-01/data/minio/disk{1...4} http://minio-02/data/minio/disk{1...4} http://minio-03/data/minio/disk{1...4} http://minio-04/data/minio/disk{1...4} http://minio-05/data/minio/disk{1...4} http://minio-06/data/minio/disk{1...4} http://minio-07/data/minio/disk{1...4} http://minio-08/data/minio/disk{1...4}"
第三步:停掉旧 Pool,统一启动
这是扩容过程中最危险的一步。MinIO 要求所有节点同时重启,才能识别新的 Pool 结构。
# 1. 先备份现有数据(虽然理论上不需要,但生产环境建议做)
mc mirror myminio/ /backup/minio-pre-expand/
# 2. 停止所有旧 Pool 节点
systemctl stop minio@node1
systemctl stop minio@node2
systemctl stop minio@node3
systemctl stop minio@node4
# 3. 更新旧 Pool 节点的 MINIO_VOLUMES,加入新 Pool 节点
# 编辑 /etc/default/minio,更新 VOLUMES 变量
# 4. 启动所有节点(旧 Pool + 新 Pool)
# 建议用脚本并行启动,减少停机时间
for node in minio-01 minio-02 minio-03 minio-04 minio-05 minio-06 minio-07 minio-08; do
ssh $node "systemctl start minio"
done
第四步:验证扩容结果
# 查看集群状态
mc admin info myminio
# 输出示例:
# ● minio-01:9000
# Uptime: 1 minute
# Version: 2026-06-15T00:00:00Z
# Network: 4/4 OK
# Drives: 4/4 OK
# Pool: 1st
#
# ● minio-05:9000
# Uptime: 1 minute
# Version: 2026-06-15T00:00:00Z
# Network: 4/4 OK
# Drives: 4/4 OK
# Pool: 2nd
# 查看总容量
mc ls --summarize myminio/
# 查看数据分布
mc admin heal myminio/
扩容期间的影响评估
很多运维同学最关心的就是:扩容期间业务会不会中断?
停机时间
- 理论上:MinIO 支持滚动重启,但扩容(添加 Pool)需要所有节点同时重启以识别新的 Pool 结构
- 实际上:停机时间取决于集群规模和网络速度。4+4 节点的集群,通常在 2~5 分钟内完成重启
- 建议:在业务低峰期操作,并提前通知业务方
数据一致性
- 扩容过程中,已有数据不会迁移。旧数据留在旧 Pool,新数据写入新 Pool
- 扩容后,读请求可以正常访问所有数据(新旧 Pool 都对外提供服务)
- 写请求优先写入新 Pool(MinIO 的默认策略是优先写入可用空间更大的 Pool)
性能影响
- 扩容后的前 24 小时,集群会进行后台的数据平衡(Heal)操作
- 这期间性能可能略有下降(5%~10%)
- 建议扩容后观察监控,确认 Heal 完成后再进入高峰期
历史 Pool 的退役与数据迁移
如果旧 Pool 的硬件已经老旧,或者你想合并 Pool,需要退役旧 Pool。
数据迁移方案
方案一:mc mirror(推荐)
# 1. 创建新 Pool 的别名
mc alias set myminio-new http://minio-new-pool:9000 admin password
# 2. 全量镜像
mc mirror myminio/ myminio-new/
# 3. 验证数据一致性
mc diff myminio/ myminio-new/
# 4. 切换业务连接到新 Pool
# 5. 退役旧 Pool
方案二:Bucket 级复制(适合部分迁移)
# 只迁移特定 Bucket
mc mirror myminio/important-bucket myminio-new/important-bucket
方案三:应用层双写(适合在线业务)
# 在应用层同时写入新旧 Pool
# 旧 Pool 只读,新 Pool 读写
# 观察一段时间后,完全切换到新 Pool
退役旧 Pool
# 1. 确认所有数据已迁移
mc ls myminio/ # 检查 Bucket 是否为空
# 2. 从集群中移除旧 Pool 节点
# 编辑所有节点的 MINIO_VOLUMES,删除旧 Pool 节点
# 重启所有剩余节点
# 3. 清理旧节点
systemctl stop minio
rm -rf /data/minio/*
常见问题排查
扩容后 mc admin info 显示 Pool 数量不对
检查 MINIO_VOLUMES 的语法是否正确。MinIO 通过 URL 格式来区分 Pool:
# 错误的写法(所有节点混在一起)
MINIO_VOLUMES="http://minio-01/disk1 http://minio-02/disk1 ..."
# 正确的写法(按 Pool 分组)
MINIO_VOLUMES="http://minio-0{1...4}/data/minio/disk{1...4} http://minio-0{5...8}/data/minio/disk{1...4}"
扩容后数据写入失败
检查新 Pool 节点的磁盘权限和挂载是否正确:
# 检查磁盘挂载
lsblk
# 检查权限
ls -ld /data/minio/disk*
# 检查 MinIO 日志
journalctl -u minio -f
扩容后性能反而下降
可能原因:
- 新旧 Pool 的网络带宽不一致(新 Pool 用了千兆网,旧 Pool 是万兆)
- 新 Pool 的磁盘性能差(HDD vs SSD)
- 后台 Heal 任务占用资源
解决方案:
- 确保新 Pool 硬件配置不低于旧 Pool
- 扩容后等待 Heal 完成再评估性能
- 检查网络配置
写在最后
MinIO 的扩容机制看起来复杂,但核心逻辑很简单:扩容单位是 Server Pool,不是单盘。只要记住这个原则,就不会犯"加一块盘"的低级错误。
总结三个关键点:
- 提前规划:用容量公式算清楚 12 个月后的需求,避免"告警了才扩容"的被动局面
- 同构扩容:新 Pool 的节点数、盘数、纠删码配置必须与旧 Pool 一致
- 谨慎操作:所有节点同时重启,建议低峰期操作,提前备份
如果你已经走到"存储告警"这一步,别慌。按本文的流程,半天内就能完成扩容。但如果你还没告警,建议现在就做一次容量规划——预防胜于治疗。
下一篇,我会讲 MinIO 的生命周期管理(ILM),教你如何自动把"老数据"迁移到廉价存储,让存储成本降下来。

被折叠的 条评论
为什么被折叠?



