Ceph PG 状态详解与线上故障处理

本文基于生产环境中排查 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 的含义:

  1. 副本数量 = 配置要求(如 3 副本策略下,3 个副本都存在)
  2. 所有副本数据版本一致,没有任何落后的副本
  3. Peering 已完成,Primary OSD 知道所有副本的状态
  4. 没有正在进行的 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 的工作流程:

  1. 发现某个副本的数据 checksum 与其他副本不一致
  2. 以"多数一致"的副本为权威(如 3 副本中有 2 个一致,则这 2 个是正确的)
  3. 用正确的副本数据覆盖错误的副本

为什么 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

说明:

  1. 客户端读取了某个对象
  2. Primary OSD 读取时发现 checksum 错误(可能是坏道)
  3. Ceph 想要触发 repair,但该 PG 当前是 unclean 状态
  4. 操作被阻塞,等待 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

根因:

  1. OSD.5 上某 PG 在读取对象时发现本地 checksum 错误(磁盘坏道)
  2. Ceph 准备从其他副本 repair,但此时该 PG 是 active+degraded(另一个 OSD 刚重启)
  3. 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 替换

相关推荐
不是书本的小明2 小时前
K8S应用优化方向
网络·容器·kubernetes
~plus~3 小时前
.NET 8 C# 委托与事件实战教程
网络·c#·.net·.net 8·委托与事件·c#进阶
w6100104664 小时前
CKA-2026-Service
linux·服务器·网络·service·cka
GTgiantech4 小时前
灵活拓展网络边界:电口光模块的智慧选型与部署指南
网络
测试专家4 小时前
天脉3操作系统
网络
JS_SWKJ4 小时前
网闸升级、备份、恢复标准化操作全指南
网络
王燕龙(大卫)5 小时前
tcp报文什么时候会真正发送
服务器·网络·tcp/ip
勿忘,瞬间5 小时前
网络编程套接字
运维·服务器·网络
@insist1235 小时前
网络工程师-网络安全基础体系:软考核心考点与合规框架全解析
网络·网络工程师·软考·软件水平考试
zhanghongbin015 小时前
本地持久化:网络故障数据保护
服务器·网络·php