第 19/20 天
引言:生产环境中最考验功力的时刻
在前 18 天的系列中,我们从架构选型一路走到高可用容灾设计,构建了一个功能完整、具备跨机房容灾能力的 Ceph 生产集群。然而,分布式存储系统有一个永恒的真理:故障不是会不会发生的问题,而是何时发生、以什么形式发生的问题。
磁盘会坏、网络会抖、内存会产生比特翻转、运维会误操作。当凌晨三点告警响起——HEALTH_ERR、PG degraded、OSD down——你能否在 30 分钟内定位根因、在数据丢失风险最小的前提下完成恢复?这恰恰是 Ceph 生产环境运维中最考验功力的环节。
本篇将系统讲解 Ceph 在 Ubuntu 24.04 + Squid v19 环境下的故障排查方法论与数据恢复实战:
- PG 状态机解读:理解 active+degraded、recovery_wait、backfill 等状态的含义
- OSD 故障诊断:从心跳超时到磁盘损坏的分级处理
- Scrub/Deep-scrub 机制:静默数据损坏(Silent Data Corruption)的发现与修复
- ceph-objectstore-tool 数据抢救:OSD 无法启动时从底层对象存储导出数据
- Monitor quorum 丢失恢复:集群大脑宕机时的紧急重建

架构与原理:Ceph 故障自愈机制与状态机
一、PG 状态机:集群健康的「心电图」
Placement Group(PG)是 Ceph 数据分布和恢复的最小单位。理解 PG 状态机是排查一切数据层故障的基础。每个 PG 在任何时刻都处于一组状态组合中,最常见的状态如下:
| PG 状态 | 含义 | 是否影响客户端 IO | 紧急程度 |
|---|---|---|---|
active+clean |
正常运行,所有副本一致 | 否 | 无 |
active+degraded |
有副本缺失但主副本可用,IO 正常 | 否 | 中 |
active+recovery |
正在恢复缺失的副本 | 否 | 中 |
active+backfilling |
正在回填(全量复制到新 OSD) | 可能降低性能 | 中 |
recovery_wait |
等待恢复(受恢复限速控制) | 否 | 低 |
peering |
PG 正在建立 peer 关系,IO 暂停 | 是 | 高 |
active+undersized |
副本数不足但可用 | 否 | 中 |
activating |
peering 完成后激活中 | 是(短暂) | 高 |
down |
PG 完全不可用,主副本也丢失 | 是 | 极高 |
inconsistent |
Scrub 发现不一致,数据可能损坏 | 否 | 高 |
snaptrim |
正在清理快照 | 否 | 低 |
核心原理:当某个 OSD 故障时,Ceph 不会立即判定它永久宕机。OSD 会经历 up→down 的心跳检测(默认 20 秒无响应标记为 down),再经过 grace 宽限期(默认 5 分钟)后才标记为 out(从 CRUSH 中移除)。out 之后才会触发数据恢复(recovery/backfill),将故障 OSD 上的 PG 副本重新分布到其他 OSD。
这个设计避免了「网络抖动导致全集群数据搬迁」的灾难。理解这一延迟机制对于判断「是否真的需要干预恢复」至关重要。
二、OSD 心跳与故障检测机制
Ceph 的故障检测采用多层心跳设计:
- OSD → Monitor 心跳:每 6 秒(
osd_beacon_grace相关)向 MON 汇报存活。若 MON 在mon_osd_down_out_interval(默认 600s)内未收到某 OSD 的 beacon,将其标记为down。 - OSD ↔ OSD 心跳:每个 OSD 维护与同一 PG 中其他 OSD 的 peer 心跳。若 peer 在
osd_heartbeat_grace(默认 20s)内无响应,向上报告该 peer down。

设计考量:这种两层心跳的好处是——即使 Monitor 自身短暂故障,OSD 之间的 peer 关系仍然维持,PG 仍能继续服务 IO。只有当 peer 也确认故障时,PG 才进入 degraded 状态。
三、Scrub 与 Deep-scrub:静默数据损坏的防线
磁盘在长期运行中可能发生「静默数据损坏」——bit 翻转、固件缺陷导致数据悄无声息地变化。Ceph 通过两种 scrub 机制发现这些问题:
- Scrub(轻量校验):对比各副本的对象元数据(大小、属性、时间戳),不读取对象数据。默认每天执行一次。CPU 开销小,不影响 IO 性能。
- Deep-scrub(深度校验):逐字节读取所有副本的对象数据并计算校验和比对。默认每周执行一次。会占用磁盘 IO 和带宽,可能影响性能。
当 scrub 发现副本间数据不一致时,PG 进入 inconsistent 状态。若启用了自动修复(osd_scrub_auto_repair = true,Squid 默认开启),Ceph 会自动用多数派副本修复少数派。若主副本本身损坏且无法自动判定正确版本,则需人工介入。
四、ceph-objectstore-tool:最后一道数据防线
当 OSD 因磁盘损坏、BlueStore 元数据损坏等原因完全无法启动时,常规的 ceph osd repair 命令无用——因为 OSD 进程根本起不来。此时 ceph-objectstore-tool(COST)是抢救数据的唯一手段。
其原理是:直接挂载 OSD 的底层 BlueStore 数据目录(以只读模式),绕过 OSD 守护进程,直接从对象存储层导出 PG 数据。然后可以将这些 PG 数据导入到新的 OSD 或临时存储中恢复。
部署实战:故障排查与数据恢复全流程
实战一:集群健康检查与 PG 状态诊断
当收到告警时,第一步永远是全面评估集群状态,切忌盲目操作。
# 1. 集群整体健康摘要
ceph status
# 输出示例(故障态):
# cluster:
# health: HEALTH_ERR
# 1 osds down
# 1 host (1 osds) down
# Degraded data redundancy: 128/384 pgs degraded
# 2. 查看详细健康告警(Squid 新特性,结构化告警)
ceph health detail
# 会列出每个具体告警的 ID、严重级别、关联 OSD/PG
# 3. 查看所有非 active+clean 的 PG
ceph pg dump_stuck | grep -v "active+clean"
# 或使用过滤版
ceph pg ls –format json | python3 -m json.tool | grep state
# 4. 查看特定 PG 的详细状态与历史
ceph pg <pgid> query
# 例如:ceph pg 2.1f query
# 输出包含:PG 状态、OSD 映射、peering 历史、recovery 进度
关键诊断逻辑:如果 PG 处于 active+degraded 且对应 OSD 状态为 down,说明是 OSD 故障导致的副本缺失,等待恢复即可;如果 PG 处于 peering 长时间不动,则可能是 MON 问题或 CRUSH Map 变更导致的重映射卡死。
实战二:OSD 故障诊断与分级处理
# 1. 查看 OSD 树状拓扑,定位故障节点
ceph osd tree | grep -E "down|out"
# 输出示例:
# host ceph-node-03 {
# osd.12 down 1.00000 1.00000 hdd 0.01785
# osd.15 down 1.00000 1.00000 hdd 0.01785
# 2. 查看 OSD 的详细信息和最近状态变化
ceph osd find 12
# 输出 OSD 的 IP、CRUSH 位置、状态标志
# 3. 查看 OSD 服务日志(判断是网络问题还是磁盘问题)
# 先确认 OSD 容器日志位置
cephadm ls | jq -r '.[] | select(.name=="osd.12") | .systemd_unit'
# 然后查看对应 journalctl 或 /var/log/ceph
# 4. 检查 OSD 所在磁盘健康状态(物理层诊断)
ssh ceph-node-03
# 查看 BlueStore 使用的块设备
ceph-volume inventory –format json
# 使用 smartctl 检查 SMART 状态
smartctl -a /dev/sdb | grep -E "Reallocated|Pending|Uncorrect|Health"
# 关键指标:
# Reallocated_Sector_Ct > 0 → 已有坏块,准备更换
# Current_Pending_Sector > 0 → 即将坏块
# Offline_Uncorrectable > 0 → 不可恢复错误,磁盘必须更换
OSD 故障的分级处理决策:
- 网络抖动(OSD 短暂 down 后自动 up):无需干预,检查交换机端口和网络质量即可。
- 磁盘 SMART 预警但未完全损坏:规划维护窗口,
ceph osd set noout防止触发恢复,然后更换磁盘。 - 磁盘完全损坏:标记
ceph osd destroy,更换磁盘后ceph-volume重建,等待 backfill 完成。 - BlueStore WAL/DB 损坏但数据盘完好:尝试重建 RocksDB,或使用 COST 导出数据。
实战三:Scrub 不一致与自动修复配置
当 PG 出现 inconsistent 状态时,需要理解 scrub 机制并安全地触发修复。
# 1. 查看集群 scrub 配置
ceph config dump | grep scrub
# 关键参数:
# osd_scrub_auto_repair = true (Squid 默认开启)
# osd_deep_scrub_interval = 604800 (7天,单位秒)
# osd_scrub_sleep = 0 (scrub 间隔,0=不限速,生产建议 0.05-0.1)
# 2. 手动触发单个 PG 的 deep-scrub
ceph pg deep-scrub 2.1f
# 观察状态变化
watch -n 2 'ceph pg ls | grep 2.1f'
# 3. 若自动修复未生效,手动触发 PG 修复
ceph pg repair 2.1f
# 注意:repair 会用 primary 副本覆盖其他副本
# 仅在确认 primary 副本正确时使用!
# 4. 生产环境 scrub 调度配置(避免影响业务高峰)
# 在 ceph.conf 或通过 ceph config set 配置:
ceph config set osd osd_scrub_begin_hour 1
ceph config set osd osd_scrub_end_hour 6
# 限制 scrub 仅在凌晨 1-6 点执行
ceph config set osd osd_deep_scrub_interval 604800
# deep-scrub 间隔 7 天
# 5. 查看 scrub 进度和队列
ceph osd scrub status
# 或(旧版):
ceph pg dump | grep scrub
实战四:ceph-objectstore-tool 数据抢救实战
这是最关键的数据恢复场景——当 OSD 因 BlueStore 损坏完全无法启动时,如何抢救其上的数据。
# 场景:osd.12 的 BlueStore DB 分区损坏,OSD 无法启动
# 目标:将 osd.12 上的 PG 数据导出到新 OSD
# 步骤 1:将故障 OSD 标记为 destroyed(阻止集群重建)
ceph osd set destroy osd.12
ceph osd set noout # 暂时禁止恢复,避免数据搬迁
# 步骤 2:停止故障 OSD 守护进程
cephadm ls | jq -r '.[] | select(.name=="osd.12") | .systemd_unit'
systemctl stop ceph-<fsid>@osd.12.service
# 步骤 3:进入 OSD 容器或直接使用 ceph-objectstore-tool
# 获取集群 FSID
FSID=$(ceph fsid)
# 步骤 4:以只读模式挂载故障 OSD 的 BlueStore 数据
# 先查看该 OSD 的磁盘路径
lsblk -o NAME,MOUNTPOINT,SIZE /dev/disk/by-partuuid/$(ceph-bluestore-tool show-label /dev/sdb | jq -r .osd_uuid)
# 假设数据在 /dev/sdb3 (block 分区), DB 在 /dev/sdb1
# 步骤 5:列出该 OSD 上的所有 PG
ceph-objectstore-tool –data-path /var/lib/ceph/osd/ceph-12
–op list-pgs
# 输出示例:2.1f, 2.2a, 3.0c, …
# 步骤 6:逐个导出 PG 数据到临时文件
ceph-objectstore-tool –data-path /var/lib/ceph/osd/ceph-12
–pgid 2.1f –op export –file /tmp/pg_2.1f.export
# 步骤 7:将导出的 PG 导入到新的 OSD(需先创建空 OSD)
# 先用新磁盘创建 OSD
ceph-volume lvm create –data /dev/nvme0n1
# 获取新 OSD 编号,假设为 osd.20
NEW_OSD=20
ceph-objectstore-tool –data-path /var/lib/ceph/osd/ceph-${NEW_OSD}
–pgid 2.1f –op import –file /tmp/pg_2.1f.export
# 步骤 8:清除 noout 并允许集群恢复
ceph osd unset noout
# 观察恢复进度
ceph -w
# 关键配置参数说明(ceph.conf / ceph config set)
# — 故障检测与恢复 —
osd_heartbeat_grace: 20 # OSD peer 心跳超时(秒),生产可调到 15
mon_osd_down_out_interval: 600 # OSD down 后多久标记 out(秒),生产建议 900-1200
osd_max_markdown: 300 # OSD 标记 down 后最多等多久重新 up
# — 恢复限速(防止恢复 IO 挤占业务 IO) —
osd_recovery_max_active: 3 # 每个 OSD 同时恢复的 PG 数,生产建议 1-3
osd_recovery_op_priority: 1 # 恢复操作优先级(1-63),生产建议 1-2
osd_client_op_priority: 63 # 客户端操作优先级,保持高值
osd_recovery_max_single_start: 1 # 同时启动恢复的 PG 数
osd_max_backfills: 1 # 每个 OSD 同时 backfill 数,HDD 建议 1,SSD 可 2-3
# — Scrub 配置 —
osd_scrub_auto_repair: true # 自动修复不一致(Squid 默认 true)
osd_deep_scrub_interval: 604800 # deep-scrub 间隔(秒),7天
osd_scrub_begin_hour: 1 # scrub 允许开始时间
osd_scrub_end_hour: 6 # scrub 允许结束时间
osd_scrub_sleep: 0.05 # scrub 间隔秒数,HDD 建议 0.05-0.1
实战五:Monitor Quorum 丢失的紧急恢复
当超过半数 Monitor 同时宕机时,集群将失去 quorum,所有 ceph 命令都会超时。这是最紧急的故障场景。
# 1. 确认 quorum 丢失(所有 ceph 命令超时或返回错误)
timeout 10 ceph -s
# 若超时,说明已失去 quorum
# 2. 找到存活 Monitor(有最新 monmap 的那个)
# 进入每个 MON 节点检查
for mon in ceph-mon-01 ceph-mon-02 ceph-mon-03; do
echo "=== $mon ==="
ssh $mon "ceph daemon mon.$(hostname) mon_status 2>/dev/null ||
cat /var/lib/ceph/mon/ceph-*/kv_backend 2>/dev/null ||
ls -lt /var/lib/ceph/mon/ceph-*/store.db/ 2>/dev/null | head"
done
# 3. 找到有最新数据的 MON 后,强制生成单节点 quorum
# 在存活 MON 节点上执行(需 root 权限)
# 假设 ceph-mon-01 有最新数据
ssh ceph-mon-01
# 生成一个只包含当前 MON 的 monmap
ceph-mon -i mon.ceph-mon-01 –extract-monmap /tmp/monmap –mon-data /var/lib/ceph/mon/ceph-ceph-mon-01
# 此命令会从本地 MON 数据库提取 monmap
# 4. 修改 monmap,移除已宕机的 MON
monmaptool –print /tmp/monmap
# 仅保留当前 MON
monmaptool –rm mon.ceph-mon-02 /tmp/monmap
monmaptool –rm mon.ceph-mon-03 /tmp/monmap
# 确认只剩一个
monmaptool –print /tmp/monmap
# 5. 将修改后的 monmap 注入 MON
ceph-mon -i mon.ceph-mon-01 –inject-monmap /tmp/monmap –mon-data /var/lib/ceph/mon/ceph-ceph-mon-01
# 重启该 MON 服务
systemctl restart ceph-$(ceph fsid)@mon.ceph-mon-01.service
# 6. 验证 quorum 恢复
ceph -s
# 此时应有 1 个 MON,集群恢复服务
# 7. 重新添加其他 MON(在故障修复后)
ceph mon add mon.ceph-mon-02 10.0.1.12
ceph mon add mon.ceph-mon-03 10.0.1.13
# 等待 monmap 同步并组建 3 节点 quorum
ceph quorum_status
# 辅助脚本:自动化 PG 状态监控与告警(保存为 pg_monitor.py)
# 使用 Python 调用 ceph CLI 获取 PG 状态并判断告警级别
import subprocess, json, smtplib
from email.mime.text import MIMEText
def get_pg_status():
"""获取集群 PG 状态摘要"""
result = subprocess.run(
['ceph', 'pg', 'dump', 'summary', '–format', 'json'],
capture_output=True, text=True, timeout=30
)
data = json.loads(result.stdout)
return data
def check_health():
"""检查集群健康并返回告警"""
result = subprocess.run(
['ceph', 'health', 'detail', '–format', 'json'],
capture_output=True, text=True, timeout=30
)
health = json.loads(result.stdout)
alerts = []
for check in health.get('checks', {}).values():
severity = check.get('severity', '') # ERROR / WARN
summary = check.get('summary', {}).get('message', '')
if severity == 'ERROR':
alerts.append(f"[CRITICAL] {summary}")
elif severity == 'WARN':
alerts.append(f"[WARNING] {summary}")
return alerts
def send_alert(alerts):
"""发送邮件告警"""
if not alerts:
return
body = "n".join(alerts)
msg = MIMEText(f"Ceph 集群告警:nn{body}")
msg['Subject'] = f"[Ceph Alert] {len(alerts)} issues detected"
msg['From'] = 'ceph-monitor@stellardata.top'
msg['To'] = 'ops-team@stellardata.top'
with smtplib.SMTP('smtp.stellardata.top', 25) as s:
s.send_message(msg)
print(f"Alert sent: {len(alerts)} issues")
if __name__ == '__main__':
pg = get_pg_status()
print(f"Total PGs: {pg.get('num_pgs', 0)}")
alerts = check_health()
send_alert(alerts)
生产环境注意事项与踩坑提示
1. 恢复限速是最重要的安全阀
踩坑:很多运维在 OSD 故障后立即将 osd_max_backfills 调到 5 甚至 10,希望「快速恢复」。结果恢复 IO 挤占了所有磁盘带宽,业务延迟飙到秒级,引发连锁雪崩。
正确做法:HDD 环境 osd_max_backfills = 1、osd_recovery_max_active = 1,宁可慢但稳。只有全 NVMe 集群才可适当调高到 3。恢复速度应受 osd_recovery_op_priority(默认 1,最低)控制,让客户端 IO 始终优先。
2. noout 标志的「双刃剑」
更换磁盘前先 ceph osd set noout 是正确操作,但务必在更换完成后立即 ceph osd unset noout。曾发生过运维设置 noout 后忘记解除,导致后续多个 OSD 故障时集群不触发恢复,最终副本数归零、数据丢失的真实事故。
建议:设置 noout 的同时设置一个日历提醒,或在监控系统中增加 noout 持续 > 2 小时 的告警。
3. ceph-objectstore-tool 操作的风险
COST 是直接操作底层 BlueStore 的工具,任何写入操作都有风险。关键注意事项:
- 始终先用
--op list-pgs只读操作确认数据可读,再做导出。 - 导出时使用
--op export(只读导出),避免--op import覆盖正常 OSD 数据。 - 绝对不要对正在运行的 OSD 执行 COST 写入操作——会导致内存中缓存数据与磁盘不一致,引发严重损坏。
- 操作前
ceph osd set noout和norecover,防止集群在操作期间触发搬迁。
4. Scrub 排程与业务高峰冲突
Deep-scrub 会全量读取磁盘数据,在大容量 HDD 集群上可能持续数小时。若不限制执行时间窗口,可能在业务高峰期导致延迟飙升。务必配置 osd_scrub_begin_hour/osd_scrub_end_hour 限制在低峰时段。同时,Squid 引入了 scrub 资源调度优化,会根据 OSD 负载自动延迟 scrub,但仍建议人工设定时间窗口作为兜底。
5. PG 卡在 peering 的排查
当大量 PG 长时间停留在 peering 状态时,常见原因:
- MON 不一致:多个 MON 的 monmap 或 OSD 元数据不同步。检查
ceph mon dump和ceph quorum_status。 - CRUSH Map 变更未完成:
ceph osd crush reweight或手动编辑 CRUSH 后未等待 PG 重新 peering 完成。 - past_intervals 过多:OSD 经历了频繁的 up/down 变化,积累了大量历史 interval,peering 需要逐一验证。可检查
ceph pg <pgid> query中的past_intervals字段,必要时通过调整osd_peering_delay或重启相关 OSD 触发重新 peering。
常见问题
Q1:OSD down 了但没自动 out,数据会丢吗?
不会。OSD down 后仅标记为不可用,数据副本仍然在其他 OSD 上。只有经过 mon_osd_down_out_interval(默认 600 秒,生产建议调到 900-1200 秒)后才会标记 out 并触发恢复。这个延迟是为了避免网络抖动导致不必要的全集群数据搬迁。在此期间 PG 处于 active+degraded 状态,客户端 IO 不受影响(只要还有其他副本可用)。
Q2:deep-scrub 发现 inconsistent,自动修复安全吗?
Squid 版本默认开启 osd_scrub_auto_repair,机制是:以 primary 副本为基准,用多数派数据覆盖少数派。风险点在于:如果恰好是 primary 副本本身损坏(概率很低但存在),自动修复会用错误数据覆盖正确副本。生产环境建议同时启用 ms_async 校验和与硬件级 T10 DIF(DIX)端到端数据完整性保护,从传输层和硬件层双重防范静默损坏。对于关键存储池,可在 inconsistent 出现后先 ceph pg query 查看 objects inconsistent 的具体对象,人工判断后再决定 repair 还是 manual override。
Q3:Monitor quorum 丢失后用 monmaptool 强制单节点,数据会丢吗?
不会丢失数据。Monitor 只存储集群元数据(monmap、OSD map、PG map 等),不存储任何用户数据。强制单节点 quorum 的操作只影响 MON 的投票拓扑,不影响 OSD 上的数据。恢复 quorum 后 OSD 读取最新 monmap 即可正常工作。但风险在于:如果选择的 MON 不是数据最新的那个,可能丢失最近几分钟的元数据变更(如刚创建的 pool、刚添加的 OSD),需要重新执行这些操作。务必通过比较各 MON 的 store.db(RocksDB)大小和修改时间,选择最新的那个。
总结
Ceph 故障排查与数据恢复是整个生产环境运维体系中最核心的应急能力。本篇系统讲解了:
- PG 状态机:从 active+clean 到 down 的完整状态图谱,以及每种状态的含义与紧急程度
- OSD 故障分级:从网络抖动到磁盘 SMART 预警到完全损坏的三级处理策略
- Scrub 机制:轻量校验与深度校验的配合,静默数据损坏的发现与自动修复
- ceph-objectstore-tool:OSD 无法启动时的最后一道数据抢救防线
- Monitor quorum 恢复:集群大脑宕机时的紧急单节点重建流程
核心原则是:先评估再行动,能等就别急,恢复限速是安全阀。 90% 的数据丢失事故不是因为初始故障,而是因为慌乱中的误操作——把 degraded 搞成 data_loss,把单 OSD 故障搞成多 OSD 雪崩。冷静、有序、有据可查的操作才是数据恢复的真正保障。
下期预告
第 20 天:Ceph 生产环境升级维护与版本迭代策略 —— 从 Reef 到 Squid 的滚动升级、cephadm upgrade 编排器的使用、OSD兼容性矩阵、回滚策略,以及版本迭代的长期维护规划。这是本系列的收官篇,我们将把从架构选型到故障恢复的全流程串联成完整的运维生命周期。
系列目录
- ✅ 第 1 天:Ceph 架构概述与生产环境选型指南
- ✅ 第 2 天:Ubuntu 系统准备:内核调优与磁盘规划
- ✅ 第 3 天:Cephadm 部署工具详解与集群引导初始化
- ✅ 第 4 天:Monitor 节点部署与高可用配置
- ✅ 第 5 天:Manager 节点部署与模块启用
- ✅ 第 6 天:OSD 存储节点部署:BlueStore 配置与磁盘管理
- ✅ 第 7 天:CRUSH Map 架构解析与故障域规划
- ✅ 第 8 天:存储池(Pool)创建与 PG 数量规划
- ✅ 第 9 天:副本与纠删码存储池策略对比实战
- ✅ 第 10 天:Ceph 块存储 RBD 配置与生产实践
- ✅ 第 11 天:Ceph 对象存储 RGW 网关部署与 S3 兼容
- ✅ 第 12 天:CephFS 分布式文件系统部署与挂载
- ✅ 第 13 天:Ceph 网络架构:集群网络与公共网络分离
- ✅ 第 14 天:Ceph 认证体系 cephx 与用户权限管理
- ✅ 第 15 天:Ceph 集群监控:Prometheus + Grafana + 内置仪表盘
- ✅ 第 16 天:Ceph 性能调优:OSD 参数与缓存分层
- ✅ 第 17 天:Ceph 集群扩容实战:OSD 动态添加与 CRUSH 重平衡
- ✅ 第 18 天:Ceph 高可用与容灾设计:多副本、跨机房与异地灾备
- 📌 第 19 天:Ceph 故障排查与数据恢复实战(本文)
- 🔜 第 20 天:Ceph 生产环境升级维护与版本迭代策略(即将发布)

















暂无评论内容