Ceph OSD down故障排查与集群运维实战

Ceph OSD down故障排查与集群运维实战 集群半夜又报警了ceph osd tree里某个 OSD 状态直接变成了down。这种事情经历过一次就明白Ceph 这东西部署起来看似不复杂真正考验人的全在运维和排障阶段。上一篇聊了 Ceph 的基本概念和架构这篇直接进入更实际的话题部署工具怎么选、ceph osd tree down这类问题怎么排查、以及日常运维中那些文档里不会写的坑。1. 先理清 Ceph 的核心设计逻辑为什么部署和排障这么有讲究很多人第一次接触 Ceph 时被它的架构图吓住——MON、OSD、MDS、MGR一堆组件看着挺唬人。实际上 Ceph 的设计逻辑并不复杂理解它之后部署和排障就有了方向感。Ceph 的核心思路可以总结成三句话数据打散到全集群任何单点故障都不影响整体所有决策由算法而不是中心节点来做。这就解释了为什么 Ceph 敢说自己“天生分布式”——它的数据分布不是靠一个中心化的索引表来记录的而是靠 CRUSH 算法直接计算出来。只要你告诉 Ceph“这份数据应该存三份分布在不同的故障域里”它就能根据集群拓扑直接算出每一份数据该放哪台机器、哪块磁盘。这种设计的直接后果是OSD 是整个系统的命脉。OSD 承担了数据存储、数据复制、数据恢复的所有脏活累活。一个 OSD 挂掉不叫事数据还有副本在别的盘上撑着但如果 OSD 长时间不恢复或者某个故障域里的 OSD 集体阵亡那数据安全就真悬了。这也是为什么我在上一篇里反复强调用 Ceph 首先得理解 OSD 的状态流转。一个 OSD 从up到down从in到out背后对应的是整个集群的自我修复流程。你看到的ceph osd tree输出表面上只是一张表实际上是你判断集群健康度的第一手资料。我见过不少新手一上来就冲进osd tree看到down就慌也不管是什么类型的down就开始拔盘、换盘。这是最危险的。排查之前你得先搞清楚几个概念up/down指的是 OSD 进程有没有在运行。up是进程活着down是进程挂了。in/out指的是这个 OSD 是否参与数据分配。in意味着数据还在往它上面写out意味着它已经退出数据分配不接新活了。rebalancing表示集群正在重新平衡数据。当一个 OSD 被标记为out后它的数据会被迁移到其他 OSD 上这个过程中你会看到 PG 状态出现backfilling或rebalancing。理解了这层关系你在排查的时候就不会一上来就瞎折腾了。先判断是进程问题还是数据问题再判断是单点问题还是大面积问题排障的顺序对了效率自然就上去了。2. ceph-deploy上一代部署利器的使用思路与致命短板如果你去翻很多两三年前的 Ceph 教程十有八九会看到 ceph-deploy 的身影。这个工具在 Ceph 的部署历史上确实扮演过重要角色——它解决了早期手动部署 Ceph 时大量重复配置的问题让你可以用一条命令初始化 MON、创建 OSD、配置 MDS。2.1 ceph-deploy 的典型工作方式用 ceph-deploy 部署一套三节点集群标准流程大概是这样的首先在所有节点上准备用户和 SSH 免密登录。ceph-deploy 的设计思路是“通过 SSH 远程批量执行命令”所以控制节点到各个目标节点的免密登录是硬前提。我当时用的时候习惯创建一个专门的cephuser配上 sudo 权限然后把公钥分发到所有节点。接着初始化集群ceph-deploy new mon1 mon2 mon3这条命令会在当前目录生成ceph.conf和ceph.mon.keyring然后分别在三个节点上部署 MON 进程。ceph.conf是核心配置文件网络段、副本数、PG 数这些全局参数都靠它来控制。创建完 MON接下来是安装 Ceph 软件包ceph-deploy install mon1 mon2 mon3这条命令会通过 apt 或 yum 源把 ceph 相关包装上。然后是部署 MGRCeph 自 Luminous 版本后新增的组件负责集群管理接口ceph-deploy mgr create mon1最后是添加 OSD。这一步最关键也最考验你对底层磁盘的规划ceph-deploy osd create --data /dev/sdb mon1 ceph-deploy osd create --data /dev/sdc mon2每个 OSD 都对应一块独立的磁盘。我在生产环境里从来不建议把 OSD 直接放在系统盘上也坚决反对用分区代替整盘——Ceph 的 OSD 最好占有一块完整的物理磁盘bluestore 后端会直接管理整块设备分区会引入不必要的兼容性和性能问题。2.2 ceph-deploy 的致命短板在哪里ceph-deploy 虽然方便但它的短板同样明显。最大的问题是它对节点环境的假设太理想化。它默认所有节点都是干净的系统、没有历史残留的配置和依赖。一旦某台机器曾经装过其他版本的 Ceph或者网络源变了ceph-deploy 很容易卡在安装依赖这一步报错方式还特别不直观。另一个问题是它不太擅长处理“事后变更”。集群刚部署完你想加个 MON 或加个 OSDceph-deploy 还能应付。但如果集群已经运行了很长时间你再用它去增删节点经常会遇到配置文件不同步、keyring 缺失这类问题。它本质上是一个“一次性部署工具”运维的事更多还得靠 ceph 命令行手动操作。还有一点容易被忽略ceph-deploy 默认生成的ceph.conf非常精简很多关键参数都没有预设。比如osd pool default size默认副本数、osd pool default pg num默认 PG 数、public network和cluster network的网络隔离这些都需要你在部署前手动加进去。我当时就吃过这个亏——集群部署完才发现默认副本数是 3但测试环境只有两块盘数据一直处于降级状态报错满天飞。现在 Ceph 官方已经不再推荐用 ceph-deploy 部署新集群了取而代之的是 cephadmOctopus 版本起官方主推。cephadm 的思路是容器化部署用一个 bootstrap 命令拉起整个管理平面后面加节点、加服务都通过ceph orch命令编排。相比 ceph-deploy它最大的进步是把 Ceph 服务本身和宿主机解耦了统一用容器承载升级回滚都方便得多。但即便如此国内仍然有大量存量集群跑在 ceph-deploy 部署的架构上。理解 ceph-deploy 的思路对你维护老集群、排查历史问题照样有实际价值。而且 ceph-deploy 里的一些配置理念比如网络规划、CRUSH map 设计放到 cephadm 时代依然是通用的底层逻辑。3. ceph osd tree down 排障实战从看到 down 到恢复 up 的完整链条接下来聊正题——ceph osd tree down到底怎么排查。这是 Ceph 运维里最日常、也最让人头大的问题之一。我尽量把整个排查链路梳理清楚你按着这个思路走大部分问题半小时内能定位到根因。3.1 先看懂 ceph osd tree 的输出再动手排障第一步不是急着重启服务而是先看输出内容。ceph osd tree输出的是一棵按主机分组的 OSD 树状图每个 OSD 后面会跟着一长串状态信息和权重你至少得能看懂下面几项IDOSD 的编号集群内唯一。CLASSOSD 的设备类型通常是hdd、ssd或nvme。WEIGHT这个 OSD 的权重默认等于磁盘容量单位是 TB。权重决定数据在 OSD 间的分布比例。PGS这个 OSD 当前承载的 PG 数量。STATUS最关注的一列up代表正常down代表进程挂了。举个例子ID CLASS WEIGHT TYPE NAME STATUS REWEIGHT PRIMARY-AFFINITY -1 3.00000 root default -2 1.00000 host ceph-node1 0 hdd 1.00000 osd.0 down 1.00000 1.00000 1 hdd 1.00000 osd.1 up 1.00000 1.00000看到osd.0 down你脑海里要立刻反应出几个问题这个 OSD 的进程还在不在对应的磁盘还认不认网络有没有断文件系统是不是满了把这些可能性按概率排个序然后一件事一件事验证。3.2 由简到繁的排障顺序进程、日志、磁盘、网络我的经验是排查顺序永远从最外围的开始先看进程再看日志再查硬件最后才动数据。这样每一步都有明确结论不会白折腾。第一步确认 OSD 进程状态。在 OSD 所在节点上执行systemctl status ceph-osd0注意0的意思是 OSD 的编号。如果服务显示active (running)但集群里仍然显示down那问题多半出在 MON 和 OSD 之间的心跳通信上——OSD 进程活着但 MON 觉得它失联了。这种情况优先检查网络和 MON 的负载。如果服务显示inactive (dead)或者根本没起来那就是 OSD 进程本身挂了继续往日志方向排查。第二步翻日志看原因。Ceph 的日志默认在/var/log/ceph/目录下OSD 的日志文件是ceph-osd.0.log。直接用 tail 看最后几百行tail -n 200 /var/log/ceph/ceph-osd.0.log我最常遇到的几个日志场景日志里出现heartbeat timeout字样说明网络通信出了问题MON 端一直收不到这个 OSD 的心跳。日志里出现bluestore I/O error或slow disk read/write说明磁盘本身存在硬件故障或 IO 卡顿。日志里出现ENOSPC说明磁盘满了OSD 拒绝写入甚至直接退出。日志里出现segfault或assert字样说明 OSD 进程自身崩溃了可能是版本 bug也可能是内存不够。第三步检查磁盘和文件系统。OSD 进程是跑在磁盘上的磁盘状态直接决定 OSD 能不能正常工作。先看磁盘是否存在、系统能不能识别lsblk再查一下磁盘的健康状态。Ceph 用的 bluefs/bluestore 可以直接看ceph-bluestore-tool --path /var/lib/ceph/osd/ceph-0 fsck-status如果磁盘硬件有问题dmesg里通常会有I/O error或者task hung之类的关键词dmesg | grep -i error | tail -n 50这里多说一句我遇到过的down里磁盘原因和网络原因大概各占一半。磁盘的原因里真正物理坏道的很少大部分是磁盘卡 IO 或者控制器超时导致内核把设备置为只读或直接 offline。这时候重启 OSD 服务往往能暂时恢复但如果不换盘很快又会再挂。第四步验证网络连通性和 MON 状态。如果 OSD 进程活着、磁盘也正常那大概率是网络层面的问题。在 MON 节点上 ping 一下 OSD 节点的 cluster network 地址看看延迟和丢包。Ceph 对网络抖动极其敏感哪怕几毫秒的延迟波动都可能触发 MON 将 OSD 标记为 down。查看 MON 的健康状态ceph -s看输出里的health和mon部分确认 MON 之间是否有网络分区。Ceph 的 MON 走的是 Paxos 协议如果 MON 之间长期通信不畅整个集群的仲裁都会出问题表现就是各个 MON 看到的 OSD 状态不一致。3.3 确认问题后按场景选择恢复方案场景一OSD 进程挂了但磁盘正常直接拉起服务。这类情况最常见原因是内存紧张、进程被 OOM killer 杀掉或者 Ceph 自身的版本 bug 导致进程退出。重启服务前先看一眼内存free -h如果内存确实不够先清理掉不必要的进程或者调低 OSD 的缓存上限再启动 OSDsystemctl start ceph-osd0启动后观察集群状态ceph -s ceph osd tree正常情况下OSD 会先进入up然后因为 PG 需要重新同步数据你会看到backfilling的状态等数据追完之后集群会回到activeclean。场景二确认磁盘物理故障需要换盘。如果确认磁盘已经无法恢复那就走换盘流程。先把坏 OSD 从集群中安全移除这一步千万别省略ceph osd purge 0 --yes-i-really-mean-itpurge会把你指定的 OSD 从 CRUSH map 里移除并释放相关 PG 的副本关系。执行完这一步后再在节点上拔掉坏盘、插上新盘然后重新创建 OSDceph-deploy osd create --data /dev/sdb mon1或者用 cephadm 环境的话ceph orch daemon add osd mon1:/dev/sdb新 OSD 加入后集群会自动把缺失的副本重新平衡回来这个过程需要时间取决于数据量和网络带宽急不来。场景三由于网络分区导致的 down先解决网络再拉扯 OSD。这种情况最让人头疼因为 OSD 进程可能完全正常但 MON 认为它挂了。你在 OSD 节点上看到服务是 running 的但集群状态就是 down。这时候强行重启 OSD 服务也没用——因为问题的根因在网络。正确的做法是先检查交换机端口、网卡状态、VLAN 配置确认网络恢复稳定后再看集群状态。网络恢复后MON 和 OSD 之间的心跳会重新建立OSD 状态自动从down变成up通常不需要手动干预。但如果集群已经因为长时间的 down 把 OSD 踢出了in状态比如ceph osd tree里显示为out那就需要你手动把 OSD 拉回来ceph osd in 0这条命令很重要——很多人在网络恢复后只盯着up/down状态忽略了in/out状态结果集群一直处于降级状态却找不到原因。4. 故障恢复后不能直接撒手复盘与加固的几件小事OSD 恢复up只是第一步真正的运维功夫在恢复之后。复盘环节我建议你做这几件事能帮你把这次故障沉淀成下次的免疫力。4.1 先看 PG 状态是否正常回位OSD 恢复后集群会进入数据恢复阶段。这时候最关键的观测指标是 PG 的状态。用命令查看ceph pg stat正常情况下你应该看到大量的activeclean。如果出现activedegraded说明仍然有副本缺失如果出现stuck相关状态说明某些 PG 长时间卡住不动需要进一步排查。我在实际运维中最常见的卡住场景是集群数据量比较大恢复过程中网卡带宽被打满导致恢复速度极其缓慢甚至出现 secondary 副本长时间没有达到最新版本。这种时候要优先保证业务流量和恢复流量的隔离——Ceph 默认的 recovery 流量和客户端流量走同一个网络一旦恢复业务延迟会明显上升。建议在ceph.conf里调低恢复速率[osd] osd_max_backfills 1 osd_recovery_max_active 3调完生效需要重启 OSD也可以在运行时动态调ceph tell osd.* injectargs --osd-max-backfills 1这样恢复过程会变慢但能给业务腾出带宽避免“一边修复一边把其他业务拖垮”的连锁反应。4.2 查一下存储池的容量余量Ceph 的 OSD 挂掉后会触发数据重新平衡而重新平衡是需要额外空间的——新副本要落盘必然占用空间。如果你某个存储池的空间本来就快满了恢复过程很容易直接撞上nearfull甚至full的状态导致整个集群停止写入。所以我每次恢复完 OSD第一件事就是看空间ceph df重点关注USED和AVAIL两列以及%USED的比例。如果某个池的用量超过 80%就要提前做扩容或者清理别等恢复任务启动后才手忙脚乱。4.3 把日志和监控补上避免下次靠人肉定位排障效率的高低很大程度上取决于你手上的监控数据全不全。我强烈建议你至少配置两个层面的监控第一层Ceph 自带的 prometheus 模块。Ceph 从 Luminous 版本开始集成了 Prometheus 监控插件默认监听 9283 端口。MGR 节点上启用ceph mgr module enable prometheus然后用 Prometheus 拉取/metrics接口再配合 Grafana 的 Ceph 官方仪表盘OSD 的状态变化、容量的增长趋势、IO 延迟的波动曲线就都可视化出来了。第二层告警通知。光有监控没有告警等于白搭。我习惯用 Prometheus 的 Alertmanager 搭配 webhook 推送到企业微信或者钉钉规则可以这样配任何 OSD 状态变为down超过 2 分钟触发告警。MON 数量少于 3 或者 MON 之间时钟偏移超过 0.05 秒触发告警。任意存储池使用率超过 85%触发告警。单个 OSD 的 latency 超过基线值的三倍持续 5 分钟触发告警。这些告警规则写起来不复杂关键是阈值要结合自己的集群规模调。我见过不少团队直接抄网上模板结果要么是告警太频繁被当成噪音要么是阈值设得太松真出问题的时候告警没触发。4.4 做一次 CRUSH map 的合理性复盘每次遇到 OSD 大面积 down我都建议顺带检查一下 CRUSH map 的故障域设计是否合理。很多早期集群直接用了默认配置所有 OSD 都堆在default根下面根本不区分主机、机架或机房。这种设计的后果是一旦某台物理机挂了这台机器上的所有 OSD 同时 down如果这些 OSD 的副本恰好都分布在同一批机器上就可能出现数据丢失。合理的 CRUSH map 至少应该做到故障域隔离到主机ceph osd tree看输出里是否每个 OSD 都归属到了各自的主机节点下。如果 OSD 直接挂在default根下面没有经过host层级那就得动 CRUSH map 了。改 CRUSH map 不建议直接编辑二进制文件最好用命令动态调。比如把某个 OSD 从默认层级移动到指定主机ceph osd crush move osd.0 hostceph-node1改完之后再确认数据重新平衡的过程没有异常ceph -s另外如果你要新建存储池强烈建议在创建时就显式声明副本数不要停留在默认值ceph osd pool create testpool 128 128 replicated ceph osd pool set testpool size 3128是 PG 数后面那个128是 PG 数量的上限通常在扩容时用。这个数字不能随便写——PG 数太少会导致单个 PG 过大恢复慢太多会浪费内存和 CPU。业界常用的估算方式是用“PG 数 (OSD 总数 × 100) / 副本数”来算个大概值但更稳妥的是直接参考官方 PG 计算器输入 OSD 数量和存储池预期容量它会推荐一个合理的 PG 数。5. 一些补充经验容易被忽略但很关键的小细节最后分享一些碎片的经验每一个都是我实际踩过坑之后才知道的。别在业务高峰期做大范围的 OSD 重启。这看起来像废话但真的很重要。你重启一个 OSD 没问题副本在其他盘上扛着如果同时重启多个 OSD它们的 PG 副本可能在另外几个 OSD 上重合那就可能触发数据不可用。批量操作前先看一眼ceph osd tree确认要重启的 OSD 在故障域上没有重叠再分批操作。ceph osd out和ceph osd in的用法要刻在脑子里。out并不是让 OSD 进程退出而是告诉集群“这个 OSD 暂时不参与数据分配”数据会被迁移到其他 OSD。这个操作在维护磁盘、更换硬件时非常有用——先把 OSD 置为out等数据迁移干净了再停进程避免在数据恢复期间拔盘导致 PG 卡死。时刻注意 MON 的时钟同步。Ceph 的 MON 对时钟偏移非常敏感偏移超过 0.05 秒就可能出现仲裁问题。我见过一个集群因为某个节点的 NTP 服务挂了MON 之间互相认为对方“失联”整个集群进入了只读模式。所以每个节点上都装好 NTP 服务定期检查ceph time-sync-status这个绝对不能省。set noout 标志在特定场景下是个好帮手。如果你要对整批 OSD 做维护比如机房断电演练或者升级内核提前设置ceph osd set noout可以防止 Ceph 在维护期间自动把 OSD 置为out并触发大规模数据迁移。维护完成后记得ceph osd unset noout。注意不要长期开着 noout否则 OSD 真挂了你不会得到任何数据迁移信号集群会一直带病运行。日志文件是无价的排障资产。有次一个 OSD 反复 down我翻了几百行日志都没找到根因最后是在/var/log/syslog里看到 OOM killer 的记录——Ceph 的 OSD 进程占用内存太高被系统杀了。从那以后我养成了定期看系统日志的习惯并且在部署 OSD 的节点上关闭了不必要的 cron 任务和监控 agent避免它们抢占内存。回到最开始那个半夜报警的场景。我一般在处理完一个 OSD down 之后会把整个流程重复检查一遍先确认ceph -s的 HEALTH_OK再看ceph osd tree的所有 OSD 状态最后看一眼监控面板上的恢复曲线数据追平之后才敢安心回去睡觉。这套流程看起来繁琐但每次照着做基本都能把风险控制在最小范围内。Ceph 的运维没有银弹靠的是对原理的理解和经验一点点积累。希望这篇内容能帮你在面对down的时候少一点慌乱多一份从容。