土法炼钢 · 系统与基础设施

【Ceph RADOS】排障坐标系:slow ops、peering 卡死、满盘、恢复拖死前台与 checksum 错误

文章导航

分类入口
storagedistributed
标签入口
#ceph#troubleshooting#slow-ops#peering#full#recovery#checksum#squid#v19.2.5

目录

「集群很慢」与「集群不健康」的诊断起点不同。前者可能来自磁盘、来自网络、来自 PG peering 占住锁、也可能来自 mClock 配错把前台 op 降了优先级;后者可能是 OSD 宕机、满盘、checksum 不一致。若不先选轴,从「调整 osd_op_num_threads」开始,往往绕一大圈还没找到根因。

本文是系列第 16 篇:按五轴映射症状,并给出核对顺序与观测入口。不粘贴未执行命令输出,字段含义见第 15 篇。

本文是「Ceph / RADOS 存储内核」系列第 16 篇(共 18 篇)。→ 系列目录

篇目 核心内容
第 15 篇 · 可观测 ceph status / pg dump / OSD perf 口径
第 16 篇 · 排障坐标系 slow ops、peering、满盘、恢复拖死、checksum
第 17 篇 · 对照替代路径 MinIO / 云块 / 本机 NVMe+SPDK

版本锚定:Ceph Squid v19.2.5


一、先选轴,再动参数

轴一:Slow Ops     — 客户端 op 长时间 pending,业务超时或延迟飙升
轴二:Peering 卡死  — PG 长期 peering/activating,部分数据不可访问
轴三:满盘         — OSD 达到 nearfull/full 阈值,写入被限制或拒绝
轴四:恢复拖死前台  — recovery/backfill 与前台 I/O 争 OSD 资源
轴五:Checksum 错误 — scrub/deep-scrub 发现副本不一致或位翻转

每轴固定三列:症状 → 先查什么 → 不要先做什么。并行乱调 osd_op_num_threads、MTU、mClock profile,会毁掉因果链——和 SPDK 排障 的「一次否证一轴」同一纪律。

flowchart TD
  symptom["症状:超时 / 延迟升 / 写入拒绝 / 不一致"]
  symptom --> axis["选轴"]
  axis --> a1["轴一 Slow Ops"]
  axis --> a2["轴二 Peering"]
  axis --> a3["轴三 满盘"]
  axis --> a4["轴四 恢复拖死"]
  axis --> a5["轴五 Checksum"]
症状(触发选轴) 先查 不要先做
业务超时 + slow_requests / OSD log slow request health detail → 目标 OSD dump_ops_in_flight(卡在哪一等待态)→ kv_sync_lat osd_op_num_threads;改客户端超时「先扛住」
PG inactive / peering 久;该 PG 上 RBD/FS/RGW hang osd tree 谁 down → pg queryblocked_by / LES 立刻 force-create-pg;未确认 LES 就 force-recovery
nearfull/full;写返回 ENOSPC osd df tree 单盘 USE% 只看 ceph df 集群均值;无限抬 full_ratio
有 recovering/backfilling 且前台延迟升 对比 recovering OSD 与其它 OSD 的 commit latency;iostat 同时狂降 recovery 又狂升 client 权重「两边都拧」
inconsistent / checksum mismatch / 读 EIO pg query scrub_errors 类型 → OSD log 哪副本件坏 → SMART 未看磁盘就 pg repair

默认入口顺序(可裁剪):health detailpg dump_stuckosd perf →(慢)dump_ops_in_flight →(peering)pg query


二、轴一:Slow Ops(慢操作)

症状

核对路径

第一步:定位哪些 OSD 有 slow op

ceph health detail 中的 slow_request 通常会列出涉及的 OSD ID 与 PG。若未列出,检查各 OSD 日志(/var/log/ceph/ceph-osd.{id}.log)中的 slow request 行,确认是 Primary 还是 Replica 慢。

第二步:查当前进行中的 op

ceph tell osd.{id} dump_ops_in_flight 输出每个 op 的已停留时长与当前所在的处理阶段(waiting for osd_op_tp_lockwaiting for subopwaiting for snap_trimmer 等)。若 op 卡在「waiting for subop」,说明是某个 replica OSD 慢;若卡在「waiting for osd_op_tp_lock」,说明是 OSD 线程池争用(CPU 或 I/O 内部并发瓶颈)。

第三步:分层定位

op 卡在哪 倾向的根因
waiting for subop replica OSD 磁盘/网络慢
waiting for kv / bluestore 状态 Primary 的 BlueStore WAL(RocksDB sync)慢;见第 8 篇
waiting for snapset snap_trimmer 占住锁;快照删除密集时出现
waiting for osd_lock / thread pool OSD 内部并发瓶颈(CPU 或 op 队列深度不足)
waiting for scrub scrub 占用 PG 锁,导致正常 op 排队

第四步:排除磁盘 I/O 慢

perf dumpbluestore.kv_sync_latstate_kv_queued_lat 是磁盘/WAL 慢的直接信号;宿主机 iostat -x%util/await 作旁证。

不要先做:加 osd_op_num_threads(磁盘饱和时只加锁争用);先改业务超时;在未分清 Primary/Replica 前重装网卡驱动。


三、轴二:Peering 卡死

症状

核对路径

第一步:确认 OSD 状态

ceph osd tree 查看哪些 OSD 是 down。Peering 卡死的最常见原因是 primary OSD 或 peering 所需的某个 replica OSD down。若 down OSD 超过 osd_mon_report_interval(默认 5 秒),MON 会标记为 down;若超过 osd_down_out_interval(默认 300 秒),MON 自动标记为 out 并触发重新分配(re-peering)。

第二步:查 PG peering 状态

对每个 stuck PG 执行 ceph pg {pgid} query(向 primary OSD 查询),输出包含:

若某个 OSD 在 probe_targets 中但长时间无回应,该 OSD 进程可能卡死(ceph daemon osd.{id} status 无响应),需强制 kill 并重启;若 OSD 已 down,MON 应已触发超时 out,此时等待 OSD map 更新完成 peering。

第三步:last_epoch_started 不一致

若 PG 有足够的 OSD up,但 peering 仍卡,可能是 LES 分歧:某个 OSD 的 LES 比其他人低太多,其 PG log 跨度不够覆盖最新 epoch。此时 peering 等待那个 OSD 补齐;若该 OSD 数据已过时且无法恢复,需要 ceph pg force-recovery {pgid}(迫使 primary 用现有最佳副本 activate,接受数据可能过时的风险)。这是有损操作,需评估可接受损失。

第四步:incomplete PG

incomplete 的 PG 需要最高级别介入。先确认是否有对应 OSD 仍在但未启动(重启可能解决),再考虑 ceph pg force-recovery 作为最后手段。若业务允许数据丢失(日志类数据),也可 ceph osd force-create-pg {pgid} 强制重建空 PG(数据丢失操作,慎用)。

不要先做:未见 blocked_by/LES 就 force-create-pg;在 OSD 仍 down+in 时疯狂 restart 同一坏盘节点而不 out。


四、轴三:满盘(nearfull / full)

症状

阈值机制

阈值 配置项 默认值 触发行为
nearfull osd_nearfull_ratio 0.85 health WARN,不影响 I/O
full osd_full_ratio 0.95 OSD 拒绝所有写入(返回 ENOSPC)
backfill_full osd_backfill_full_ratio 0.90 OSD 拒绝接受 backfill 写(保护 full 前的窗口)

注意:阈值基于单个 OSD 的使用率,不是集群整体。若集群中有几个 OSD 特别满(数据分布不均),整体 ceph df 可能显示容量充足,但部分 OSD 触发 full。

核对路径

第一步:定位哪些 OSD 满

ceph osd df tree 按 OSD 显示使用率(USE%);-f plain 格式便于 grep。找出 USE% 接近或超过 85% 的 OSD。

第二步:短期缓解

第三步:根本解决

加 OSD(扩容磁盘),增加 OSD 后 CRUSH rebalance 自动将数据分散。加 OSD 触发大量 backfill,需在业务低峰期进行。

隐患:若 osd_nearfull_ratio 告警未及时处理,很快触发 full,导致集群写入全停。建议在 Prometheus 告警中对 nearfull 配置足够早的告警(建议 USE% > 75% 即开始跟踪)。

不要先做:只盯 ceph df 集群 RAW%;把 full_ratio 一次抬到 0.99 当「扩容策略」;在满盘 OSD 上同时触发大规模 backfill。


五、轴四:Recovery / Backfill 拖死前台

症状

机制回顾

Recovery 和 backfill 是后台操作,与前台 client I/O 共用同一 OSD 的磁盘带宽和网络带宽(详见第 10 篇)。mClock 调度器(Squid 默认)通过为 client op、recovery、backfill、scrub 分配不同优先级来保护前台,但 mClock 的优先级基于队列调度,当磁盘本身 I/O 耗尽时(%util=100%),调度无法凭空增加 IOPS。

核对路径

第一步:确认是否真的拖死前台

比较 ceph osd perf 中受影响 OSD 的 commit latency 与正在 recovering 的 OSD。若 commit latency 仅在 recovering OSD 上升高,说明这些 OSD 的磁盘已是瓶颈;若非 recovering OSD 的 latency 也升高,需检查是否有网络拥塞(recovery 流量打满 OSD 之间的网络,导致所有 replica write 慢)。

第二步:限制 recovery/backfill 速率

若确认前台已被影响:

ceph config set osd osd_recovery_max_active_hdd 1        # 每个 OSD 并发 recovery op 上限(HDD)
ceph config set osd osd_recovery_max_active_ssd 2        # SSD 版本
ceph config set osd osd_max_backfills 1                  # 每个 OSD 并发 backfill op 上限
ceph config set osd osd_recovery_sleep_hdd 0.1           # recovery op 之间 sleep(s)

降低并发和加 sleep 是以「拖长恢复时间」换「保护前台」,需在恢复时间(PG 处于降级)与前台 SLO 之间权衡。

第三步:强制提升某 PG 恢复优先级

若某个 PG 降级严重(副本数 = 1),需加速恢复时:

ceph pg force-recovery {pgid}
ceph pg force-backfill {pgid}

标记为 forced_recovery 的 PG 在 mClock 中获得更高权重,但代价是其他 PG 恢复减速,以及对该 OSD 的前台延迟影响更大。

mClock 配置陷阱:Squid 的 mClock 默认 profile 是 high_recovery_ops,在纯 SSD 集群上可能恢复很激进,前台 P99 延迟上升。可切换为 balancedhigh_client_opsceph config set osd osd_mclock_profile balanced)重新平衡。

不要先做:未确认「延迟升与 recovering OSD 同集合」就全局砍 recovery;把轴一的线程旋钮和轴四的 mClock 旋钮同一变更窗口一起拧。


六、轴五:Checksum 错误与 PG inconsistent

症状

机制回顾

BlueStore 对写入的每个数据块计算 crc32c 校验(详见第 9 篇)。读取时重新计算并比对;若不一致,记录 checksum mismatch 并在 OSD log 中写入告警,以及通知 MON 更新 health 状态。Deep-scrub 会对每个对象的所有副本做全量 checksum 验证,发现副本间内容不同时标记 PG 为 inconsistent

核对路径

第一步:确认 inconsistent PG 的来源

ceph health detail 列出 inconsistent PG 的 ID。对每个 inconsistent PG 执行:

ceph pg {pgid} query

输出中的 scrub_errors 字段说明不一致的类型:read_error(磁盘读失败)、object_info_inconsistency(对象 size/mtime 不一致)、object_data_inconsistency(数据内容不一致)。

第二步:确认有哪些对象不一致

scrub 结果记录在 Primary OSD 的 log 文件中(/var/log/ceph/ceph-osd.{id}.log),搜索 ERR objdata_digest_mismatchsize_mismatch。对应的对象名、PG 和 OSD 信息都在这里。

第三步:尝试 repair

ceph pg repair {pgid}

repair 命令触发一次 deep-scrub,并在发现不一致对象时将少数派副本(错误副本)用多数派副本(正确副本)覆盖。前提:多数派副本的 checksum 是正确的——若磁盘发生多点故障导致多数副本损坏,repair 可能覆盖正确的少数副本。在执行 repair 之前,应先通过 OSD log 确认哪个副本是可信来源。

第四步:硬件失败的判断

若同一 OSD 反复出现 checksum error(read_error 类型),通常是磁盘坏块。此时: - 检查 OSD 宿主机的 dmesgsmartctl -a /dev/{disk} 确认磁盘状态。 - 若磁盘已损坏,将该 OSD 标记为 out(ceph osd out {id}),触发数据恢复到其他 OSD,再替换磁盘。

不要直接 repair 而不检查磁盘:若磁盘是 silent corruption(静默损坏,checksum 不一致但未触发 I/O error),repair 时写回的数据可能立即再次损坏。此时应先换磁盘再 repair。

不要先做:把 repair 当「清除 WARN 的按钮」;在未区分 read_error vs data_digest_mismatch 前批量 repair 多个 PG。


七、串联案例(机制推演,非实录)

以下「案例」仅演示选轴顺序,不冒充事故报告,不含伪造日志或 ceph -s 截图。

案例 A:业务写超时,ceph health 报 slow_requests。
先定轴一(slow ops):ceph health detail 确认涉及 OSD ID,dump_ops_in_flight 找到 op 卡在「waiting for subop」。进入 replica 方向:对 replica OSD 做 ceph osd perf,commit latency 高出其他 OSD 2 倍。再看该 OSD 宿主机的 iostat -x%util=99%。结论:磁盘 I/O 饱和,非网络问题,非 mClock 配置问题。操作:降低该 OSD 的 CRUSH weight(ceph osd reweight)迁移负载,等待磁盘更换。

案例 B:扩容后 backfill 期间前台延迟上升。
先定轴四(恢复拖死):ceph -s 显示大量 backfilling PG,osd perf 显示参与 backfill 的 OSD commit latency 从 2ms 升到 15ms。验证:iostat 确认磁盘 %util 高于平时 40%。操作:ceph config set osd osd_max_backfills 1(从默认 1 降至更严格限速可视需要),或在业务低峰期执行扩容。此案例中不要同时调 slow op 参数——slow op 是前台延迟的结果,根因在 backfill 占 I/O。

案例 C:deep-scrub 后 HEALTH_WARN inconsistent PG
直接进轴五:ceph health detail 列出 pg X.Y is inconsistent。对 PG X.Y 执行 query 查看 scrub_errors 类型,若为 data_digest_mismatch,先检查 Primary OSD log 确认哪个副本是错误来源。在对应 OSD 宿主机检查 smartctl,若磁盘 SMART 有 reallocated sectors 增加,先 ceph osd out {id} 等待恢复,再换磁盘。不要在未确认磁盘状态之前直接 ceph pg repair

八、五轴检查清单(可剪入 on-call runbook)

  1. 起点ceph health detail——看清是哪类 warning/error,对应哪个轴。
  2. PG 状态ceph pg dump_stuck / ceph pg {pgid} query——PG peering/inconsistent/incomplete。
  3. OSD 延迟ceph osd perf——commit latency 异常的 OSD 先排查磁盘。
  4. 当前 slow opceph tell osd.{id} dump_ops_in_flight——看 op 卡在哪个阶段。
  5. 容量ceph osd df tree——任何 OSD USE% > 85% 进轴三。
  6. 恢复速率ceph status io 行有 recovery 吞吐时,确认是否与前台延迟升高同步出现。
  7. Checksum:每次 inconsistent PG 出现,先查 OSD log 再 repair。
  8. 配置漂移:确认 osd_mclock_profileosd_recovery_max_activeosd_max_backfills 是否与预期一致(避免升级或 cephadm 重新应用配置后意外改变)。

跨轴提示:slow op(轴一)和 recovery 拖死(轴四)经常同时出现——先确认 slow op 的 op 阶段是否在 waiting for subop(说明 replica OSD 磁盘忙于 recovery),否则两个轴的调优方向不同,同时乱调只会污染因果链。


九、谱系、争论与开放问题

谱系

排障坐标系不是独立发明:它把第 5 篇 peering、第 6 篇读租约、第 8–9 篇 BlueStore、第 10 篇 recovery/mClock 的失败落点收成五条可互斥的轴。学术上对应两层传统——Weil 等 OSDI/PDSW 的「故障域与自主恢复」叙事,以及 Gulati 等 OSDI 2010 mClock 把客户端/恢复/后台流量拆成可加权服务类。本篇贡献是运维可执行的归因顺序,不是新算法。

争论:先调 mClock 还是先换盘

A 侧(配置优先):前台延迟升高时先改 osd_mclock_profile / osd_max_backfills,假设 I/O 争用可被调度器切开。
B 侧(介质优先):先用 osd perf + 宿主机 %util/SMART 证伪「单盘饱和」;mClock 不调度 RocksDB compaction 与 BlueStore deferred_finisher(第 10 篇),改 profile 可能只移动症状。
可检验裁决:若降低 backfills 后 commit latency 仍钉在同一 OSD,而 %util≈100%,则判 B;若 latency 随 backfill 并发线性下降且磁盘未饱和,再谈 A。

开放问题

  1. 五轴对 Crimson/SeaStore 是否同构:Classic 的 dump_ops_in_flight 阶段名与 Crimson reactor 路径是否可一一映射?在 Squid 技术预览边界内,不能假设本篇 runbook 直接可搬(入口:第 18 篇 Crimson 边界)。
  2. incomplete 有损恢复的可观测门槛:何时允许人工介入「接受数据丢失」?需要哪些 LES / missing / unfound 字段同时满足才不算误判?问题保持开放,禁止把论坛步骤当规范。

参考资料

官方文档(A 级)

系列内

上一篇:可观测 · 系列目录 · 下一篇:对照替代路径

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。


By .