☰
Rook-Ceph 数据修复实战
2026/10/10 8:20:27 网站建设 项目流程

前言

终于等来了国庆长假!宅在家折腾自己的 HomeLab、 K8s、 NAS 服务器集群的感觉真爽!这次长假真的是折腾了好多东西,感兴趣的读者请期待后续的爆更,等不及的话也可以直接看我的 repo -east4ming/homelab2的 PR 和 Commits。

今天是第一篇。

事情是这样的:我家 HomeLab 的 Rook-Ceph 集群,前阵子趁着国庆给全部节点从 Ubuntu 24.04 升级到 26.04.1。结果其中一台n100-cheshi-0升级出现问题,我多次重启均无法恢复,🧠一热+一急直接断电,直接 GG😭,一躺就是好几个小时。好在最后还是修复了。(也是一篇文章,敬请期待)

节点重新上线之后,我本以为 Ceph 会自己缓过来,毕竟盘都还在,PG 也没报错。结果一看ceph -s,好家伙,4 个 OSD 只有 3 个在线,OSD 1 躺在 down/out 里装死,ceph osd df里它的 SIZE 是 0 B。

说实话,一开始我脑子里全是「磁盘挂了」「BlueStore 元数据损坏」「要重建 OSD 了」这些最坏的剧本。折腾了半宿才发现,磁盘上那 4TB 数据一点事没有,是 Rook operator 自己「不认」它了。

这篇文章记录从排查到修复的完整链路。重点在排查思路:用三方 FSID 比对加源码验证,把一个看起来像数据损坏的问题,定位成 Secret 里某个字段过期。

📝声明:

  • 本文非广告、非推广,纯属踩坑记录。
  • 环境为 4 节点 K3s + Rook-Ceph(4 OSD),homelab 环境,不是生产环境,仅为读者提供思路,别照抄。

背景:autoout 是罪魁祸首吗?

先说清楚 OSD 为什么会被踢出去。Ceph 有个参数mon_osd_down_out_interval,默认 600 秒。OSD 失联超过这个时间,monitor 就会把它标记为out,同时把它的 CRUSH weight 清零,这就是所谓的 autoout。

节点断电好几个小时,OSD 1 早就超时了,于是:

  1. OSD 1 被 autoout,CRUSH weight 归零;
  2. 它的数据被迁移到其余 3 个 OSD;
  3. 集群恢复到HEALTH_OK,81 个 PG 全部active+clean。

单看这一步,这是设计行为,不是故障。真正的问题在后面:节点回来了,OSD 1 却没有自愈。

判据一:Deployment 消失 ≠ Pod CrashLoop

我第一个动作是看 Pod:

kubectl-nrook-ceph get deploy|greposd

输出里只有rook-ceph-osd-0/2/3,根本没有rook-ceph-osd-1这个 Deployment。

这个信号很说明问题。Rook 管理 OSD 的姿势是这样的:

现象含义
Deployment 存在,Pod CrashLoop / Pendingoperator 认了这块盘,问题出在运行时、调度或设备上
Deployment 根本不存在operator 在准备阶段就没采纳这块盘,Pod 从没被创建过

也就是说,OSD 1 不是运行时故障,是 operator 主动跳过。这个区别直接把我从「磁盘坏了」拉到「operator 为什么不认它」。

ceph osd df里 OSD 1 的 SIZE 显示0 B也是同一个逻辑:不是盘没了,是压根没被纳管。

判据二:osd-prepare 日志才是决定性证据

Rook 纳管 OSD 的流程是:rook-ceph-osd-prepare作业先扫盘、判断能不能用,然后才由 operator 创建对应的 Deployment。所以真正的答案在 prepare 作业的日志里。

kubectl-nrook-ceph logs job/rook-ceph-osd-prepare-n100-cheshi-0

关键几行(已简化):

osd_id: 1 type: bluestore ceph_fsid: abb2c4e2-12a3-4b93-8d90-f2eaf2290901 ... skipping osd.1 ... belonging to a different ceph cluster 0 ceph-volume raw osd devices configured on this node

看到belonging to a different ceph cluster这句,我当时有点懵:我哪来的第二个 Ceph 集群?

不过日志也告诉我,磁盘上的元数据是合法且完整的:osd_id 1、type bluestore、ceph_fsid abb2c4e2-…。问题不在盘,而在 Rook 认为「当前集群的 FSID」和盘上的 FSID 对不上。

三方 FSID 比对

顺着这条线,我把三个来源的 FSID 摆到一起:

来源命令 / 位置FSID
运行中集群真值ceph fsidabb2c4e2-12a3-4b93-8d90-f2eaf2290901
OSD 1 磁盘 BlueStore 元数据prepare 日志ceph_fsidabb2c4e2-…(一致)
rook-ceph-monSecret.data.fsidf568f7c3-5603-4108-99ff-3742cf008a83(过期)

三行凑到一起,答案就出来了:

  • 集群真值 = 磁盘元数据 =abb2c4e2-…✅
  • 只有rook-ceph-monSecret 里的fsid是旧的f568f7c3-…❌

operator 拿着 Secret 里的过期 FSID 去跟磁盘比对,自然判定「这不是我的盘」,于是跳过采纳。磁盘、数据、PG,全都是好的。

源码级验证:为什么这个字段不会自动纠正?

这里我多了个心眼:万一只是巧合,下次会不会又漂?于是我翻了 Rook 上游源码pkg/operator/ceph/controller/cluster_info.go,搜fsidSecretNameKey,只有三处:

  • L52:常量定义
  • L154:读取(构造ClusterInfo时用)
  • L423:写入,且仅在 Secret 不存在、创建新集群时才写

换句话说,fsid没有任务在「发现不一致时自动纠正」。它一旦写歪,就会一直歪下去。这也解释了为什么节点恢复后 OSD 1 永远无法自愈:这不是等一等就能好的故障。

📝Notes:这个结论让我对整个修复方案有了信心。改掉 Secret 里的值就够了,磁盘不用动。

修复:patch 而非 delete

修复动作只有一行命令。原则是只改fsid字段,保留ceph-secret、ceph-username、mon-secret不动。

NEW_FSID=$(ceph fsid)kubectl-nrook-ceph patch secret rook-ceph-mon--typemerge\-p"{\"data\":{\"fsid\":\"$(echo-n"$NEW_FSID"|base64-w0)\"}}"

然后让 operator 重新加载ClusterInfo:

kubectl-nrook-ceph rollout restart deploy/rook-ceph-operator

🐾注意:千万别图省事把rook-ceph-monSecret 删了重建。它挂着DisasterProtectionFinalizer,删掉很可能被 operator 当成「新集群 bootstrap」,那才是真的灾难。

验证:从 0 devices 到 1 devices

重启 operator 后,再看 prepare 日志:

1 ceph-volume raw osd devices configured on this node

从0变成1,就是采纳成功的信号。接着:

kubectl-nrook-ceph get deploy rook-ceph-osd-1# 1/1 Readyceph osd tree# osd.1 up/inceph osddf# crush weight 0.86850

OSD 1 自动重新注册,CRUSH weight 恢复,全程没有手工执行ceph osd in或ceph osd crush reweight。

副作用:全部 OSD 滚动重启

operator 下发新 OSD 配置时,触发了我全部 4 个 OSD 的滚动重启。集群短暂进入HEALTH_WARN:

指标数值
osds down1
objects degraded11648 / 44646 ≈ 26.090%
pgs degraded23

看到 26% degraded 那一瞬间,心跳快了一下 😅。不过这属于正常过渡态:期间所有 PG 仍满足副本要求,没有出现undersized或incomplete。大约 1~2 分钟后集群自己恢复了。

闭环:HEALTH_OK ≠ 数据无损

断电场景下,最不能信的就是HEALTH_OK。PG 状态正常只代表「副本数够」,不代表「数据没被写坏」。

所以我对全部 81 个 PG 跑了一遍 deep-scrub:

ceph pg deep-scrub$(ceph pgls|awk'NR>1{print $1}')ceph health detail

结果:全部deep-scrub ok,0 inconsistent objects,HEALTH_OK。至此才算真正收工。

预防措施

踩完坑总结几条,供各位参考:

  1. 升级前ceph osd set noout,升级完再unset noout,避免 autoout 触发全量数据迁移;
  2. 给节点升级准备 UPS,另外不要手贱强制关机,几百块的 UPS 比几 TB 的重平衡便宜多了, 另外这次就是断电的教训;
  3. 备份rook-ceph-monSecret,出问题时才有基线可比对;
  4. 恢复后必须 deep-scrub,别只看到HEALTH_OK就关电脑。

总结

回头看,这次故障的「魔鬼」藏在一个从没被人注意过的字段里。磁盘完好、数据完好、PG 完好,坏的是rook-ceph-monSecret 里那个永远不会自动纠正的过期 FSID。而 Rook 上游源码里的「只读不写」,决定了它只能靠人工修。

Deployment 消失 ≠ Pod CrashLoop这个判据,帮我省了至少一晚上的瞎折腾。三方 FSID 比对加源码级验证,则是把「玄学」变成「确定」的关键两步。

纸上得来终觉浅,绝知此事要躬行。OSD 1 其实压根没病,只是被错认了户口。搞清楚这一点,修复就只是一行kubectl patch- 精简,优雅。

以上。

📚️ 参考文档

  • Rook 官方文档 - Ceph OSD Management
  • Ceph 文档 - OSD autoout 与 mon_osd_down_out_interval
  • Ceph 文档 - Deep Scrubbing
  • Rook 源码pkg/operator/ceph/controller/cluster_info.go(fsidSecretNameKey定义与读写位置)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询