终于等来了国庆长假!宅在家折腾自己的 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 环境,不是生产环境,仅为读者提供思路,别照抄。
先说清楚 OSD 为什么会被踢出去。Ceph 有个参数 mon_osd_down_out_interval,默认 600 秒。OSD 失联超过这个时间,monitor 就会把它标记为 out,同时把它的 CRUSH weight 清零,这就是所谓的 autoout。
节点断电好几个小时,OSD 1 早就超时了,于是:
HEALTH_OK,81 个 PG 全部 active+clean。单看这一步,这是设计行为,不是故障。真正的问题在后面:节点回来了,OSD 1 却没有自愈。
我第一个动作是看 Pod:
kubectl -n rook-ceph get deploy | grep osd输出里只有 rook-ceph-osd-0/2/3,根本没有 rook-ceph-osd-1 这个 Deployment。
这个信号很说明问题。Rook 管理 OSD 的姿势是这样的:
现象 | 含义 |
|---|---|
Deployment 存在,Pod CrashLoop / Pending | operator 认了这块盘,问题出在运行时、调度或设备上 |
Deployment 根本不存在 | operator 在准备阶段就没采纳这块盘,Pod 从没被创建过 |
也就是说,OSD 1 不是运行时故障,是 operator 主动跳过。这个区别直接把我从「磁盘坏了」拉到「operator 为什么不认它」。
ceph osd df 里 OSD 1 的 SIZE 显示 0 B 也是同一个逻辑:不是盘没了,是压根没被纳管。
Rook 纳管 OSD 的流程是:rook-ceph-osd-prepare 作业先扫盘、判断能不能用,然后才由 operator 创建对应的 Deployment。所以真正的答案在 prepare 作业的日志里。
kubectl -n rook-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 |
|---|---|---|
运行中集群真值 |
|
|
OSD 1 磁盘 BlueStore 元数据 | prepare 日志 |
|
|
|
|
三行凑到一起,答案就出来了:
abb2c4e2-… ✅rook-ceph-mon Secret 里的 fsid 是旧的 f568f7c3-… ❌operator 拿着 Secret 里的过期 FSID 去跟磁盘比对,自然判定「这不是我的盘」,于是跳过采纳。磁盘、数据、PG,全都是好的。
这里我多了个心眼:万一只是巧合,下次会不会又漂?于是我翻了 Rook 上游源码 pkg/operator/ceph/controller/cluster_info.go,搜 fsidSecretNameKey,只有三处:
ClusterInfo 时用)换句话说,fsid 没有任务在「发现不一致时自动纠正」。它一旦写歪,就会一直歪下去。这也解释了为什么节点恢复后 OSD 1 永远无法自愈:这不是等一等就能好的故障。
📝 Notes:这个结论让我对整个修复方案有了信心。改掉 Secret 里的值就够了,磁盘不用动。
修复动作只有一行命令。原则是只改 fsid 字段,保留 ceph-secret、ceph-username、mon-secret 不动。
NEW_FSID=$(ceph fsid)
kubectl -n rook-ceph patch secret rook-ceph-mon --type merge \
-p "{\"data\":{\"fsid\":\"$(echo -n "$NEW_FSID" | base64 -w0)\"}}"然后让 operator 重新加载 ClusterInfo:
kubectl -n rook-ceph rollout restart deploy/rook-ceph-operator🐾 注意:千万别图省事把
rook-ceph-monSecret 删了重建。 它挂着DisasterProtectionFinalizer,删掉很可能被 operator 当成「新集群 bootstrap」,那才是真的灾难。
重启 operator 后,再看 prepare 日志:
1 ceph-volume raw osd devices configured on this node从 0 变成 1,就是采纳成功的信号。接着:
kubectl -n rook-ceph get deploy rook-ceph-osd-1 # 1/1 Ready
ceph osd tree # osd.1 up/in
ceph osd df # crush weight 0.86850OSD 1 自动重新注册,CRUSH weight 恢复,全程没有手工执行 ceph osd in 或 ceph osd crush reweight。
operator 下发新 OSD 配置时,触发了我全部 4 个 OSD 的滚动重启。集群短暂进入 HEALTH_WARN:
指标 | 数值 |
|---|---|
osds down | 1 |
objects degraded | 11648 / 44646 ≈ 26.090% |
pgs degraded | 23 |
看到 26% degraded 那一瞬间,心跳快了一下 😅。不过这属于正常过渡态:期间所有 PG 仍满足副本要求,没有出现 undersized 或 incomplete。大约 1~2 分钟后集群自己恢复了。
断电场景下,最不能信的就是 HEALTH_OK。PG 状态正常只代表「副本数够」,不代表「数据没被写坏」。
所以我对全部 81 个 PG 跑了一遍 deep-scrub:
ceph pg deep-scrub $(ceph pg ls | awk 'NR>1{print $1}')
ceph health detail结果:全部 deep-scrub ok,0 inconsistent objects,HEALTH_OK。至此才算真正收工。
踩完坑总结几条,供各位参考:
ceph osd set noout,升级完再 unset noout,避免 autoout 触发全量数据迁移;rook-ceph-mon Secret,出问题时才有基线可比对;HEALTH_OK 就关电脑。回头看,这次故障的「魔鬼」藏在一个从没被人注意过的字段里。磁盘完好、数据完好、PG 完好,坏的是 rook-ceph-mon Secret 里那个永远不会自动纠正的过期 FSID。而 Rook 上游源码里的「只读不写」,决定了它只能靠人工修。
Deployment 消失 ≠ Pod CrashLoop 这个判据,帮我省了至少一晚上的瞎折腾。三方 FSID 比对加源码级验证,则是把「玄学」变成「确定」的关键两步。
纸上得来终觉浅,绝知此事要躬行。OSD 1 其实压根没病,只是被错认了户口。搞清楚这一点,修复就只是一行 kubectl patch - 精简,优雅。
以上。
pkg/operator/ceph/controller/cluster_info.go(fsidSecretNameKey 定义与读写位置)原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。