去年冬天处理过一次 MON 节点全挂的事故,从无从下手到一步步把集群拉回来,让我意识到一个问题:很多 Ceph 相关文档都在讲存储池、RBD、CRUSH,但专门把 MON 命令系统梳理一遍的几乎没有。而 MON 恰恰是集群的命门,仲裁一丢,整个分布式存储就变成了只读的摆设。这篇东西不是手册的翻译,是我自己在这套命令上实际摸爬滚打后的总结,覆盖状态查看、节点管理、认证修复、monmap 重建等高频场景,适合刚接手 Ceph 集群的运维,也适合被 MON 故障折腾过、想系统补课的人。
1. 先摸清家底:用 ceph mon dump 读懂集群地图与 MON 演进
接手一个陌生集群,第一步不是急着敲各种命令,而是先搞清楚 MON 的底细。ceph mon dump是我每次排查问题第一个执行的操作,没有之一。这条命令输出的实际上是 MON 的持久化状态,也就是 monmap,它记录了集群里所有 MON 节点、它们的地址、以及这个 map 的版本号。
ceph mon dump输出内容看起来不多,但信息密度很高,逐段拆开讲。最上面一般是epoch、fsid、last_changed、min_mon_release之类的元信息。epoch表示当前 monmap 的版本号,每次增删 MON、修改地址,epoch 就会加一。排查问题时要习惯性地记下这个数字,后面用ceph mon dump对比新旧版本时,epoch 就是最直接的参照物。
fsid是集群的唯一标识,备份和恢复 monmap 时需要精确匹配。如果 fsid 对不上,别想把 dump 文件恢复到另一个集群。min_mon_release是当前 MON map 支持的最低版本,比如quincy或reef,混合版本集群中要特别注意这个字段,老版本 MON 和新版本共存时可能触发协议不兼容。
中间部分是一系列 MON 节点的定义,每条记录大致长这样:
0: [v2:10.0.0.11:3300/0,v1:10.0.0.11:6789/0] mon.node10是 MON 在该 map 中的编号;v2地址是 Ceph 较新版本默认监听的协议,端口3300;v1地址是传统协议,端口6789。老版本只会有v1,如果在扩容时混用 v1 和 v2,会看到一条 MON 同时输出两种协议栈。这里有个坑,我后面会专门讲。
还要留意输出末尾的dump汇总,它会告诉你dumped monmap to /tmp/monmap之类的路径——这是ceph mon dump附带的行为,它会自动把 monmap 以二进制形式导出到本地文件。这个文件非常重要,monmaptool修改、恢复操作都依赖它,建议养成每次 dump 后顺手复制一份留档的习惯。
ceph mon dump > /tmp/monmap_dump_$(date +%F).txt这条命令虽然叫 dump,但它导出的是纯文本。如果想导出二进制 monmap 文件,用ceph mon getmap:
ceph mon getmap -o /tmp/monmap.binceph mon dump更偏向于“看”,ceph mon getmap则偏向于“拿”——拿到文件后可以做校验、恢复、离线重建。两条命令配合使用,MON 维度的问题基本都能摸到底。
2. 状态透视:ceph quorum_status 到仲裁信息的深入解读
ceph quorum_status这条命令很多人用过,但未必读懂了输出。它告诉你当前集群 MON 仲裁是否健康,以及谁在仲裁里、谁不在。
ceph quorum_status输出是 JSON 格式,关键字段包括quorum、monmap、quorum_leader_name。quorum是一个数组,里面是当前参与仲裁的 MON 编号。比如quorum: [0, 1, 2]表示三节点 MON 都健康;如果出现quorum: [0, 1]而 MON 总数是 3,说明第三个 MON 掉线或者网络隔离了。
这里有一个经常被误解的点:ceph -s里的mon: 3 daemons, quorum 0,1,2才是仲裁状态的简化视图,而ceph quorum_status是详细 JSON。两者不是替代关系,而是同一个状态的不同展示粒度。排查问题时,我习惯先看ceph -s快速确认仲裁,再看quorum_status拿细节。
关于仲裁,还有一个容易忽略的理论基础:MON 使用的是 Paxos 协议的简化版,这决定了必须超过半数 MON 在线才能形成仲裁。3 节点 MON 允许挂 1 个;5 节点允许挂 2 个;如果是 2 节点 MON 这种非标准部署,挂 1 个就等于仲裁丢了。所以部署规划时就该明白,2 节点 MON 不是高可用,是自欺欺人。
ceph quorum_status里quorum_leader_name字段标识当前谁是仲裁中的 leader。集群写入元数据时,leader 负责协调各 MON 的 Paxos 提案。如果 leader 频繁变化,往往不是好事——可能是网络抖动导致选举震荡,这种时候要去查 MON 节点之间的网络质量,而不是急着重启服务。
实际使用中,ceph quorum_status常被写进监控脚本,判断仲裁状态。一个简单的健康检查建议配合ceph mon stat,它输出更紧凑:
ceph mon statceph mon stat会直接给出类似e3: 3 mons at {0=...}的信息,e3就是当前 epoch。如果脚本里同时跑ceph quorum_status和ceph mon stat,一个拿详细状态,一个拿快速摘要,排查效率会高很多。
还有一个方向容易被忽略:ceph quorum_status也能用于验证故障域配置。比如你把 MON 分布在 3 个不同机架,某次机架断电后,quorum_status里缺失的节点编号会告诉你哪个机架的 MON 挂了。这比直接 ping IP 更准确,因为即便进程活着,Paxos 通信断开也可能导致节点被踢出仲裁。
3. 扩容与收缩:MON 节点的添加删除操作与注意事项
MON 节点会在什么时候需要增删?最常见的两种情况是硬件换代迁移,以及初期只部署了单 MON 需要扩大冗余。操作本身不复杂,但顺序错了容易出大事,尤其是删除 MON。
3.1 添加一个 MON 节点的完整过程
假设新节点的 IP 是10.0.0.14,hostname 是node4,需要先在这台机器上安装 ceph-mon。如果是容器化或使用 cephadm,过程会有差异,本文讲的是传统手动部署流程,因为它更能体现原理。
第一步,在新节点上生成 MON 的密钥环:
ceph auth get-or-create mon.node4 mon 'allow *' -o /etc/ceph/ceph.mon.keyring这条命令会从集群拉取或新建 MON 认证密钥。注意目录权限,建议chown ceph:ceph后放到标准位置,否则后续服务启动会因读不到密钥而失败。
第二步,在新节点上初始化 MON 数据目录:
ceph-mon -i node4 --mkfs --mon-data /var/lib/ceph/mon/ceph-node4这一步的关键是--mon-data路径必须和配置文件里一致。--mkfs会从集群获取当前 monmap 和密钥环,生成本地数据。如果网络不通或认证失败,这一步就会卡住或报错。
第三步,把新 MON 加入现有集群:
ceph mon add node4 10.0.0.14:6789ceph mon add是控制命令,它会修改 monmap 并把新 MON 的信息广播出去。执行完后,用ceph quorum_status查看,新节点应该出现在列表里。但此时新 MON 的数据目录还是“空”的,需要启动服务逐步同步:
systemctl start ceph-mon@node4启动后 MON 会从现有仲裁节点获取完整的历史数据。有时候启动完成后ceph -s看到的 MON 数增加了,但新节点没有马上参与仲裁,这通常是数据同步还没完成,稍等片刻即可。
3.2 删除一个 MON 的最低伤害操作
删除 MON 是高风险操作,最怕的是把还在线的节点误删。一个稳妥的顺位是:
ceph mon remove node2执行该命令前,务必确认被删节点的心跳已经停止。如果误删了一个还在运行的 MON,该节点会持续尝试加入仲裁并产生状态冲突。最坏情况下,它会带着旧的 monmap 版本反复提示仲裁变化,影响集群稳定性。
删除后还有一步容易遗漏:清理该节点的本地数据目录和认证信息。
ceph auth del mon.node2 systemctl stop ceph-mon@node2 rm -rf /var/lib/ceph/mon/ceph-node2如果不执行ceph auth del,这个节点的认证信息会一直留在集群的 auth 数据库中,虽然短期不会造成故障,但会形成安全面和运维噪音。特别是当集群里 MON、MDS、MGR 角色混在一起时,残留的认证可能会导致后续审计混乱。
3.3 地址变更时必须遵守的操作顺序
MON 的 IP 变了,不能直接改配置文件重启,那样新旧地址会同时在集群中存在,干扰仲裁。正确的做法是:
ceph mon set-addrs node3 [v2:10.0.0.13:3300,v1:10.0.0.13:6789]先把 monmap 里的地址改掉,然后再去节点上改配置文件、重启服务。顺序反了,轻则这个 MON 无法加回仲裁,重则集群内暂时出现两个同名 MON 的冲突。我在一次迁移中就因为先重启服务再改 map,导致该 MON 连续几次被踢出仲裁,最后花了十几分钟才稳定。
对于大规模版本升级,特别注意混合地址问题:Ceph 新版本默认启用 v2 协议,旧节点只有 v1。新老共存时,ceph mon dump会同时显示 v2 和 v1 地址。ceph mon add新节点时如果只写 v1 地址,可能会出现新节点无法被部分旧节点访问的诡异问题。最简单的方式是升级完所有节点后统一使用 v2 地址,并确保mon_host里包含全部可用地址。
4. 容灾恢复:单 MON 数据修复与 monmap 重建的完整流程
MON 的数据目录损坏、monmap 丢失,这些故障听起来吓人,但整套恢复逻辑是成体系的。下面这套流程我用过不止一次,是真正能落地的方案。
4.1 数据目录层面:看懂 critical 文件并做预防
每个 MON 的数据目录/var/lib/ceph/mon/ceph-<name>下,有一组 key 文件,比如store.db、current.epoch等。store.db是 LevelDB 格式的元数据库,保存了 MON 自己管理的状态。如果这个库损坏,ceph-mon进程可能启动到一半就崩溃。
排查方法:先看日志。
journalctl -u ceph-mon@node1 -n 100日志里如果出现Corruption或IO error相关字样,基本可以断定 store.db 有问题。这种情况下,最有效的手段是从另一个健康 MON 拷贝一份数据,或者从备份恢复。
写这篇的时候,我看到不少同行会定期 cron 一条命令来备份 MON 数据,这是很值得推荐的预防性操作:
ceph-mon -i node1 --inject-monmap /tmp/backup_monmap严格说这条命令适合把已有的 monmap 注入到当前数据目录,而不是直接另存。如果想做数据层面的备份,通常用快照或 LevelDB 的在线备份工具。更常见的实操是定期把 monmap dump 出来保存,因为它体积小、恢复成本低,而完整数据目录的备份往往太重。MON 最重要的数据其实是 monmap + auth key,保护好这两个,大部分故障都能兜底。
4.2 monmap 重建:当集群全部 MON 失联时的手动流程
有时整个 MON 层挂了,各 MON 数据目录可能已经不一致。这时需要用ceph-mon --extract-monmap从某个 MON 数据目录里把现有的 monmap 抽出来,再手工重建、修复。
假设所有 MON 都不在线,只剩 node1 的数据目录可用:
ceph-mon -i node1 --extract-monmap /tmp/monmap.bin这个命令会读取 node1 本地数据目录里的 monmap 文件并导出。如果提取失败,说明该目录可能已不完整,可以尝试其他节点的目录。
拿到 bin 文件后,用monmaptool查看并重建:
monmaptool --print /tmp/monmap.bin输出会列出当前 MON 列表及地址。如果发现某个节点地址变了,或某个节点需要移除,直接改:
monmaptool --rm node2 /tmp/monmap.bin monmaptool --add node1 10.0.0.11:6789 /tmp/monmap.bin这些都改完后,重新注入到各 MON 数据目录:
ceph-mon -i node1 --inject-monmap /tmp/monmap.bin ceph-mon -i node2 --inject-monmap /tmp/monmap.bin注意,注入 monmap 时需要指定--inject-monmap参数,它会让 MON 在启动阶段直接使用你提供的 monmap 而不是本地存储的旧版本。原理是在store.db未初始化的状态下,monmap 充当了启动的种子。
启动验证这一步最容易翻车。先手动启动第一个 MON:
ceph-mon -i node1 --foreground观察输出是否进入election状态。如果没有其他 MON 在线,它会一直等仲裁。这时再启动第二个 MON,两个节点应该能形成2/3或3/3仲裁。如果启动时同时带入旧的 monmap 和新的地址,可能出现different fsid报错——这说明你拿到的 bin 文件不属于当前集群,八成是混用了其他集群的备份。
关于 fsid 问题和整体恢复,有一个常见误区要提醒:--inject-monmap修改的是本地数据目录里的 map 文件,不等于修改了集群整体的状态。集群只有在新 MON 启动并形成仲裁后,才会通过 Paxos 把新 monmap 广播出去。所以注入后,尽量第一时间启动所有 MON,不要长时间只有一个节点处于已注入状态,否则一旦这个节点再崩溃,没有其他节点能同步这个新 map,恢复工作就得重来。
5. 认证体系:与 MON 强相关的 auth 命令及故障关联
很多运维把ceph auth单纯当作密钥管理工具,但认证和 MON 的关系远比表面深。MON 认证出问题,轻则客户端连不上,重则 MON 本身互相认为对方非法,导致仲裁分裂。
5.1 认证命令是排查性能故障的方向之一
ceph auth list能列出所有实体及权限,排查某个客户端为何被拒绝时,它是第一步:
ceph auth list输出中你会看到client.admin、client.rbd、osd.0等。重点看caps部分,比如:
client.rbd caps: [mon] allow r caps: [osd] allow rwx如果某个客户端权限缺失,用ceph auth caps修复:
ceph auth caps client.rbd mon 'allow r' osd 'allow rwx'注意caps修改是整体覆盖,不是增量追加。我踩过不少次坑,比如想给某个用户加一条mgr权限,结果把原来的osd权限覆盖掉,导致服务端告警。所以执行前,建议先把原有 caps 复制留存,再构造新命令。
5.2 get-or-create 与 get-or-create-key 的正确姿势
在创建客户端用户时,两条命令经常被混用:
ceph auth get-or-create client.rbd mon 'allow r' osd 'allow rwx' -o /etc/ceph/ceph.client.rbd.keyring这条命令如果用户已存在,它会直接返回现有密钥;如果不存在,则创建。-o参数把 keyring 写进文件,避免在终端显示密钥。
另一条ceph auth get-or-create-key只输出密钥本身,适合在脚本里直接赋值变量,但不建议在生产环境把密钥打印到日志中。两者本质是同一个功能的不同输出形态。
5.3 认证失败导致 MON 反复脱离仲裁的修复
遇到过这样一个故障:集群有 3 个 MON,其中一个节点 OSD 正常,但ceph -s里它的 ID 不在 quorum 里。日志报auth: unable to verify...或monmap mismatch。
这类问题多半不是 MON 之间网络不通,而是它们持有不同的ceph.mon.keyring或认证状态不一致。修复方案是重新同步所有 MON 的 keyring:
ceph auth get mon.node2 -o /tmp/ceph.mon.keyring scp /tmp/ceph.mon.keyring node2:/etc/ceph/ceph.mon.keyring systemctl restart ceph-mon@node2注意:ceph auth get需要在仲裁健康时执行,否则它读不到正确的数据库。如果集群已经因为 MON 认证问题处于震荡状态,先尝试只修复一个 MON 并让它加入仲裁,再逐个修复其他节点,避免一把抓。
还有一种情况是client.admin的 keyring 过期或丢失了,此时即使 MON 正常,也无法用ceph命令连接。修复方法是先用一台还有本地 keyring 的节点,直接指定 MON 地址和密钥进入:
ceph -n client.admin --keyring /etc/ceph/ceph.client.admin.keyring -m 10.0.0.11:6789 status这条命令能绕过默认配置的可达性问题,让它直连某个 MON。如果仍然失败,考虑用ceph-authtool重建一个 admin keyring 再配合caps授权。这是最后手段,务必确保操作者权限足够,否则可能把整个集群的认证账本弄乱。
6. 隐藏命令与监控技巧:注入参数、日志级别调整与 MON 心跳定位
MON 相关命令不只有ceph mon那一簇,很多隐藏能力潜藏在ceph tell和日志参数中。生产环境遇到 MON 相关问题,这些命令往往比常规命令更高效。
6.1 ceph tell mon 注入运行参数
遇到 MON 行为异常,比如 OSD 上报超时导致误判,可以通过ceph tell在运行状态下临时调整参数:
ceph tell mon.* injectargs '--mon_osd_report_timeout 600'mon_osd_report_timeout控制 MON 等待 OSD 上报的时限。默认值较低时,网络稍有波动就可能把健康 OSD 标记为 down,进而触发数据重均衡。如果只是临时的网络抖动,先用这条命令拉长超时,比直接修改整个集群的配置文件要安全。
还可以用它动态调整 MON 日志级别,故障定位时很有用:
ceph tell mon.* injectargs '--debug_mon 20 --debug_paxos 20 --debug_auth 20'日志级别从 0 到 20,默认通常只有 0-5。临时调高后,MON 日志会输出大量 Paxos、认证相关细节。定位完毕记得调回来,否则日志量会暴涨,对磁盘造成压力。
ceph tell mon.* injectargs '--debug_mon 0 --debug_paxos 0 --debug_auth 0'注意:injectargs的修改不写配置文件,服务重启后失效。如果需要长期生效,还是要到ceph.conf里持久化设置。
6.2 通过 mon_status 快速判断心跳和网络隔离
ceph tell mon.0 mon_status或直接在 MON 节点查看:
ceph daemon mon.node1 mon_statusceph daemon命令是针对具体守护进程的接口,mon_status会输出包括rank、state、election_epoch在内的详细信息。state字段可能显示leader、peon、probing等状态。probing说明当前 MON 还在尝试联系其他节点,如果长时间停留在probing,大概率是该节点与其他 MON 的网络隔离,或地址配置错误。
这个命令在排查 MON 到达不了仲裁时非常有用。它比ceph quorum_status更底层,因为它读的是单个守护进程的本地状态,而不是通过控制命令汇总。你会发现有时候ceph quorum_status返回的仲裁是全局视图,而某个 MON 本地认为自己是probing,两者不一致时,就该怀疑该节点的本地视图已经不健康了。
6.3 日志文件路径变化与排查线索
MON 日志在哪,因部署方式不同差别很大。传统手动部署:
- 日志通常在
/var/log/ceph/ceph-mon.node1.log - 如果使用 systemd,journal 是另一个来源:
journalctl -u ceph-mon@node1
实际排查时我习惯先看 journal,因为 systemd 会捕获标准输出和错误,日志时序更完整。文件日志则适合长期归档分析。无论看哪个,建议把debug_mon、debug_paxos调高后再复现问题。Paxos 相关的告警往往是 MON 间选举不稳的信号,比如lease timeout、not enough paxos members之类,看到后第一反应不是重启,而是检查网络丢包和时钟同步。
时钟同步对 MON 来说真的很重要。MON 的 Paxos 协议严重依赖时间戳,NTP 服务一旦失效,即使网络畅通,各节点感知的“当前时间”不同会导致租约不连续、选举频繁重置。处理这种问题,查看时间源比操作 Ceph 命令更快:
chronyc tracking如果发现System time偏差持续几十毫秒到几百毫秒,先处理时钟同步,再观察 MON 仲裁是否自然恢复。这条经验救过我很多次,比盲目调参有效得多。
在实际项目中,我对 MON 命令的使用心得是:所有命令都要有对应“副作用”的意识。ceph mon dump会落盘一份二进制 monmap,ceph mon add会改 monmap 并触发广播,ceph auth caps会整体覆盖权限。理解了这些副作用,你就不会在操作后一脸懵地看到集群状态发生跳变。MON 命令本身并不复杂,复杂的是命令背后那套选举、认证和 map 同步机制。多花一点时间去验证每次命令的后续影响,你对集群的掌控力会有质的变化。