故障排查与最佳实践
排障总纲
对象存储的故障看起来五花八门,其实可以用三个问题快速缩小范围:
- 影响面:一个桶、一个前缀,还是全集群?单桶出问题多半是权限或生命周期规则;全集群出问题多半是仲裁、网络或磁盘。
- 读写哪边:只能读不能写,通常是写仲裁不足或磁盘写满;读写都不行,通常是节点或池整体不可用。
- 所有客户端还是个别客户端:个别客户端报错,先怀疑它的密钥、时钟、SDK 版本;全都报错才看服务端。
先跑这三条命令,八成的问题当场就能定位:
# 集群与磁盘状态,第一眼就看它
mc admin info cluster
# 服务端日志,找 error / warning 关键字
sudo journalctl -u minio --since "30 min ago" | grep -iE 'error|warn|offline'
# 看具体请求为什么被拒(403/500 必备)
mc admin trace -v --status-code 403 cluster
MinIO 的很多告警是瞬时的(网络抖动、磁盘控制器超时),重启只会掩盖现场。先抓日志和 mc admin info 的输出,再决定动作。重启前把 mc admin info --json 存一份,事后对比能看出丢了多少盘。
磁盘故障
怎么发现
三个信号,按可靠程度排序:
- 监控告警:
minio_cluster_drive_offline_total大于 0,这是最快的。 mc admin info:Drives 那一行变成15 drives online, 1 drives offline,还会标出是哪台节点的哪块盘。- 服务端日志:
disk offline、drive ... is not available、I/O error。
磁盘故障后的处理流程
以「一块盘彻底坏了」为例,标准动作是:
# 1. 确认坏的是哪块盘(节点 + 挂载点)
mc admin info cluster
# 2. 在对应节点上看内核日志,确认是硬件问题
sudo dmesg -T | grep -iE 'error|reset|failed' | tail -20
# 3. 换盘后格式化并挂载(挂载点必须和原来完全一致)
sudo mkfs.xfs -f /dev/vdb
sudo mount /dev/vdb /mnt/disk1
sudo chown -R minio-user:minio-user /mnt/disk1
# 4. 重启该节点的服务,让它重新识别这块盘
sudo systemctl restart minio
# 5. 触发修复(先用 dry-run 看范围)
mc admin heal --dry-run --recursive cluster
mc admin heal --recursive cluster
影响范围有多大
这是运维最关心的问题,答案取决于纠删码集合的布局:
- 只影响该盘所在集合的对象,其他集合完全不受影响。16 盘一个集合时,每个对象都少了 1 个分片,但没有对象会因此丢失,业务侧通常无感。
- 读写继续可用,前提是丢盘数没有超过校验值。
EC:4下坏 4 块盘还能读写,坏第 5 块就开始有对象读不出来。 - 修复期间性能下降:heal 会读其他分片、重建、回写,磁盘 I/O 和网络流量都会明显上升。大容量盘的修复可能持续数小时到数天。
- 修复期间冗余下降:只有 1 块盘坏、正在重建时,再坏 1 块还能扛;但如果已经坏了 3 块,第 4 块坏掉就只剩最后一点余量了。坏盘要尽快换,不要攒着。
有人看到某块盘空了,想用 cp 把别的盘上的文件拷过去「补上」——这是最危险的操作。MinIO 的分片带校验和元数据,手工拷贝会破坏一致性,导致整块盘被判定为损坏。换盘后让 MinIO 自己 heal,不要碰盘上的文件。
节点掉线
排查顺序
节点掉线时按这个顺序查,从便宜到贵:
# 1. 网络通不通(ICMP 可能被禁,直接测端口更准)
ping -c 3 minio3.example.net
nc -zv minio3.example.net 9000
# 2. 服务在不在
ssh minio3.example.net 'systemctl status minio --no-pager'
# 3. 时间同步是否正常(集群对时钟很敏感)
ssh minio3.example.net 'chronyc tracking'
# 4. 数据盘挂载是否还在
ssh minio3.example.net 'df -h /mnt/disk*'
# 5. 证书是否过期(TLS 集群常见)
openssl s_client -connect minio3.example.net:9000 -servername minio3.example.net 2>/dev/null | openssl x509 -noout -dates
常见根因按出现频率排:磁盘挂载丢失(重启后 fstab 没生效)、证书过期、时间偏差过大、服务 OOM 被杀、网络分区。
掉一个节点还能不能扛
以 4 节点 × 4 盘、EC:4 为例:
| 故障 | 每集合丢盘 | 读 | 写 | 说明 |
|---|---|---|---|---|
| 掉 1 块盘 | 1 | 正常 | 正常 | 还有 3 块余量 |
| 掉 4 块盘(不同节点) | 4 | 正常 | 正常 | 余量耗尽,再来一块就危险 |
| 掉 1 个节点(4 块盘) | 4 | 正常 | 正常 | 正好卡在边界上 |
| 掉 2 个节点(8 块盘) | 8 | 部分失败 | 拒绝 | 超过校验值,数据丢失 |
「掉一个节点正好卡在边界」这件事要特别记住:EC:4 的 4 节点集群,同时挂两个节点就是数据丢失。所以节点故障要当 P0 处理,不能因为「还能读写」就慢慢修。
集群不可写
写请求全部失败是最紧急的故障。按下面这张表逐项排除:
| 现象 | 可能原因 | 确认方式 | 处置 |
|---|---|---|---|
| 503 Service Unavailable | 写仲裁不足,丢盘数达到校验值 | mc admin info 看 offline 盘数 | 优先恢复节点/磁盘 |
| 400 / 签名错误 | 客户端与服务端时间偏差超过 15 分钟 | date 对比、chronyc tracking | 修 NTP |
| Access Denied | 密钥错误或策略变更 | mc admin trace --status-code 403 | 核对策略 |
| 写入后立刻失败,日志报磁盘满 | 磁盘使用率接近 99% | df -h、监控水位 | 清理或扩容 |
| 所有请求超时 | 某节点网络分区、负载均衡打到坏节点 | mc admin info、LB 健康检查 | 摘除坏节点 |
| TLS 握手失败 | 证书过期或 CA 未分发 | openssl s_client | 换证书并全节点重启 |
如果集群有多个服务器池,任意一个池完全不可用,整个部署的 I/O 都会停止——MinIO 无法判断请求原本该路由到哪个池,继续服务可能造成不一致。这不是 bug,是设计上的安全选择。所以多池部署要保证每个池的可用性都在监控范围内,别只盯着总容量。
脑裂与分区
「脑裂」在 MinIO 里的表现和传统分布式系统不太一样,理解仲裁规则就不会慌:
- 校验值小于集合盘数的一半(比如 16 盘
EC:4):读写仲裁都是 12。网络分区导致一半盘不可见时,剩下 8 块达不到 12,读写都停,反而不会脑裂。 - 校验值正好等于集合盘数的一半(比如 16 盘
EC:8):写仲裁被提高到K + 1 = 9。这样即使集群被分成两半(各 8 块盘),两边都写不了,避免同一对象被写到两个「半边」上产生两个不一致的版本。读仲裁仍是 8,所以读可能还能成功。
处置原则只有一条:恢复网络,不要试图手工修复数据。
- 分区恢复后,MinIO 会自动重新达成仲裁并继续服务。
- 如果期间有节点被强制重启过,检查
mc admin info和日志里是否有heal相关告警,必要时手动mc admin heal。 - 绝对不要为了「让集群先跑起来」去删盘上的文件、改
xl.meta、或者用mc rm清理「看起来坏了」的对象——这些动作会把可恢复的降级状态变成永久数据丢失。
权限问题
Access Denied 是最常见的「不是故障的故障」。按这个顺序查:
# 1. 这个用户到底绑了什么策略
mc admin user info cluster reader
mc admin policy info cluster readwrite
# 2. 桶的匿名策略是不是被改过
mc anonymous list cluster
# 3. 用 trace 看请求落在哪个桶、哪个动作上被拒
mc admin trace -v --status-code 403 cluster
常见原因归类:
- 密钥不对:Access Key / Secret Key 打错、用了别的环境的凭据、密钥被轮换后客户端没更新。
- 策略不匹配:只授了
s3:GetObject却要上传;策略里的资源写成arn:aws:s3:::bucket而不是arn:aws:s3:::bucket/*,导致对象级操作被拒。 - 前缀限制:策略限制了
photos/public/*,客户端往photos/private/写当然被拒。 - 时间偏差:签名带时间戳,服务端时间差超过 15 分钟直接拒签,错误信息常常也是 Access Denied,很有迷惑性。
- 区域不匹配:客户端配了
us-east-1,桶实际在别的 region,部分 SDK 会报签名错误。 - TLS 中间人:自签证书没被信任,客户端直接拒连,报错看着像认证问题。
给每个业务建独立用户、独立策略,资源精确到前缀,动作精确到需要的几个。排障时先看策略 JSON,再看桶策略,最后才怀疑服务端——90% 的 Access Denied 在策略层面就能找到答案。
扩容后的数据重平衡
扩容之后最常见的「故障」其实是误解:新池是空的,老池还是满的,容量怎么没降下来?
原因在写入路由:新对象按各池空闲空间的比例分配,已经写下的对象不会自动搬家。所以扩容后短时间内,老池的使用率不会下降。
# 看各池的容量分布
mc admin info cluster
# 手动触发重平衡,把老池的数据匀到新池
mc admin rebalance start cluster
# 看进度
mc admin rebalance status cluster
# 需要时暂停/停止
mc admin rebalance stop cluster
重平衡会大量读写磁盘,务必挑业务低峰期,并且提前确认网络带宽够用。如果只是想「让新数据写到新池」,不跑重平衡也没问题,新写入自然会往空闲的池倾斜。
重平衡的收益是让容量分布均匀、性能可预测,代价是一段时间的 I/O 压力。如果老池还有余量、业务对延迟敏感,可以不做,等自然增长慢慢摊平。不要在不了解影响面的情况下随手 mc admin rebalance start。
最佳实践清单
架构与部署
- 生产至少 4 节点 × 4 盘,
EC:4;容量按两年规划,70% 水位前扩容。 - 磁盘直通、XFS、挂载点连续编号、写进
fstab并重启验证。 - 全节点同一端口、同一配置、时间同步;变更时全节点同时重启,禁止滚动。
- 前面挂负载均衡(least_conn),TLS 终结在 LB 或 MinIO 自身,二选一不要混。
安全
- 换掉默认凭据,root 账号只用于管理,业务用独立用户 + 最小权限策略。
- 9001 控制台不暴露公网;指标端点不开公网;证书提前 30 天续期。
- 预签名 URL 当临时凭证对待,不发长期链接、不贴公开渠道。
数据治理
- 版本控制只开给需要的桶,同时配非当前版本过期规则。
- 用生命周期规则代替客户端删除脚本,临时目录必须有前缀级过期规则。
- 给业务桶设配额,防止单桶写爆拖垮集群。
可靠性
- 坏盘立刻换,别攒;监控离线磁盘数是第一优先级告警。
- 纠删码不等于备份,必须有异地
mc mirror或桶复制,且每月演练恢复。 - 扩容前导出配置:
mc admin cluster bucket export和mc admin cluster iam export。
变更
- 升级前读 release notes,在测试环境验证 SDK 兼容性;
mc版本不要落后服务端太多。 - 任何删除操作前先
mc ls --recursive确认范围,重要数据先mc mirror一份。 - 折腾集群前先把
mc admin info --json和日志存下来,出问题才有对比基线。
小结
- 排障先定三件事:影响面、读写方向、是否所有客户端;开局三命令是
mc admin info、journalctl、mc admin trace。 - 磁盘故障的处理是「换盘 → 原挂载点格式化挂载 → 重启 → heal」,影响范围限于该盘所在集合,修复期间性能下降且冗余临时降低。
EC:4的 4 节点集群掉两个节点就丢数据,节点故障按 P0 处理。- 集群不可写优先查仲裁、时间、证书、磁盘水位;多池部署下一个池全挂会停掉整个集群的 I/O。
- 脑裂靠「校验值等于一半时写仲裁 +1」防住,处置原则是恢复网络,不碰数据。
Access Denied九成在策略和时间偏差上,用mc admin trace --status-code 403直接看被拒的动作。- 扩容后老数据不会自动迁移,需要时用
mc admin rebalance,挑低峰期做。 - 最佳实践的核心就三句:坏盘立刻换、冗余之外还要有备份、变更全节点同时做。
系列正篇到此结束,附录里的命令速查和参考资料可以当案头手册随时翻。