Ceph 集群运维实战:常见命令、故障诊断与性能优化
命令表最大的问题不是太长,而是不告诉你哪一条会后悔。这篇把 Ceph 命令分三类,只留真正改变判断的十几条,并说清哪些动作是不可逆的。
我刚接手 Ceph 的时候收集过一份命令清单,一百多条,按"日常/管理/诊断/优化"分了四类。用了半年之后发现一个尴尬的事实:清单里真正在关键时刻救过我的不超过十几条,而其中三条曾经差点把事情弄得更糟。
问题不在于清单太长,在于它没有标注哪一条是"问",哪一条是"动"。
#一、把命令分成三类,比背下来更重要
按"它能改变什么"来分,Ceph 的命令其实只有三类:
| 类别 | 特征 | 例子 | 什么时候用 |
|---|---|---|---|
| 读 | 只看状态,不动集群 | ceph -s、ceph osd df tree、ceph osd perf、ceph pg dump_stuck | 任何时候,越多越好 |
| 问 | 会触发一次计算或探测,但不改变数据分布 | ceph osd map、ceph pg <pgid> query、ceph health detail | 定位问题时 |
| 动 | 改变集群状态,部分不可逆 | ceph osd out、ceph osd crush remove、ceph osd destroy、ceph pg repair、ceph osd lost | 说清楚代价之后 |
这张表是我认为 Ceph 运维里最该先建立的东西——因为它把"我记得这条命令"和"我知道这条命令的代价"分开了。第二件事才是值钱的。
#二、先学会读懂 ceph -s 里的沉默
ceph -s 是绝大多数人第一条学会的命令,也是最容易被读得太快的命令。它有两种读法:
ceph -s
# cluster:
# id: <uuid>
# health: HEALTH_WARN
# 1 pool(s) have non-power-of-two pg_num
# services:
# mon: 3 daemons, quorum a,b,c (age 4d)
# mgr: x(active, since 3d), standbys: y
# osd: 12 osds: 12 up (since 2d), 12 in (since 2d)
# data:
# pools: 3 pools, 289 pgs
# objects: 1.20M objects, 4.1 TiB
# usage: 5.6 TiB used, 12 TiB / 18 TiB avail
# pgs: 289 active+clean第二行里的 since 2d / since 3d 是最容易被忽略、也最有信息量的部分。它记录的是"最后一次状态变化距今多久",等价于一个免费的变更审计:
osd: 12 osds: 12 up (since 2d)—— 如果这个数字比你上次巡检时小,说明两天前有 OSD 掉过又回来了,而你可能完全没收到通知。mon: 3 daemons, quorum a,b,c (age 4d)—— age 突然变小,说明 Monitor 发生过重新选主。pgs:全是active+clean时没有信息量;一旦不是,这一行就是优先级最高的信息。
我现在的习惯是把 ceph -s 的输出连同时间戳定期落盘,用来看"变过什么",而不是"现在是什么"。能看出沉默的那部分,才算读懂了这条命令。
#三、第二层:health detail 与 osd df tree
ceph health detail
ceph osd df tree
ceph osd perfhealth detail给的是可执行的提示(哪条规则触发、涉及哪个池/OSD)。HEALTH_WARN有好几十种,靠记没用,看 detail。osd df tree一次回答三件事:容量分布是否均衡、每块 OSD 的%USE是否接近、PG 数是否均衡。注意VAR一列——容量和 PG 数的方差不均衡时,修法完全不同(crush weightvspg_num/autoscaler)。osd perf给每块 OSD 的 apply/commit 延迟。集群"整体变慢但-s正常"时,先看这里再谈调优。
#四、"动"类命令:把可逆性标清楚
这一节是这篇真正的重点。下面每一步的"可逆性"我按自己的理解标注,动手前请以你的版本文档复核:
| 命令 | 做了什么 | 可逆吗 | 代价 |
|---|---|---|---|
ceph osd out <id> | 让数据从这块 OSD 迁出 | 可逆(osd in) | 触发一次完整回填,集群负载显著上升 |
ceph osd crush remove osd.<id> | 从拓扑里摘掉 | 可重建,但要重算 | 若在 out 未完成时执行,会连同未迁完的数据一起失去 |
ceph osd destroy <id> | 销毁该 OSD 的元数据/身份 | 不可逆 | 该 OSD 上的数据被视为不存在 |
ceph osd purge <id> | 一步做完 out+crush remove+auth 清理 | 不可逆 | 顺序错了就没有回头路 |
ceph pg repair <pgid> | 以 primary 为准修复副本 | 不可逆地覆盖 | 如果 primary 本身是坏的那一份,会把坏数据复制到所有副本 |
ceph osd lost <id> --yes-i-really-mean-it | 声明某 OSD 的数据永久丢失 | 不可逆 | 之后 PG 会把缺失副本当作"没有"来重建 |
要特别说 pg repair:它在"某个副本明显损坏、且你确信 primary 是好的"时是正确工具;在你不知道哪一份是对的的时候用它,等于随机选一份覆盖其余所有副本。这类命令的共同特征是——它们不会问你确定不确定,只问你打不打那个参数。
一个可操作的习惯:把上面这些命令包一层再放进脚本,让执行前必须显式确认:
osd_remove() {
local id="$1"
echo "即将执行:out -> 等待回填 -> crush remove -> destroy"
echo "当前该 OSD 上的 PG 数:$(ceph pg ls-by-osd osd.$id 2>/dev/null | wc -l)"
read -r -p "确认继续?(yes) " ok
[ "$ok" = yes ] || return 1
ceph osd out "$id"
while ceph -s | grep -q "recovery"; do sleep 30; done # 等迁完,不要跳步
ceph osd crush remove "osd.$id"
}那个 while 循环看起来笨,但它是把"我以为迁完了"换成"命令告诉我迁完了"。顺序错一步的代价通常是数据,不是时间。
#五、故障诊断:按层走,不要跳
Ceph 的怪处在于:几乎所有的"存储问题"最后都能追到非存储的地方——网络、时钟、内存、甚至 BIOS 的电源策略。所以诊断路径比命令本身重要:
# 1) 先分层:是数据面还是控制面?
ceph -s; ceph health detail
# 2) 如果是 OSD 相关,先看单块盘
ceph osd tree # 有没有 down/out
ceph osd perf # 是不是某几块特别慢
ceph osd metadata <id> | grep -i host # 慢的几块是否在同一台机器
# 3) 同一台机器上多块盘同时慢 → 往上走一层
# (网卡错包、CPU 抢占、内存不足、NUMA 绑核、RAID 卡缓存策略)
# 4) 控制面:Monitor 的时钟与网络
ceph mon stat
ceph daemon mon.<name> mon_status | head -20经验上最高频的三个根因是:跨机架/跨机房带宽被打满、单机上的中断集中在少数 CPU 核、以及"以为对时是自动的"(Monitor 对时间敏感,clock skew 相关的告警很多人第一反应是加硬件,其实先核 NTP)。
#六、恢复期间的那些参数,是在选立场
ceph config get osd osd_max_backfills
ceph config get osd osd_recovery_max_active
ceph config get osd osd_recovery_sleep
ceph config get osd osd_client_op_priority
ceph config get osd osd_recovery_op_priority把恢复调快,前台业务的尾延迟就变差;调慢,集群处于冗余不足状态的时间就变长。这不是"调优",是分配:你决定把这段窗口的不适感分给用户还是分给风险。
一句实话:这件事没有统一答案,但有统一的做法——把当前取值和你为什么这么取写进运维文档。半年后的你(或者接手的同事)需要知道这个数字是"某次故障时临时调的",而不是"一直这样"。
#七、演练:没跑过的预案等于没有预案
预案这种东西,读起来都懂,执行起来都会卡。我建议至少把下面三个动作在非关键池上真跑一遍:
- 一次
osd out到回填完成的完整流程——量出"多久迁完",这个数字直接决定你的恢复窗口预算。 - 一次 Monitor 重启——确认重启后
mon_status恢复、客户端不断连太久。 - 一次"盘坏了"的模拟——
osd destroy一块可丢弃的 OSD,再把它重新加回来,走一遍ceph-volume的路径。
这三个动作的价值不在于"到时候会做",而在于你会发现一些文档里没写、但一定会卡住你的东西(认证残留、crush 权重没重置、systemd unit 名字对不上,诸如此类)。
#八、这篇没写什么
- 没写完整命令表。 那类清单更适合当速查卡,而不是文章——而且它对版本的敏感性很高。这里只留下"改变判断"的那十几条。
- 没写容量规划与硬件选型。 那是另一套前提(盘型、网络、副本策略),脱离它们给数字是误导。
- 没写监控系统怎么搭。 Prometheus + ceph_exporter 是可选项之一,但"看什么指标"比"用什么采集"重要得多,值得单独一篇。
- 没给参数推荐值。 上面说过理由:没有负载画像的推荐值,是把试错成本转嫁给读者。
如果只从这篇带走一句话:把"我知道这条命令"升级成"我知道这条命令不可逆"——Ceph 的绝大多数事故不是不懂原理,而是在不可逆的那一步没有停两秒。
写于 2025 年 11 月 5 日
- 栏目
- 技术文章
- 约
- 7.1 分钟
- 字数
- 4.2K
- 阅读
- 2
本文为原创记录,转载请注明出处。如果这篇替你省了时间,欢迎留言说说你踩到的坑。
同题 · related
- 把博客从 docker-compose 搬到 k3s:单机迁移与上线全过程在 4C3.6G 的云主机上把博客从 docker-compose 迁到单机 k3s。附完整 YAML 清单(Deployment、Service、PVC、Ingress、Certificate 等)与全部配置命令,记录部署、数据迁移与切换上线的踩坑过程。
- DevOps 运维中 AI 那些事把 AI 放进运维的真实分工:从服务器硬件、网络、GPU、内存,到虚拟化、Kubernetes、应用构建、CI/CD、可观测性、资源分配,再到 AI 自身的部署与算力调度。讲清楚哪些活它能接、边界划在哪、以及我踩过的那些坑。
- Linux systemd 服务管理:从编写 unit 到故障排查使用 systemd 管理自定义服务,覆盖 unit 文件、启动策略、日志查看和失败恢复。
留言 · remarks
00 条还没有留言,来说点什么吧。