MinIO 三节点分布式集群部署手册(Ubuntu 22.04 · 实测可用)
本文是一份经过真实部署验证的 MinIO 分布式集群部署手册。从空白裸盘开始,到集群可读写、容错演练通过,全程命令可直接复制执行。
文中的坑都是实际踩过的:官方下载源全站 410、环境变量三台不一致导致起不来、三台密码各自随机生成导致 403、容量算错一倍……
建议首次部署就按顺序走,不要跳步。
一、方案设计(先想清楚再动手)
1.1 参考环境
| 项目 | 值 |
|---|---|
| 节点 | 3 台,192.168.0.150 / .151 / .152(示例) |
| 系统 | Ubuntu 22.04,内核 5.15 |
| 数据盘 | 每节点 1 块 50 GiB 裸盘(/dev/sdb) |
| 系统盘 | /dev/sda,LVM 根分区(不要动) |
| 挂载点 | /data/minio |
| API / Console 端口 | 9000 / 9001 |
1.2 容量与容错:先算清楚
MinIO 的校验盘数量由每 pool 的盘数自动决定,不是随便配的:
| 每 pool 盘数 | 默认 EC | 数据/校验 | 可用比例 | 可容错 |
|---|---|---|---|---|
| 3 | EC:1 | 2 / 1 | 66% | 1 盘 |
| 4 ~ 5 | EC:2 | 2/2 或 3/2 | 50% ~ 60% | 2 盘 |
| 6 ~ 7 | EC:3 | 3/3 或 4/3 | 50% ~ 57% | 3 盘 |
| ≥ 8 | EC:4 | n-4 / 4 | 50% ~ 75% | 4 盘 |
3 节点 × 1 盘 = 3 盘,即 EC:1:
3 盘 = 2 份数据 + 1 份校验
裸容量 150 GiB → 可用 ≈ 100 GiB
容错:任意 1 块盘 / 1 台节点整体宕机,读写不中断
我踩过的坑:一开始按 EC:2 估算,以为只有 46 GiB 可用。实际部署后
mc admin info显示total: 100 GiB,正好是裸容量的 2/3,才反推出是 EC:1。验证方法:
total ÷ 裸盘总容量 = 可用比例,反推即可确认 EC 等级。
常见误解:3 盘不能容忍 2 台同时宕机。要容忍 2 台节点整体宕机,每台至少 2 盘、总盘数 ≥ 5。
1.3 磁盘布局原则
/dev/sdb (裸盘)
└─ mkfs.xfs
└─ mount /data/minio ← MinIO 视其为 1 个 drive
不要分区、不要 LVM、不要 RAID。 MinIO 自己管冗余,叠 RAID 会让纠删码失效、性能腰斩。
fstab 必须用 UUID + noatime,不要写 /dev/sdb——盘符可能漂移,重启后挂错盘或挂不上。
二、换环境前先改这 6 处
这份手册是在特定环境验证的,换环境时先把参数改掉再执行:
| # | 需要改的 | 示例值 | 说明 |
|---|---|---|---|
| 1 | 节点 IP(三处) | 192.168.0.150/151/152 |
各处命令 + unit 里的 MINIO_VOLUMES |
| 2 | 数据盘设备名 | /dev/sdb |
必须 lsblk 确认,可能是 /dev/vdb |
| 3 | 挂载点 | /data/minio |
改路径要同步改 unit |
| 4 | 二进制版本 | RELEASE.2025-07-23T15-54-02Z |
三台必须一致 |
| 5 | 账号密码 | admin / 自定义 |
三台必须一致 |
| 6 | 盘数 / 节点数 | 3 盘 | 变则容量与 EC 全变 |
三、部署前检查(三台都做)
# 1. 目标盘干净、未挂载
lsblk /dev/sdb
blkid /dev/sdb # 应无输出
# 2. 确认根分区在 sda
df -h /
# 3. 节点互通
for h in 192.168.0.150 192.168.0.151 192.168.0.152; do ping -c2 -W1 $h; done
# 4. 端口未占用
ss -lntp | grep -E ':(9000|9001)\b' # 应无输出
# 5. 时间同步(分布式必须)
timedatectl status # 需 "System clock synchronized: yes"
时间不同步会导致签名校验失败,报 RequestTimeTooSkewed。修复:
apt install -y systemd-timesyncd && timedatectl set-ntp true
四、获取 MinIO 二进制(官方源已下线)
4.1 必须用镜像站
dl.min.io 现在对所有社区版路径返回 410 Gone,只剩需要 license 的 AIStor 企业版。用 MinIO 中国镜像:
BASE=https://dl.minio.org.cn
VER=RELEASE.2025-07-23T15-54-02Z
curl -fL -o /usr/local/bin/minio "$BASE/server/minio/release/linux-amd64/archive/minio.$VER"
curl -fL -o /usr/local/bin/mc "$BASE/client/mc/release/linux-amd64/mc"
chmod +x /usr/local/bin/minio /usr/local/bin/mc
#或者用wget直接下例如下面
# wget https://dl.minio.org.cn/server/minio/release/linux-amd64/archive/minio.RELEASE.2025-07-23T15-54-02Z
# wget https://dl.minio.org.cn/client/mc/release/linux-amd64/mc
必须校验下载结果,防止下到 410 错误页(只有几百字节):
head -c4 /usr/local/bin/minio | od -c | grep -q '177 E L F' && echo "ELF 正常"
ls -lh /usr/local/bin/minio /usr/local/bin/mc # 约 105MB / 30MB
minio --version # 应显示对应 RELEASE
| 项目 | 期望 |
|---|---|
| 文件类型 | ELF 可执行文件 |
| minio | ≈ 105 MB |
| mc | ≈ 30 MB |
| 版本 | 三台完全一致 |
4.2 内网无法出网
在能上网的机器下载后分发:
scp /usr/local/bin/minio /usr/local/bin/mc root@192.168.0.151:/usr/local/bin/
ssh root@192.168.0.151 'chmod +x /usr/local/bin/minio /usr/local/bin/mc'
4.3 归档二进制(强烈建议)
镜像站是第三方资源,版本可能被清理——dl.min.io 就是这样从可用变成全站 410 的。部署成功后存一份到内网:
mkdir -p /opt/minio-archive/RELEASE.2025-07-23T15-54-02Z
cp /usr/local/bin/minio /usr/local/bin/mc /opt/minio-archive/RELEASE.2025-07-23T15-54-02Z/
cd /opt/minio-archive/RELEASE.2025-07-23T15-54-02Z
sha256sum minio mc > SHA256SUMS
以后扩容、重装、升级回滚都不再依赖外网。
五、磁盘准备(三台都做)
# 确认是空白盘再执行!会清空数据
mkfs.xfs -f -L minio-data /dev/sdb
mkdir -p /data/minio
UUID=$(blkid -s UUID -o value /dev/sdb)
cp /etc/fstab /etc/fstab.bak.$(date +%s)
echo "UUID=$UUID /data/minio xfs defaults,noatime,nodiratime 0 0" >> /etc/fstab
mount -a
mountpoint /data/minio && df -h /data/minio
六、系统调优(三台都做)
6.1 关闭 swap
MinIO 对延迟敏感,官方建议禁用 swap:
swapoff -a
sed -i.bak -E '/\sswap\s/s/^/#/' /etc/fstab
6.2 内核参数
cat > /etc/sysctl.d/99-minio.conf <<'EOF'
vm.swappiness = 1
vm.dirty_ratio = 10
vm.dirty_background_ratio = 5
fs.file-max = 2097152
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 1024 65000
EOF
# 逐项应用, 单键失败只告警不中断
while IFS='=' read -r k v; do
[[ -z "$k" || "$k" == \#* ]] && continue
sysctl -w "$(echo $k)=$(echo $v)" >/dev/null 2>&1 \
&& echo "ok $k" || echo "warn $k 不支持(可忽略)"
done < /etc/sysctl.d/99-minio.conf
坑:不要加
net.ipv4.conf.all.accept_source_route和net.ipv4.conf.all.promote_secondaries。
这两个键在 5.15 内核上返回Invalid argument,与 MinIO 运行无关,加了只会刷报错。
6.3 文件句柄
cat > /etc/security/limits.d/99-minio.conf <<'EOF'
* soft nofile 1048576
* hard nofile 1048576
root soft nofile 1048576
root hard nofile 1048576
EOF
6.4 系统用户与目录
id minio &>/dev/null || useradd -r -s /sbin/nologin -M minio
mkdir -p /etc/minio/certs /var/log/minio
chown -R minio:minio /data/minio /etc/minio /var/log/minio
chmod 750 /etc/minio/certs
七、凭据:三台必须完全一致
umask 077
cat > /etc/minio/minio.env <<'EOF'
MINIO_ROOT_USER=admin
MINIO_ROOT_PASSWORD=YourStrongPassw0rd
EOF
chmod 600 /etc/minio/minio.env
最大的坑之一:不要让各节点各自随机生成密码。凭据不一致时集群可能起得来,但所有访问 403,而且报错完全不指向根因,极难排查。
确认三台一致(指纹相同即可):
source /etc/minio/minio.env
printf '%s:%s' "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD" | sha256sum | cut -c1-12
密码含 $ 时注意:Pass$word 这类会被 shell 展开。读取一律用变量:
source /etc/minio/minio.env
mc alias set m1 http://192.168.0.150:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"
不要写 export $(grep ... /etc/minio/minio.env | xargs),也不要直接拼到命令行。
八、systemd 服务:三台写完全相同的文件
cat > /etc/systemd/system/minio.service <<'EOF'
[Unit]
Description=MinIO Object Storage (distributed)
Documentation=https://min.io/docs/minio/linux/index.html
Wants=network-online.target
After=network-online.target
AssertFileIsExecutable=/usr/local/bin/minio
[Service]
Type=notify
User=minio
Group=minio
EnvironmentFile=/etc/minio/minio.env
Environment="MINIO_VOLUMES=http://192.168.0.150:9000/data/minio http://192.168.0.151:9000/data/minio http://192.168.0.152:9000/data/minio"
Environment="MINIO_OPTS=--address :9000 --console-address :9001"
Environment="MINIO_BROWSER=on"
ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMES
Restart=always
RestartSec=5
LimitNOFILE=1048576
LimitNPROC=1048576
TasksMax=infinity
TimeoutSec=0
SendSIGKILL=no
StandardOutput=append:/var/log/minio/minio.log
StandardError=append:/var/log/minio/minio.log
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable minio
三条铁律(本次最大的坑)
① MINIO_VOLUMES 三台顺序必须一模一样
顺序不同,MinIO 会认为是不同的集群拓扑。
② 绝对不要设置 MINIO_SERVER_URL / MINIO_BROWSER_REDIRECT_URL
MinIO 启动时会比对三台所有 MINIO_* 变量,要求值完全相同。如果按各自 IP 填写(比如 https://192.168.0.151:9000),必然不同,直接拒绝组集群:
Error: http://192.168.0.152:9000->http://192.168.0.151:9000 has incorrect configuration:
Expected MINIO_* environment name and values across all servers to be same:
Mismatching environment values: [MINIO_SERVER_URL MINIO_BROWSER_REDIRECT_URL]
INFO: Waiting for at least 1 remote servers with valid configuration to be online
这个错误会每 2 秒刷一次,systemd 的 Type=notify 让它看起来像卡死,实际上它在无限等待。
只有前面有负载均衡 / VIP / 统一域名时才设,且三台填同一个值。
③ 不要设 MINIO_PROMETHEUS_AUTH_TYPE
非必要变量越少越好,减少不一致的可能。
九、启动:三台几乎同时
分布式启动是互相等待的,必须三台一起启动:
# 三台各开一个终端, 几乎同时执行
systemctl start minio
或从一台并行启动(需 SSH 免密):
for h in 192.168.0.150 192.168.0.151 192.168.0.152; do
ssh root@$h 'systemctl start minio' &
done; wait
单台日志出现
Waiting for at least 1 remote servers...是正常等待,不是卡死。三台都起来后几秒内自动组集群。
十、验证
10.1 健康检查
for h in 192.168.0.150 192.168.0.151 192.168.0.152; do
printf "%-16s " "$h"
curl -s -o /dev/null -w '%{http_code}\n' "http://$h:9000/minio/health/cluster"
done
# 三行都应为 200
10.2 集群信息
source /etc/minio/minio.env
mc alias set m1 http://192.168.0.150:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"
mc admin info m1
期望输出:
● 192.168.0.150:9000
Uptime: ...
Version: 2025-07-23T15:54:02Z
Network: 3/3 OK ← 节点互连全通
Drives: 1/1 OK ← 每台 1 块盘在线
Pool: 1
● 192.168.0.151:9000 ...
● 192.168.0.152:9000 ...
3 nodes, 3 drives, 1 pool
三个关键点:三台 Version 相同、Network: 3/3 OK、Drives: 1/1 OK。
小坑:
mc admin info m1报No valid configuration found for 'm1' host alias,是因为没执行mc alias set。alias 存在~/.mc/config.json,换用户或换机器要重设。不想写配置文件可以用环境变量:
export MC_HOST_m1="http://${MINIO_ROOT_USER}:${MINIO_ROOT_PASSWORD}@192.168.0.150:9000"(注意别写成//IP/:9000这种畸形格式)。
10.3 读写测试
mc mb m1/testbucket
echo "hello minio $(date)" > /tmp/a.txt
mc cp /tmp/a.txt m1/testbucket/
mc cat m1/testbucket/a.txt
十一、容错演练(必做)
这是验证纠删码真的生效的唯一方法,不是看进程有没有起来。
# 1. 灌入测试数据
mkdir -p /tmp/bulk && for i in $(seq 1 50); do head -c 1M /dev/urandom > /tmp/bulk/f$i.bin; done
mc mb -p m1/testbucket
mc cp --recursive /tmp/bulk/ m1/testbucket/bulk/
# 2. 停掉一台模拟故障
ssh root@192.168.0.151 'systemctl stop minio'
# 3. 立刻读 —— 必须成功
mc cat m1/testbucket/bulk/f25.bin > /dev/null && echo "读取正常 ✓"
# 4. 立刻写 —— 也必须成功
echo test > /tmp/new.txt && mc cp /tmp/new.txt m1/testbucket/ && echo "写入正常 ✓"
# 5. 状态应为 1 drive offline
mc admin info m1 | grep -E 'offline|Drives'
# 6. 恢复并自愈
ssh root@192.168.0.151 'systemctl start minio'
sleep 20
mc admin heal m1 --recursive
mc admin info m1 | grep offline # 应回到 0 offline
若第 3 步读取失败,说明 EC 未生效,检查三台 MINIO_VOLUMES 是否完全一致。
十二、日常运维
systemctl status minio
journalctl -u minio -f # 实时日志
tail -f /var/log/minio/minio.log
mc admin info m1 # 集群概览
mc admin heal m1 --recursive # 手动触发自愈
mc admin trace m1 # 实时请求追踪
mc support perf drive m1 # 磁盘性能基线
滚动升级
三台版本必须一致,逐台替换,每台确认健康后再下一台:
BASE=https://dl.minio.org.cn
NEW_VER=RELEASE.2025-XX-XXTXX-XX-XXZ
cp /usr/local/bin/minio /usr/local/bin/minio.bak
systemctl stop minio
curl -fL -o /usr/local/bin/minio.new "$BASE/server/minio/release/linux-amd64/archive/minio.$NEW_VER"
chmod +x /usr/local/bin/minio.new && mv /usr/local/bin/minio.new /usr/local/bin/minio
systemctl start minio
curl -fsS http://<本机IP>:9000/minio/health/cluster && echo OK
扩容
老集群保持在线,在每台 MINIO_VOLUMES 末尾追加新地址(只能追加,不能插中间),然后所有节点一起重启:
http://192.168.0.150:9000/data/minio
http://192.168.0.151:9000/data/minio
http://192.168.0.152:9000/data/minio
http://192.168.0.153:9000/data/minio ← 新增
扩完执行 mc admin heal m1 --recursive 让旧数据重分布。
备份
- 配置:
/etc/minio/minio.env、/etc/systemd/system/minio.service - 数据:纠删码不是备份。重要数据另存一份:
mc mirror m1/prod /backup/prod - 不要
rsync /data/minio:分布式布局下单拷某个节点没有意义
十三、TLS
# 文件名固定, 不能改
cp your.crt /etc/minio/certs/public.crt # 服务器证书(含中间链)
cp your.key /etc/minio/certs/private.key
chown -R minio:minio /etc/minio/certs
chmod 600 /etc/minio/certs/private.key
systemctl restart minio # 三台都放
证书 SAN 必须包含三台 IP 或覆盖它们的域名,否则 SDK 校验失败。
自签(仅测试):
cd /etc/minio/certs
openssl req -x509 -nodes -days 825 -newkey rsa:2048 \
-keyout private.key -out public.crt -subj "/CN=minio.local" \
-addext "subjectAltName=IP:192.168.0.150,IP:192.168.0.151,IP:192.168.0.152"
chown minio:minio private.key public.crt && chmod 600 private.key
systemctl restart minio
启用后地址变为 https://IP:9000 / https://IP:9001,不需要额外设 MINIO_SERVER_URL。
十四、已知坑速查表
| 现象 | 原因 | 处理 |
|---|---|---|
curl: (22) ... 410 下载失败 |
dl.min.io 已下线社区版 |
用 dl.minio.org.cn |
| 下载的文件只有几百字节 | 下到了错误页 | 校验 ELF 头 + 文件大小 |
sysctl: ... Invalid argument |
用了 5.15 不支持的网络键 | 去掉那两键 |
启动卡住,刷 Waiting for at least 1 remote servers |
其它节点未启动 | 三台一起启动 |
Mismatching environment values: [MINIO_SERVER_URL ...] |
按节点设了 URL 变量 | 删掉这两行 |
| 集群起来了但访问 403 | 三台 MINIO_ROOT_PASSWORD 不同 |
统一 env 后三台重启 |
No valid configuration found for 'm1' host alias |
没执行 mc alias set |
先建 alias |
RequestTimeTooSkewed |
节点时间不同步 | timedatectl set-ntp true |
写入报 XMinioStorageFull |
可用空间 < 5%(硬性保留) | 清理或扩容 |
mkfs.xfs: command not found |
未装 xfsprogs | apt install -y xfsprogs |
重启后 /data/minio 空了 |
fstab 用了 /dev/sdb |
改用 UUID= |
| Console 打不开 | 9001 被防火墙挡 | 检查 ufw / iptables |
十五、部署完成确认清单
[ ] 三台 minio --version 完全一致
[ ] 三台 /etc/minio/minio.env 内容一致(凭据指纹相同)
[ ] 三台 minio.service 中 MINIO_VOLUMES 顺序一致
[ ] service 中无 MINIO_SERVER_URL / BROWSER_REDIRECT_URL / PROMETHEUS
[ ] mountpoint /data/minio 通过,df 显示容量正常
[ ] 三台 health/cluster 均返回 200
[ ] mc admin info 显示 3 nodes, 3 drives, 1 pool
[ ] 三台 Network: 3/3 OK、Drives 1/1 OK
[ ] 读写测试通过
[ ] 容错演练通过:停 1 台仍可读写,恢复后 heal 完成
[ ] 二进制已归档到 /opt/minio-archive/
小结
三个最关键的教训,按重要性排序:
- 所有
MINIO_*环境变量必须三台完全一致,任何按节点变化的变量(尤其是 URL 类)都不能进 unit - 凭据必须三台一致,不能各节点随机生成,否则 403 且报错不指向根因
- 容量要按盘数算 EC,别凭感觉估,用
mc admin info的total ÷ 裸容量反推验证
其余都是细节。这三条对了,剩下基本不会出问题。

浙公网安备 33010602011771号