本文基于生产环境中排查 slow ops(
waiting for clean to repair)的真实问题展开,系统梳理 Ceph PG 的核心状态、状态流转机制,以及 repair、recovery、backfill 的深层原理,并给出线上常见故障的排查与修复方法。
一、什么是 PG?
PG(Placement Group,放置组)是 Ceph 中数据分布的基本逻辑单元。
-
客户端写入的每个对象,首先被哈希映射到某个 PG
-
再由 PG 映射到一组 OSD(主副本 + 从副本)
-
数据的副本管理、一致性维护、故障恢复,都以 PG 为粒度进行
客户端 → 对象 (object) → PG → OSD 组 [Primary, Secondary1, Secondary2]
理解 PG 的状态,是理解 Ceph 数据可靠性和故障处理的关键。
二、PG 核心状态含义
PG 的状态是可以叠加的 ,一个 PG 可以同时处于多个状态,如 active+degraded+recovering。
2.1 active
PG 处于活跃状态,可以正常响应客户端的读写请求。几乎所有正常工作的 PG 都带有 active 标志。
2.2 clean ✅
clean 是 PG 最"健康"的状态。
clean 的含义:
- 副本数量 = 配置要求(如 3 副本策略下,3 个副本都存在)
- 所有副本数据版本一致,没有任何落后的副本
- Peering 已完成,Primary OSD 知道所有副本的状态
- 没有正在进行的 recovery 或 backfill
特别注意:clean ≠ 数据内容一定正确
clean 只保证副本的数量和版本正确,但不能保证磁盘上的字节没有被悄悄损坏(如坏道、比特翻转)。这正是 repair 存在的意义。
2.3 degraded ⚠️
副本数量不足,但 PG 仍然可以读写。
产生原因:
-
某个 OSD 挂掉或下线,该 PG 在那个 OSD 上的副本丢失
-
例如 3 副本配置,某 OSD 离线后只剩 2 个副本
正常: OSD1(Primary) + OSD2(Secondary) + OSD3(Secondary) → active+clean
OSD3挂: OSD1(Primary) + OSD2(Secondary) + OSD3(❌离线) → active+degraded
degraded 状态下数据仍可读写,但风险升高------此时若再有一个 OSD 挂掉,可能导致数据不可用。
2.4 peering(协商中)
PG 各副本 OSD 之间正在互相协商,确定当前哪些数据是最新的、权威的。
产生原因:
- OSD 重启或新加入
- Primary OSD 切换
Peering 是所有恢复操作的前置状态,成功后才能进入 recovering 或 clean。
2.5 recovering(恢复中)
正在把落后/丢失的对象从其他副本同步过来,补齐副本数量。
产生原因:
- OSD 重新上线后,发现自己的数据比其他副本落后
- 是
degraded → clean的过渡状态
特点:恢复的是增量数据,只同步 OSD 下线期间缺失的对象。
2.6 backfilling(回填中)
全量将 PG 数据推送到一个新的 OSD 上。
产生原因:
- 集群扩容后,CRUSH map 变化,PG 被迁移到新 OSD
- 新 OSD 加入集群,没有该 PG 的任何历史数据,必须全量拷贝
特点:比 recovery 更重,是全量迁移,对集群 IO 影响较大。
2.7 stale(陈旧)
Primary OSD 长时间没有向 Monitor 上报 PG 状态。
产生原因:
- Primary OSD 挂掉或网络隔离
- 严重时 PG 状态变为
unknown
2.8 incomplete(不完整)
Peering 失败,集群无法找到包含最新数据的足够 OSD。
产生原因:
- 多个 OSD 同时挂掉,且丢失了最近写入的数据
- 这是非常危险的状态,可能意味着数据已经永久丢失
2.9 down
PG 完全不可用,所有包含该 PG 数据的 OSD 都不在线。
三、PG 状态流转图
┌─────────────────────────────────────────────┐
│ │
OSD重启/下线 ↓ │ recovery/backfill完成
│
[active+clean] ─────────────→ [active+degraded] │
│ │ │
│ 发现数据校验错误 │ OSD重新上线 │
↓ ↓ │
[active+clean] [active+recovering] ────────────────┘
(repair ing) │
│ │ 或 PG迁移到新OSD
│ repair完成 ↓
↓ [active+backfilling] ───────────────┐
[active+clean] │
│ backfill完成
↓
[active+clean]
更完整的流转(含异常路径):
creating → peering → active+clean
→ active+degraded → active+recovering → active+clean
→ active+backfilling → active+clean
→ stale → peering(Primary恢复后)
→ incomplete(严重故障,可能数据丢失)
→ down(所有副本不可用)
四、repair、recovery、backfill 深度对比
这三个操作经常被混淆,但它们解决的是完全不同层面的问题:
| 操作 | 解决什么问题 | 触发条件 | 数据来源 | 对 PG 状态要求 |
|---|---|---|---|---|
| recovery | 副本数量/版本不足(增量) | OSD 重新上线 | 其他 OSD 的副本 | PG 可以 degraded |
| backfill | 副本完全缺失(全量) | PG 迁移到新 OSD | 其他 OSD 的副本 | PG 可以 degraded |
| repair | 数据内容静默损坏 | scrub 发现校验错误 / 客户端读取校验失败 | 其他 OSD 的"好"副本 | PG 必须 clean |
4.1 recovery 详解
本质:增量数据同步
当一个 OSD 下线一段时间后重新上线,该 OSD 上的 PG 数据已经落后于其他副本。Recovery 的工作是把这段时间内缺失的对象逐一同步过来。
bash
# 查看 recovery 进度
ceph pg stat
ceph osd pool stats
# 调整 recovery 速度(加速)
ceph tell osd.* injectargs '--osd_recovery_max_active=5'
ceph tell osd.* injectargs '--osd_recovery_sleep=0'
线上常见问题:
- Recovery 速度太慢,degraded 持续时间过长,影响数据安全性
- Recovery 占用大量带宽,影响正常业务 IO
应对方法:
bash
# 提升 recovery 优先级(加速但会影响业务)
ceph tell osd.* injectargs '--osd_recovery_max_active=10'
ceph tell osd.* injectargs '--osd_recovery_sleep=0'
# 降低 recovery 影响(减少带宽占用)
ceph tell osd.* injectargs '--osd_recovery_max_active=1'
ceph tell osd.* injectargs '--osd_recovery_sleep=0.1'
4.2 backfill 详解
本质:全量数据迁移
当 PG 因为集群扩容、OSD 更换等原因被分配到一个全新的 OSD 时,该 OSD 没有该 PG 的任何数据,必须从其他副本全量推送。
Backfill 比 recovery 更重,因为:
- 数据量更大(全量 vs 增量)
- 占用带宽更多
- 对业务 IO 影响更显著
bash
# 查看 backfill 状态的 PG
ceph pg dump | grep backfill
# 调整 backfill 并发数
ceph tell osd.* injectargs '--osd_max_backfills=2'
# 强制某个 PG 优先 backfill
ceph pg force-backfill <pg_id>
线上常见问题:
- 集群扩容后大量 PG 同时 backfill,导致集群 IO 压力激增
- Backfill 过慢导致新 OSD 长期无法承担正常流量
应对方法:
bash
# 限制 backfill 并发数,降低对业务影响
ceph tell osd.* injectargs '--osd_max_backfills=1'
# 分批扩容,避免一次性触发大量 backfill
4.3 repair 详解
本质:用好副本覆盖坏副本,修复静默数据错误
Repair 解决的是数据内容层面的问题------磁盘坏道、比特翻转等原因导致某个 OSD 上的数据字节被悄悄改坏,但 Ceph 的副本计数和版本号看起来仍然正常。
repair 的工作流程:
- 发现某个副本的数据 checksum 与其他副本不一致
- 以"多数一致"的副本为权威(如 3 副本中有 2 个一致,则这 2 个是正确的)
- 用正确的副本数据覆盖错误的副本
为什么 repair 必须在 clean 状态下进行?
因为 repair 是一个破坏性操作(覆盖数据),必须满足:
- 所有副本都可达,才能判断谁是正确的
- 副本数量完整,"多数一致"的逻辑才有意义
- 如果在 degraded 状态下 repair,可能用一个也损坏的副本去覆盖其他副本,造成数据永久损坏
五、repair 的触发场景
场景一:客户端读取时发现校验错误
客户端读取对象
→ Primary OSD 读出数据
→ 本地 checksum 校验失败(坏道/比特翻转)
→ Ceph 标记该对象为 scrub error
→ 尝试从 Secondary OSD 获取正确数据进行 repair
→ 要求 PG 必须是 clean 状态
这就是 waiting for clean to repair 的直接来源。
场景二:定期 scrub 发现副本不一致
Ceph 会定期对 PG 执行 scrub(浅扫描)和 deep-scrub(深扫描):
bash
# scrub:检查对象元数据一致性(快,不读数据内容)
ceph pg scrub <pg_id>
# deep-scrub:读取完整数据,对比所有副本的 checksum(慢)
ceph pg deep-scrub <pg_id>
当 deep-scrub 发现某副本数据与其他副本不一致时,会自动触发 repair。
场景三:手动触发 repair
bash
# 对指定 PG 执行 repair
ceph pg repair <pg_id>
# 对整个 pool 执行 repair
ceph osd pool set <pool_name> scrub_min_interval 1
六、ops 卡在 waiting for clean to repair 的排查与解决
(整体思路就是优先把等待repair的PG恢复到clean状态: 优先做recovery和backfill)
6.1 问题背景
当执行以下命令时发现 ops 卡在 waiting for clean to repair:
bash
ceph daemon osd.<id> dump_blocked_ops
说明:
- 客户端读取了某个对象
- Primary OSD 读取时发现 checksum 错误(可能是坏道)
- Ceph 想要触发 repair,但该 PG 当前是 unclean 状态
- 操作被阻塞,等待 PG 变成 clean
6.2 排查步骤
bash
# Step 1: 确认被阻塞的 ops
ceph daemon osd.<id> dump_blocked_ops
# Step 2: 查看当前集群 PG 状态
ceph pg stat
ceph -s
# Step 3: 找到 unclean 的 PG
ceph pg dump_stuck unclean
ceph pg dump_stuck degraded
# Step 4: 确认具体 PG 的详情
ceph pg <pg_id> query
6.3 解决方法
方法一:强制该 PG 优先恢复
bash
# 如果 PG 处于 degraded+recovering 状态
ceph pg force-recovery <pg_id>
# 如果 PG 处于 degraded+backfilling 状态
ceph pg force-backfill <pg_id>
方法二:调整 OSD 参数,加速恢复
bash
# 增加最大 backfill 并发数
ceph tell osd.* injectargs '--osd_max_backfills=5'
# 减少 recovery 睡眠时间(加快处理速度)
ceph tell osd.* injectargs '--osd_recovery_sleep=0'
# 增加 recovery 并发
ceph tell osd.* injectargs '--osd_recovery_max_active=5'
方法三:等待 PG 自然恢复(集群压力不大时)
如果集群整体健康,可以等待 PG 自然完成 recovery/backfill,变成 clean 后 repair 会自动执行。
6.4 完整修复流程
发现 slow ops(waiting for clean to repair)
↓
ceph pg dump_stuck unclean → 找到阻塞的 PG
↓
ceph pg force-recovery/force-backfill <pg_id>
↓
ceph tell osd.* injectargs '--osd_max_backfills=5 --osd_recovery_sleep=0'
↓
等待该 PG 变成 active+clean
↓
repair 自动执行,slow ops 消除
↓
(可选)检查 OSD 是否有硬件问题(坏道)
七、线上常见 PG 故障场景与处理
7.1 大量 PG degraded(OSD 宕机)
现象:
bash
ceph -s
# health: HEALTH_WARN
# xx pgs degraded
处理:
bash
# 查看哪个 OSD 挂了
ceph osd tree | grep down
# 等待 OSD 自动恢复,或手动重启
systemctl restart ceph-osd@<id>
# 如果 OSD 确认无法恢复,标记 out 并触发恢复
ceph osd out <id>
7.2 PG stuck unclean(长时间不恢复)
现象: PG 长时间处于 degraded 或 recovering,没有进展
处理:
bash
# 查看卡住的 PG
ceph pg dump_stuck
# 加速恢复
ceph tell osd.* injectargs '--osd_max_backfills=5'
ceph tell osd.* injectargs '--osd_recovery_sleep=0'
# 强制恢复特定 PG
ceph pg force-recovery <pg_id>
7.3 PG incomplete(危险!可能数据丢失)
现象:
bash
ceph pg dump | grep incomplete
处理(谨慎!):
bash
# 查看 PG 详细信息
ceph pg <pg_id> query
# 极端情况下,如确认数据已丢失,可标记 pg 为 complete(危险操作)
ceph pg <pg_id> mark_unfound_lost revert
⚠️ 这个操作会承认数据丢失,谨慎使用。
7.4 scrub error(数据校验失败)
现象:
bash
ceph -s
# health: HEALTH_ERR
# xx scrub errors
处理: (对校验失败的PG做repair)
bash
# 查看有 scrub error 的 PG
ceph pg dump | grep inconsistent
# 对指定 PG 执行 repair
ceph pg repair <pg_id>
# 同时排查对应 OSD 的磁盘健康
smartctl -a /dev/sdX
7.5 集群扩容后 backfill 影响业务
现象: 扩容后集群 IO 压力激增,业务延迟上升
处理:
bash
# 限制 backfill 并发,降低影响
ceph tell osd.* injectargs '--osd_max_backfills=1'
ceph tell osd.* injectargs '--osd_backfill_scan_min=4 --osd_backfill_scan_max=8'
# 在业务低峰期再放开
ceph tell osd.* injectargs '--osd_max_backfills=4'
八、核心概念总结
| 概念 | 一句话理解 |
|---|---|
| clean | 副本数量完整且版本一致,但不保证数据内容正确 |
| degraded | 副本数量不足,数据有丢失风险,仍可读写 |
| recovering | 正在从其他副本同步增量数据,补齐副本 |
| backfilling | 正在向新 OSD 全量迁移数据 |
| repair | 用好副本覆盖坏副本,修复静默数据损坏(必须在 clean 状态下) |
| scrub | 定期检查副本元数据一致性 |
| deep-scrub | 定期读取完整数据,对比所有副本 checksum |
九、一个完整的故障案例
现象: 收到 slow ops 告警
bash
# 查看被阻塞的操作
ceph daemon osd.5 dump_blocked_ops
# 发现: waiting for clean to repair
根因:
- OSD.5 上某 PG 在读取对象时发现本地 checksum 错误(磁盘坏道)
- Ceph 准备从其他副本 repair,但此时该 PG 是
active+degraded(另一个 OSD 刚重启) - repair 等待 PG 变成 clean,而 PG 恢复进度排在队列后面 → 无限等待
修复步骤:
bash
# 1. 找到阻塞的 PG
ceph pg dump_stuck unclean
# 2. 强制该 PG 优先恢复
ceph pg force-recovery 2.1f
# 3. 加速 OSD 恢复
ceph tell osd.* injectargs '--osd_max_backfills=5 --osd_recovery_sleep=0'
# 4. 等待 PG 变成 active+clean(监控)
watch ceph pg 2.1f query
# 5. PG 变 clean 后,repair 自动触发,slow ops 消失
# 6. 检查触发问题的 OSD 磁盘健康
smartctl -a /dev/sdf
# → 发现坏道,安排 OSD 替换