PVE 存储全盘问号复盘(下):真凶不是 pvestatd,而是时钟跳变引爆的 Ceph 雪崩
引言:上一篇的结论,这次只对了一半
半年前我写过一篇《PVE 存储全盘问号、本地盘读不到的死锁故障排查与虚拟机跨盘导出恢复》,结论是:外部存储(PBS/NFS)不可达 → pvestatd 轮询时陷入 I/O 等待 → 管理面单线程被拖死 → 网页端全盘问号。处置手段是 killall -9 pvesm/pvestatd/pvedaemon/pveproxy + 给失联存储加 disable 1 + 重启服务。
2026 年 9 月 16 日傍晚,同样的”全盘问号”再次出现。我照着那套流程走了一遍——完全无效。
这一次的卡点根本不在 pvestatd,而在内核态:Ceph RBD 设备的读 I/O 卡住了,mount 进程停在不可中断的 D 状态。killall -9 连信号都送不进去,杀掉 pvestatd 只会让它在几秒后重新卡在同一个位置。
本文是那篇的续集与纠错:把这次的证据链完整摆出来,并给出真正有效的排查顺序。
一、现象:同样全盘问号,但关键指标和上次不一样
- PVE 网页端左侧树状图里,节点、虚拟机、容器、
local、local-lvm全带灰色问号; pvestatd日志里出现单次状态更新耗时 806 秒 / 1163 秒 / 1217 秒——管理面确实被拖死了十几分钟;- 但
pvesm status1.5 秒正常返回,所有存储都是active; df -h、/etc/pve/nodes、corosync 仲裁全部正常。
一句话总结这个差异:管理面确实被拖死了,但它不是”死锁在 pvestatd 自己的轮询逻辑里”,而是被内核 I/O 拖住的。
而 pvesm status 快不快,恰好就是区分这两种情况的第一个分水岭。
二、排查过程:六步定位
Step 1:先量化现象边界,别急着 killall
1 | |
本次输出:
1 | |
1 | |
pvesm 不卡、pvestatd 报存储挂载失败——说明问题不在 PVE 自己的轮询代码里,继续往内核走。
Step 2:抓 D 状态进程与内核栈(本次的破局点)
1 | |
1 | |
1 | |
1 | |
这一段栈信息是整次排查的转折点,翻译过来就是:
容器 rootfs 挂在 Ceph RBD 设备(
/dev/rbd0)上,挂载时要做 ext4 日志回放(jbd2_journal_recover),而读日志块(jread→bh_uptodate_or_lock)一直等不到 I/O 返回。
内核态 D 状态 + RBD 设备 → 矛头立刻指向 Ceph,而不是 NFS。
经验:
D状态的进程是杀不掉的,kill -9只会挂在信号队列里。看到 D 状态,第一件事是cat /proc/<PID>/stack,而不是 killall。
Step 3:检查 Ceph 集群与 RBD 锁
1 | |
抓到四组异常:
1 | |
clock skew detected on mon.cl3:偏差 6.68e7 秒,约 774 天——有一台节点的时钟差得离谱;nodown flag(s) set:有人给 Ceph 设置了nodown,OSD 永远不会被判为 down,客户端会一直 hang 而不是切换;Slow OSD heartbeats:前端/后端网络心跳最长 1.6 秒;16 osds: 16 up (since 2m):所有 OSD 两分钟前刚重启过——这是最可疑的一条。
再看内核日志:
1 | |
重启前的老客户端(client464914337)没释放 RBD 镜像锁,新挂载要反复等待、最后强制破锁——这一步会白白卡掉几十秒,正好是”挂载卡住”的表象之一。
Step 4:回溯上一个 boot,问一句”OSD 为什么刚全部重启”
1 | |
关键三行(注意:日志前缀里的 Jun 16 是错误时钟自己的时间戳):
1 | |
系统时钟被 chrony 一次性向前拨了 72,927,926 秒(约 844 天),从错误时间直接跳到当前时间。pve-ha-crm 那句 loop take too long (72927931 seconds) 就是时钟跳变的”指纹”。
时钟一跳,systemd 的 OnCalendar 定时器集体补跑:
1 | |
另外 RRD 也会报警,因为它没法接受时间倒退/跳跃:
1 | |
Step 5:谁 HUP 了 Ceph?——logrotate 的 postrotate
紧接着的下一行日志,凶手就自己报了名:
1 | |
追这条 killall 的出处:
1 | |
1 | |
这是 logrotate 轮转 Ceph 日志后,让守护进程重新打开日志文件的常规 postrotate 动作。 平时它人畜无害(每天轮转一次,发个 SIGHUP 让 Ceph 换日志句柄而已);但在时钟跳变的那一秒,它和其它定时任务一起被”补跑”,于是这一记 SIGHUP 精准地砸在了刚起身、还没稳住的 Ceph 守护进程上。
至此链条完全闭合:
1 | |
Step 6:另一条独立病因——硬挂载 NFS 撞上后端重启
1 | |
1 | |
1 | |
- 一台节点在 17:29–17:32 被
192.168.0.252的 NFS 超时刷屏,之后日志直接中断(没能留下正常关机记录); - 存储服务器(192.168.0.108)当天重启了多次,重启后 PVE 立刻报
storage 'BIGSTOR' is not online; - 三个 NFS 存储里,只有
BIGSTOR加固成了soft,timeo=10,retrans=2,retry=0,DS01和Kstor仍是默认的hard,timeo=600,retrans=2。
结论:NFS 是这次的”引信”,Ceph 是”炸药”。 两者都会把 pvestatd 拖死,但处置方式完全不同。
三、根因总结
| 链条 | 触发 | 传导 | 结果 |
|---|---|---|---|
| 时钟链条 | RTC/BIOS 时间错(实测三台节点分别偏 1063 / 774 / 844 天) | chrony 大步长 step → 定时器补跑 → logrotate 对全部 Ceph 守护进程 killall -1 |
OSD 集体重启 → RBD I/O 停摆 → 挂载卡 D 状态 → pvestatd 被拖死 800~1200 秒 |
| NFS 链条 | 存储服务器重启、192.168.0.252 失联 |
hard,timeo=600 挂载进入不可中断等待 |
同样是 D 状态、同样拖死管理面 |
四、与上一篇的差异(更正)
| 对比项 | 上一篇的结论 | 本次实测 |
|---|---|---|
| 现象 | pvesm status 卡死、无响应 |
pvesm status 1.5 秒正常返回,存储全 active,问号照样出现 |
| 卡点层级 | pvestatd 轮询外部存储陷入 I/O 等待 |
内核态 RBD 日志回放读 I/O(jbd2_journal_recover → D 状态) |
killall -9 pvesm/pvestatd |
有效,能解开死锁 | 无效:D 状态进程送不进信号;杀掉 pvestatd 只是让它几秒后重新卡在同一处 |
| 处置顺序 | 先杀进程 → 禁用存储 → 重启服务 | 先救后端(Ceph/NFS)→ 对不可达存储 disable 1 → 必要时重启节点 → 最后才动管理面服务 |
| 排查入口 | 检查 storage.cfg 外置存储 |
先 ps 抓 D 状态进程 + cat /proc/<PID>/stack,再按设备类型分流 |
上一篇的核心逻辑并没有错——外置存储不可达确实会拖死管理面,这次也再次验证了(NFS 那条链条)。错的是把”killall 管理进程”当成了通用解法:它只对”管理面自己的轮询死循环”有效,对内核 D 状态 I/O 完全无能为力。
五、更新后的应急处置顺序(Runbook)
1 | |
顺序原则:先数据面(Ceph/NFS 后端),再存储配置,再节点,最后管理面。 反过来做,只会反复卡在同一个位置。
六、预防措施
- 修 RTC/CMOS(最高优先级):本次三台节点开机时 RTC 时间分别错误 1063 / 774 / 844 天。只要 RTC 不修,每次重启都会重演”时钟跳变 → 定时器风暴 → logrotate HUP Ceph”这套组合拳。换主板电池、进 BIOS 校时,并用
hwclock -r与timedatectl复核。 - 加固 chrony:
1
2
3grep -E "makestep|rtcsync" /etc/chrony/chrony.conf
# 建议保留 makestep(让时钟尽快对齐),但更关键的是 rtcsync——
# 让 chrony 把正确时间写回 RTC,避免下次开机又从头错起 - 收敛 logrotate 的 Ceph 段:确认
/etc/logrotate.d/ceph-common的 postrotate 是否必须对所有 Ceph 守护进程发信号;可以缩小到必要进程,或改为夜间固定时段执行,避免与开机时的时间跳变撞车。 - 统一 NFS 挂载参数:至少不要让
timeo=600的hard挂载对着一个会重启的存储服务器。soft,timeo=10,retrans=2,retry=0的代价是写失败会返回EIO(需要应用层重试),但对 PVE 管理面稳定性收益明显。注意 NFS 选项写在storage.cfg里才持久。 - 清理 Ceph 的
nodown:这个 flag 会让 OSD 永不判 down,客户端一直 hang 而不切换。确认无正在进行的维护后执行ceph osd unset nodown。 - 盯住 PBS 容量:本次检查发现备份 datastore 已用 81.97%(14.76 TiB 只剩 1.92 TiB),且裁剪策略是
keep-all=1(永不裁剪)。要么改保留策略,要么扩容,否则备份迟早写失败。 - 四类告警信号(任一出现都值得立刻看):
pvestatd: status update time (N seconds),N 超过几十秒;- 内核
nfs: server X not responding; chronyd: System clock was stepped;ceph health不再是HEALTH_OK。
七、一句话总结
PVE 的”全盘问号”只是一个症状。真正要问的是两个问题:问号出现时,
pvesm status还跑得动吗?D 状态进程卡在哪个设备上?前者决定管理面是不是自己死锁(那就 killall + 重启服务),后者决定你是该去救 Ceph、还是该去修时钟。
只要 SSH 还能连上、df 还能跑,数据大概率是安全的——但”遇事不慌”的前提是知道该看哪个指标,而不是背一套固定的命令。