
我第一次下定决心把对象存储“好好地”做成高可用,是在一个台风夜。凌晨三点,机房外面风像刀子一样刮,CDN 回源突然飙到 5xx,我们的视频平台首页一片灰。值班群里有人说:“内容库挂了。”我拔掉雨衣上的水,冲回机柜前,看着那台单点 NAS 的告警灯一闪一闪,心里只有一句:这事儿必须重做。
后来的两周,我把香港节点的存储从零到一重构为 MinIO 分布式集群,接上 HAProxy + Keepalived,后端磁盘做了纠删码,前端走 VIP,旁路监控报警、异地复制、滚动升级。再遇到大促回源时,面板没再红过。
这篇稿,就是我按“实战复盘”的方式,把完整的构建过程、参数、坑点和优化细节写给你。不管你是第一次上手,还是准备把 MinIO 玩到进阶,这一篇都够你从设计到上线。
业务目标与设计原则
- 业务形态:点播为主(HLS/DASH 切片与封面),少量上传与转码回写。
- SLO:对象存储对外 99.95%(≥ 月不可用 ≤ 22 分钟),内部写入 99.9%。
- RPO/RTO:本地集群 RPO≈0(多副本/纠删码),跨城异步复制 RPO≤15 分钟;集群级故障 RTO≤30 分钟。
- 关键诉求:无单点、滚动升级、支持横向扩容、对 CDN 友好。
拓扑与硬件选型(香港双机房)
我们把 1 个集群跨两个机房(同城双可用区),低时延专线互联,前面挂两台负载均衡器抢 VIP。
香港服务器与网络(建议参数)
| 角色 | 数量 | CPU | 内存 | 系统盘 | 数据盘 | 网卡 | 机房 | 备注 |
|---|---|---|---|---|---|---|---|---|
| MinIO 节点 (S1–S6) | 6 | AMD EPYC 7302P / Intel Xeon Silver 同级 | ≥128GB | NVMe 480GB | 8× 16TB SATA HDD + 2× 1.92TB NVMe SSD(元数据/缓存) | 25GbE ×2(Bond) | A(3)+B(3) | XFS,纠删码池 |
| LB 节点 (LB1–LB2) | 2 | 8C | 32GB | SATA | — | 10/25GbE ×2 | A(1)+B(1) | HAProxy + Keepalived |
| 监控/日志 | 2 | 8C | 32GB | — | — | 10GbE | A(1)+B(1) | Prometheus/Alertmanager/Loki |
| KMS(可选) | 2 | 4C | 16GB | — | — | 10GbE | A(1)+B(1) | KES + Vault/云 KMS |
OS 说明:以下示例以 CentOS 7 为主(很多老环境仍在用)。CentOS 7 已 EOL,如可选择建议迁移 Rocky/Alma/EL9;但本文全部命令在 CentOS 7 上可直接落地。
IP 与端口规划
| 名称 | IP | 端口 | 用途 |
|---|---|---|---|
| VIP(对外) | 203.0.113.10 | 443 | S3 API / Console(经 HAProxy) |
| LB1/LB2 | 10.10.0.10/11 | 443→9000/9001 | 反向代理到各 MinIO 节点 |
| MinIO S1–S6 | 10.10.1.21–26 | 9000(API)/9001(Console) | 集群节点 |
| 监控 | 10.10.2.10/11 | 9090/9093 | Prometheus/Alertmanager |
架构设计要点
MinIO 分布式纠删码:6 节点 × 每节点 8 HDD(可按 48~64 盘组成 1 个或多个 erasure set)。
- 推荐单池容量 ≥ 48 盘,MinIO 会按盘数自动选择 纠删码的数据/奇偶校验比(小于等于 16 盘通常是 4 校验,再大可能是 6 校验)。
- 写入/读取法定人数:单次操作涉及一个 erasure set,需超过半数“条带”可用即可完成(这就是它能抗盘/节点故障的原因)。
前端高可用:HAProxy + Keepalived 提供 VIP;LB 双机,VRRP 主备,后端到所有 MinIO 节点健康检查。
磁盘层:XFS 文件系统、noatime、inode64;HDD 设 deadline,NVMe 设 none/mq-deadline。
TLS 与加密:前端 TLS 终止在 HAProxy;对象层可启用 SSE-S3;如有合规要求,接 KES + Vault/KMS。
监控与自愈:Prometheus 拉指标,healing backlog、drive offline、http 5xx 设置告警。
异地容灾:第二地(或同城另一集群)用 Bucket 复制 异步同步关键桶,RPO 按业务决定(我设 15 分钟)。
部署步骤(端到端)
基础环境与内核优化(CentOS 7)
# 基础包
yum install -y epel-release vim htop jq wget curl chrony lsof xfsprogs parted hdparm \
iproute bridge-utils net-tools conntrack-tools keepalived haproxy
# 时间同步
systemctl enable --now chronyd
# 关闭 SELinux(如需开启请自行加策略)
setenforce 0
sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
# 防火墙(按需放行或关闭)
systemctl stop firewalld && systemctl disable firewalld
# ulimit
cat >/etc/security/limits.d/99-nofile.conf <<'EOF'
* soft nofile 1048576
* hard nofile 1048576
* soft nproc 65536
* hard nproc 65536
EOF
# sysctl(网络与文件句柄)
cat >/etc/sysctl.d/99-minio.conf <<'EOF'
fs.file-max=2097152
vm.swappiness=1
net.core.somaxconn=65535
net.core.netdev_max_backlog=250000
net.ipv4.tcp_max_syn_backlog=262144
net.ipv4.tcp_timestamps=0
net.ipv4.tcp_fin_timeout=15
net.ipv4.tcp_tw_reuse=1
net.ipv4.ip_local_port_range=1024 65000
EOF
sysctl --system
1. 磁盘分区与 XFS 挂载
目标:每节点 8 块 HDD 作为数据盘、2 块 NVMe 作为缓存/索引(可选),全部 独立挂载 到 /data{1..8}。
# 以 /dev/sdb 为例,单分区整盘
for d in b c d e f g h i; do
parted -s /dev/s$d mklabel gpt
parted -s /dev/s$d mkpart primary xfs 1MiB 100%
mkfs.xfs -f -m reflink=0,crc=1 -i size=512 /dev/s${d}1
done
# 获取 UUID 并写入 fstab
blkid | egrep '/dev/s[bcdefghi]1' | while read -r line; do
dev=$(echo "$line" | awk -F: '{print $1}')
uuid=$(echo "$line" | sed -n 's/.*UUID="\([^"]*\)".*/\1/p')
idx=$(echo "$dev" | sed -n 's/.*s\([bcdefghi]\)1/\1/p')
num=$(printf "%d" "'$idx") # a->97,b->98...
mountp="/data$((num-96))"
mkdir -p "$mountp"
echo "UUID=$uuid $mountp xfs defaults,noatime,inode64,logbufs=8,attr2 0 0" >> /etc/fstab
done
mount -a
磁盘调度器(可选)
# HDD 用 deadline,NVMe 通常用 none/mq-deadline(视内核/驱动)
cat >/etc/udev/rules.d/60-io-scheduler.rules <<'EOF'
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/scheduler}="deadline"
ACTION=="add|change", KERNEL=="nvme[0-9]n[0-9]", ATTR{queue/scheduler}="none"
EOF
udevadm control --reload && udevadm trigger
2. 安装 MinIO 二进制与用户
useradd -r -s /sbin/nologin minio
cd /usr/local/bin
curl -O https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
mkdir -p /etc/minio /var/minio
chown -R minio:minio /etc/minio /var/minio /data*
环境变量与集群端点(每台节点)
假设节点主机名 s1~s6(解析到各自内网 IP)。
cat >/etc/minio/minio.conf <<'EOF'
MINIO_ROOT_USER=admin
MINIO_ROOT_PASSWORD='ChangeMe_Strong_P@ss'
MINIO_SERVER_URL=https://storage.example.com # 供 Console/回调生成外链用(走 VIP/域名)
MINIO_BROWSER_REDIRECT_URL=https://storage.example.com
MINIO_OPTS="server --console-address ':9001' \
http://s1/data{1...8} \
http://s2/data{1...8} \
http://s3/data{1...8} \
http://s4/data{1...8} \
http://s5/data{1...8} \
http://s6/data{1...8}"
EOF
chmod 600 /etc/minio/minio.conf
chown minio:minio /etc/minio/minio.conf
systemd 单元(每台节点)
cat >/etc/systemd/system/minio.service <<'EOF'
[Unit]
Description=MinIO Object Storage
After=network-online.target
Wants=network-online.target
[Service]
User=minio
Group=minio
EnvironmentFile=/etc/minio/minio.conf
WorkingDirectory=/var/minio
ExecStart=/usr/local/bin/minio $MINIO_OPTS
LimitNOFILE=1048576
LimitNPROC=65536
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now minio
健康检查:所有节点启动后,journalctl -u minio -f 观察 erasure set 成功组成、无 drive not found 错误。随后执行:
# 管理端:下载 mc
curl -O https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc && mv mc /usr/local/bin/
# 添加别名(走 VIP,经 HAProxy)
mc alias set prod https://storage.example.com admin 'ChangeMe_Strong_P@ss' --api S3v4
mc admin info prod
3. 前端高可用:HAProxy + Keepalived
HAProxy 配置(LB1/LB2 同步)
cat >/etc/haproxy/haproxy.cfg <<'EOF'
global
log /dev/log local0
maxconn 200000
tune.ssl.default-dh-param 2048
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5s
timeout client 120s
timeout server 120s
frontend fe_https
bind *:443 ssl crt /etc/haproxy/certs/storage.pem alpn h2,http/1.1
http-response set-header Strict-Transport-Security "max-age=31536000"
default_backend be_minio
backend be_minio
option httpchk GET /minio/health/ready
http-check expect status 200
balance uri whole
server s1 10.10.1.21:9000 check inter 2s fall 3 rise 2
server s2 10.10.1.22:9000 check inter 2s fall 3 rise 2
server s3 10.10.1.23:9000 check inter 2s fall 3 rise 2
server s4 10.10.1.24:9000 check inter 2s fall 3 rise 2
server s5 10.10.1.25:9000 check inter 2s fall 3 rise 2
server s6 10.10.1.26:9000 check inter 2s fall 3 rise 2
EOF
mkdir -p /etc/haproxy/certs
# 放置 storage.pem (证书链+私钥)
systemctl enable --now haproxy
Keepalived(VRRP VIP:203.0.113.10)
cat >/etc/keepalived/keepalived.conf <<'EOF'
vrrp_instance VI_1 {
state MASTER # 在 LB2 改为 BACKUP
interface bond0 # 你的对外网口
virtual_router_id 51
priority 120 # LB2 用 110
advert_int 1
nopreempt # 避免抢占抖动
authentication {
auth_type PASS
auth_pass 7xHk!Minio
}
virtual_ipaddress {
203.0.113.10/24 dev bond0
}
track_script {
chk_haproxy
}
}
vrrp_script chk_haproxy {
script "/bin/pidof haproxy"
interval 2
weight 20
}
EOF
systemctl enable --now keepalived
证书:我用 ACME 客户端(如 acme.sh)签发放到 /etc/haproxy/certs/storage.pem。证书更新后 systemctl reload haproxy 即可。
4. 初始化桶、策略、生命周期
# 别名已配置为 prod
mc admin user add prod cdnreader 'Another_Strong_P@ss'
mc admin policy attach prod readwrite --user admin
mc admin policy attach prod readonly --user cdnreader
# 创建业务桶
mc mb prod/videos
mc mb prod/thumbnails
# 桶策略:CDN 回源只读
mc anonymous set download prod/videos
mc anonymous set download prod/thumbnails
# 生命周期:切片 30 天后转低频,180 天后过期(按需调整)
cat > lifecycle.json <<'EOF'
{
"Rules": [
{
"ID": "video-archive",
"Status": "Enabled",
"Filter": { "Prefix": "" },
"Transitions": [{ "Days": 30, "StorageClass": "STANDARD_IA" }],
"Expiration": { "Days": 180 }
}
]
}
EOF
mc ilm import prod/videos < lifecycle.json
5. 监控与告警(Prometheus)
MinIO 自带指标:/minio/v2/metrics/cluster。
Prometheus scrape_configs 例子:
scrape_configs:
- job_name: 'minio'
metrics_path: /minio/v2/metrics/cluster
scheme: http
static_configs:
- targets:
- 10.10.1.21:9000
- 10.10.1.22:9000
- 10.10.1.23:9000
- 10.10.1.24:9000
- 10.10.1.25:9000
- 10.10.1.26:9000
关键告警建议
- minio_cluster_heal_objects_total 持续增长且 minio_cluster_heal_writes_total 低 → 可能 healing 堵塞
- minio_cluster_usage 接近阈值(>80%)
- 节点 readiness 掉线
- HAProxy 后端存活数 < 80%
6.(可选)对象加密:KES + Vault/KMS
合规环境可启用 SSE-S3 或 SSE-KMS。这里给 SSE-S3 + KES 的最小可用示例要点:
- 部署 kes,配置到 Vault 或云 KMS;
- MinIO 端 MINIO_KMS_KES_ENDPOINT、MINIO_KMS_KES_KEY_FILE、MINIO_KMS_KES_CERT_FILE;
- 桶级别启用加密。
应用接入与 SDK 参数建议(视频平台实战)
- 回源优先走 CDN:应用签发 预签名 URL 给 CDN,TTL 5~60 分钟。
- Multipart 上传:分片大小 ≥ 64MB;并发 8~16;失败重试指数退避。
- Range GET:HLS/DASH 播放器天然按小文件片段取 Range,保持 LB 超时 ≥120s。
- 命名规范:videos/{vid}/hls/{bitrate}/{seg.ts};小文件集中目录配合 Nginx 缓存/CDN 减小回源压力。
- 幂等:使用 x-amz-content-sha256 与对象 ETag 校验,避免重复上传。
压测与容量规划(warp 工具)
# 安装 warp(MinIO 官方基准工具)
curl -L https://dl.min.io/warp/latest/warp-linux-amd64 -o /usr/local/bin/warp
chmod +x /usr/local/bin/warp
# 读写混合压测(注意控制并发与时长,避免影响线上)
warp mixed --host https://storage.example.com \
--access-key admin --secret-key 'ChangeMe_Strong_P@ss' \
--autoterm --concurrent 128 --duration 5m --object-size 8MiB,64MiB,256MiB
容量估算简表(示例)
| 指标 | 数值 |
|---|---|
| 原始盘容量 | 6 节点 × 8 × 16TB ≈ 768TB |
| 纠删码校验开销(约) | 6/48~4/16(按盘数自动) |
| 可用容量(估) | ~ 600TB(留出 10% 保留与元数据) |
| 目标峰值回源 | 8–12 Gbps(25GbE 双上联裕量足够) |
实际可用容量与性能取决于 erasure set 分组、盘型、并发与对象大小,以上为保守估算。
滚动升级与扩容
滚动升级:
mc admin service stop 单节点;2) 替换 /usr/local/bin/minio;3) systemctl start minio;4) healing 完成后换下一台。
加盘/加节点:
加盘:同节点新增 /data9.. 并加入 MINIO_OPTS(重启全体或按官方建议滚动)。
加节点:按 成组 增长(保持 erasure set 对齐),避免“奇数/不齐”导致条带不均。
常见坑与现场解决
- MTU 不一致:LB–节点之间 MTU 1500 / 9000 混用会触发分片与偶发超时。统一 MTU,并在 HAProxy 打开 option http-keep-alive(默认有),减少握手开销。
- 时钟漂移:TLS 与多节点签名对时敏感。chrony 对齐到同一 NTP,禁用 BIOS 自调时。
- 磁盘命名漂移:重启后 /dev/sdX 顺序变化,MinIO 找不到盘导致离线。用 UUID + fstab 固定,避免 by-id 漂移。
- healing 风暴:盘短暂抖动恢复后,对象大量修复抢占 IO。分时段维护、限制并发(新版本自动做得不错),并提前做 SMART 预警。
- ulimit 太小:出现 “too many open files”。确认 systemd LimitNOFILE=1048576,并重启 整机。
- Keepalived 抢占抖动:主节点恢复后反复切换。加 nopreempt,并对脚本健康检查设置 rise/fall。
- HAProxy 超时:大文件上传慢时 504。把 timeout client/server 提到 300s,或端到端断点续传。
- 盘混速:SATA/HDD 品牌混杂导致条带拖慢。按批次建池,或把慢盘踢出做冷数据。
- MINIO_SERVER_URL 错:生成的预签名 URL 用了内网域名,外部不可达。务必填 VIP 外网域名。
运维手册里的“红线”
- 不要把单台 NAS 当主存储(我们吃过亏)。
- 不要省略监控与告警(healing 与容量最容易被忽视)。
- 不要在业务高峰做版本大升级或全量 rebalance。
- 一定用 VIP + 健康检查,并实测 failover。
- 一定写好 容量、性能、安全三线阈值与 SOP。
附:跨集群异步复制(同城/异地)
在第二机房(或他城)建一套独立 MinIO,配置 mc 双向认证:
# 主集群(prod),灾备集群(dr)
mc alias set prod https://storage.example.com admin 'ChangeMe_Strong_P@ss'
mc alias set dr https://dr-storage.example.com admin 'ChangeMe_Strong_P@ss'
# 对桶 videos 建复制规则(prod -> dr)
mc replicate add prod/videos --remote-bucket videos --remote-drilldown --sync --priority 1 dr
mc replicate ls prod/videos
带宽不足时设置限速,或者只复制“主内容桶”,日志/临时桶不复制。
成本与 TCO 粗算(示例)
| 项 | 月成本(HKD) | 备注 |
|---|---|---|
| 机柜与电力 | 2× 1/2 柜 | 视托管商 |
| 服务器折旧 | 若干 | 3 年折旧 |
| 专线/互联 | 按带宽计费 | 两机房互联 |
| 运维 | 0.5–1 人月 | 监控+值守 |
| 软件 | 0 | 社区版 MinIO / Prometheus |
与公有云对比:回源带宽高、冷热数据比例低时,自建 TCO 优势明显;但需承担硬件与运维复杂度。
台风停了,我在机房外买了碗云吞面,通知群里发了一句:“切到新集群。”HAProxy 的日志像码表一样稳,Prometheus 上的 5xx 线贴着零,回源曲线在涨又不慌。我知道,这回不是侥幸。
如果你正准备把视频平台的内容库从“不出问题最好”变成“出问题也不怕”,就按上面一步步做。它不神秘,只是需要把每个细节都认真做过一遍。下次凌晨三点,你也能像我一样,坐在屏幕前看着图表稳定,然后去煮一杯热咖啡。
清单(可直接执行/复用)
- CentOS 7 基线(ulimit/sysctl/chrony)
- 8×HDD + XFS + UUID fstab
- MinIO 分布式 6 节点 / MINIO_OPTS 端点列表
- HAProxy + Keepalived + VIP
- 桶/策略/生命周期与只读匿名回源
- Prometheus 采集与关键告警
- warp 压测与容量验证
- 滚动升级 SOP
- 跨集群异步复制