土法炼钢 · 系统与基础设施

【Ceph RADOS】MON/MGR 与 Map:quorum、epoch 传播与客户端可见性

文章导航

分类入口
storagedistributed
标签入口
#ceph#rados#monitor#osdmap#pgmap#epoch#paxos#mgr#squid#v19.2.5

目录

一次 RADOS 写最先依赖的不是 OSD 应答,而是客户端持有的 OSDMap 是否够新:Primary 发现客户端 map 落后时会附上更新并要求重试(Objecter 透明追赶)。MON quorum 与 epoch 传播是读写失败归因的前提。

本篇不写 cephadm,也不重复 distributed/38 概览。聚焦:MON 的 Paxos 变体与租约、OSDMap 字段与增量传播、PGMap/MGR 的弱一致聚合、客户端可见性窗口。

本文是「Ceph RADOS 存储内核」系列第 2 篇(共 18 篇)。→ 系列目录

篇目 核心内容
第 1 篇 · RADOS 全景 缺口、五轴坐标系、18 篇路线
第 2 篇 · MON/MGR 与 Map quorum、OSDMap/PGMap、epoch 传播
第 3 篇 · OSD 与 Messenger daemon、msgr2、Op 进入 OSD
第 4 篇 · CRUSH 加深 rule/bucket/weight 机制钉
第 5 篇 · PG 与 Peering PrimaryLogPG、PG log、状态机
第 6 篇 · 客户端写路径 librados → 副本应答、stale read 边界

版本锚定:Ceph Squid v19.2.5。源码路径以 ceph/ceph tag v19.2.5 为准;文档以 docs.ceph.com/en/squid/ 为准。


一、MON 的角色与 Paxos 变体

ceph-mon 是集群权威状态的唯一来源(Single Source of Truth)。它不参与数据读写热路径,只维护五类 map:MONMap、OSDMap、PGMap(历史在 MON,Luminous 后统计面迁 MGR)、MgrMap、ServiceMap。客户端与 OSD 从 MON 取权威版本,本地缓存后靠 epoch 判断是否过期。

Quorum 丢失的工程含义:过半数 MON 不可用时,无法提交新的 OSDMap epoch。已持有旧 map 的客户端仍可能短暂读写「看起来还活着」的 OSD,但任何需要新 epoch 的事件(OSD 真正 down、Pool 变更、flag 切换)都会卡住——这常被误诊为「存储层全挂」,其实是管理面权威停摆

Paxos,不是 Raft

Ceph MON 使用自研 Paxos 变体(src/mon/Paxos.hPaxos.cc @ v19.2.5),而非 Raft 或教科书 Multi-Paxos 照搬。可核对特征:

部署通常用奇数个 MON(3 或 5)。两个 MON 在一节点故障时立刻失去 quorum,生产不推荐。OSD 抖动导致高频提案时,Leader CPU 上升会拖慢管理命令,但不应用「加 MON 到很多」幻想线性扩展写吞吐——Paxos 的提交仍受多数派往返约束。

谱系上,Paxos 原始论述见 Lamport(ACM TOCS, 1998);Ceph 选择中心化 map 服务,与 Dynamo(SOSP 2007)的 gossip 形成对照(第八节)。

MONMap 与地址发现

MONMap 记录 MON 名字与地址。客户端经 mon_host 找到任一 MON,拉取完整 MONMap 后可连接任意成员。MONMap 自身变更也走 Paxos,因此「换 MON 地址」是集群级事件,不是改本地 hosts 文件那么轻。


二、OSDMap:epoch 语义与结构字段

OSDMap 是集群数据面的权威描述,每次发生 OSD 状态变更都会产生新的 epoch。它的核心字段包括:

字段 含义
epoch 单调递增的版本号;所有 OSD 和客户端以 epoch 对齐
osd_state[] 每个 OSD 的 up(心跳正常)和 in(被纳入数据分布)标志位
osd_weight[] 用于 CRUSH 计算的 OSD 容量权重(0.0–1.0)
osd_primary_affinity[] Primary 亲和性(0.0–1.0),影响 CRUSH 谁当 Primary
pools Pool 列表,每个 Pool 含 pgp_num、pg_num、CRUSH ruleset、副本数等
CRUSH map 完整的 CRUSH 层级结构与规则,内嵌在 OSDMap 里
flags 全局标志,如 NOUPNODOWNNOSCRUBNOREBALANCE

epoch 递增触发条件(不完全列举,见 src/mon/OSDMonitor.cc @ v19.2.5):

每次 epoch 递增,MON Leader 生成 OSDMap::Incremental(相对上一 epoch 的 diff)。客户端与 OSD 优先拉增量。全量 map 仍可能在跳变、裁剪或缓存空洞时出现——大集群上全量暴涨会直接打满 MON 出口带宽,这是「中心化 map」争论的实证入口。

与数据正确性的关系:epoch 更新不等于数据写完。一个 epoch 可能只标记无关 OSD down,当前 PG 的 acting 不变;Objecter 只在路由结果变化时重发。反之,acting 未变但 PG 仍可能因其他原因 peering(例如需要推进 up_thru)。把「epoch 变了」直接等同「该重做所有 I/O」会误导限流与重试策略。


三、PGMap 与 per-PG 状态

PGMap 记录每个 PG 的聚合统计信息,由 MGR 从各 OSD 上报的 pg_stat_t 中汇聚(v12 Luminous 之后从 MON 迁移到 MGR)。它不记录 PG log 条目(那存在 OSD 本地),只记录:

ceph pg dump(实际输出由 MGR 提供)和 ceph statusPG summary 都来自 PGMap。理解 PGMap 与 OSDMap 分离很重要:OSDMap 是 MON 的强一致数据,PGMap 是 MGR 的最终一致聚合,两者 epoch 语义不同。


四、MGR:主备模型与模块框架

ceph-mgr 是 Luminous(v12)引入的管理守护进程,主要目的是把监控聚合与插件逻辑从 MON 中解耦出来,避免 MON Paxos 路径上的额外负担。

主备切换

同一集群可运行多个 MGR,但只有一个处于 Active 状态,其余为 Standby。Active MGR 宕机时,Standby 通过与 MON 的心跳检测在数秒内切换上来,无需 quorum 协商。切换期间 ceph status 等监控命令短暂不可用,但数据读写路径不受影响。

模块框架

MGR 提供 Python 模块框架(src/mgr/)。内置模块包括:

模块通过 MGR 提供的 C++ 绑定读取 OSDMap、PGMap 和 OSD perf counter,不能直接修改集群状态;修改需通过 MON 命令。理解这一点可避免一种误诊:Dashboard 短暂不可用时,有人以为数据面也挂了;实际上只要 MON quorum 与 OSD 仍在,I/O 可以继续,只是失去了聚合视图。

Balancer 的 upmap 模式会改写部分 PG 的最终映射(第 4 篇),这些变更最终仍体现为 OSDMap epoch 递增——因此「开了自动均衡」会提高 epoch 速率,并间接放大 past intervals 与客户端追赶频率。运维上把 balancer 与 NOREBALANCE 等 flag 放在同一坐标系里考虑,而不是当成无关的美化插件。


五、epoch 传播:增量 map 与订阅模型

OSD 侧的 map 获取

OSD 启动时通过 OSD::init() 向 MON 发送 MMonSubscribe,订阅 OSDMap 和 PGMap 更新。MON 在 epoch 变更时主动推送增量 map(MOSDMap 消息,含 incremental 字段);OSD 合并后更新本地缓存。这是 push 模型,OSD 无需轮询。

客户端侧的 epoch 缓存

librados 的 Objectersrc/osdc/Objecter.h @ v19.2.5)本地缓存 OSDMap。发出的 MOSDOp 携带客户端当前 epoch。OSD 侧在处理前做 map 检查(见 docs.ceph.com/en/squid/dev/osd_internals/map_message_handling/):

具体 result code 随消息类型与版本分支变化,排障时应在日志中同时看 map epoch 差是否伴随 MOSDMap,不要只记一个 errno 口诀。Objecter::_calc_target() 仅在 acting/Primary 变化时重路由 pending Op,避免每个无关 epoch 都重发全集。

map 传播与 PastIntervals 的耦合

OSDMap 不只服务路由:peering 要用 past intervals(自 last_epoch_started 以来 acting/up 不变的 epoch 段)决定必须联系哪些历史成员(dev/osd_internals/past_intervals/)。因此「epoch 涨得太快」不只是 MON CPU 问题——它放大 past interval 集合、拉长 peering,并增加客户端追赶次数。NOSCRUB / NOREBALANCE 等 flags 降低无关 epoch 噪声,是运维上缩小可见性窗口的常用手段。

sequenceDiagram
  participant MON as MON Leader
  participant OSD as OSD (Primary)
  participant CLI as librados Client

  MON->>OSD: MOSDMap (incremental, epoch N)
  MON->>CLI: MOSDMap (incremental, epoch N)
  CLI->>OSD: MOSDOp (epoch N, object X)
  OSD->>OSD: check epoch match
  OSD-->>CLI: op result and/or MOSDMap

六、客户端可见性窗口

持有 epoch \(N-1\) 的客户端,在 MON 已发布 epoch \(N\)(某 OSD down)后,仍可能向旧目标发 Op。若目标进程已死,发现延迟由 osd_op_timeout(默认 0 = 无超时)和 Messenger keepalive 决定——这是可用性窗口,换取「不必每 I/O 问 MON」。

与数据可见性不同:新 epoch 可能只动无关 OSD,acting 不变则无需重路由。OSD 收到 map 后会对相关 PG 发通知并进入 peering;客户端多在下一次 Op 才被动发现落后。故障刚发生时,监控上「先更糟再恢复」常来自这层异步差,而不是 BlueStore 突然变慢。

读路径上,即使 map 最终一致,仍须满足第 6 篇的 readable_until 租约,才能避免旧 interval 服务 stale read。


七、小结

  1. MON quorum 是集群状态的权威来源:使用 Paxos 变体而非 Raft,quorum 过半数才能提交变更;MON 数量应为奇数,3 或 5 为常见部署。
  2. OSDMap 内嵌 CRUSH map:每次 OSD 状态变更都递增 epoch,增量 map(OSDMap::Incremental)避免全量传输;flags 字段控制全局操作开关如 NOSCRUBNOREBALANCE
  3. PGMap 由 MGR 聚合:非强一致,反映的是 OSD 上报的最终一致状态;与 OSDMap 的 epoch 语义不同,ceph status 显示的 PG 统计来自 MGR 而非 MON。
  4. MGR 是管理面的执行层:主备切换在秒级完成,数据读写路径不依赖 MGR;MGR 宕机期间仅影响 Dashboard / Prometheus 等监控面,不影响 I/O。
  5. epoch 追赶对应用透明:Objecter 合并 MOSDMap 并重路由;表现为延迟毛刺。配置有限 osd_op_timeout / rados_osd_op_timeout,避免默认「无超时」在宕机 Primary 上无限挂起。
  6. epoch 速率影响 peering 面:过快的 map 变更拉长 past intervals;flags 与减少无效 CRUSH/upmap 抖动是同一坐标系上的控制旋钮。

八、谱系、争论与开放问题

谱系

MON 的 Paxos 实现可追溯到 Sage Weil 博士论文(2007)。论文设计中 RADOS 就预设了中心化 map 服务,这与同期 Dynamo(DeCandia et al., SOSP 2007)采用去中心化 gossip 的选择形成对比。Ceph 选择中心化 map 有明确理由:确定性的 epoch 语义使 peering 和 recovery 的正确性更容易推理;代价是 MON 成为单一 quorum 依赖点。

争论:中心化 map vs P2P gossip

A 侧(Ceph MON Paxos):epoch 全局有序,客户端和 OSD 的一致性协议边界清晰,peering 状态机可以依赖 epoch 单调性做假设。
B 侧(gossip-based,如 Dynamo / Cassandra):无单点 quorum 瓶颈,超大集群(数万节点)可线性扩展;代价是最终一致性,epoch-style 协议难以直接移植。

在 Ceph 实际部署中,超过数千 OSD 的集群开始出现 MON 的 map 处理压力,社区在 Reef(v18)和 Squid(v19)中对 MON 的 map 存储做了优化(如 pg_upmap_primary 减少无效 map 变更),但根本上仍是中心化架构。

开放问题

  1. 超大规模 MON 瓶颈:当集群扩展到数万 OSD 时,每次 OSD down/up 都触发 OSDMap epoch 递增,MON 的 Paxos 吞吐成为限制因子。目前没有去中心化 map 的生产实现;Crimson 系列在探索是否能减少 map 变更频率,但尚无公开论文级解法。可读入口:OSDI/SOSP 方向的 scalable coordination 研究,以及 Ceph 社区 dev mailing list 的 large-scale deployment 讨论。

  2. PGMap 的最终一致性边界:PGMap 由 MGR 从 OSD 上报聚合,本质上是最终一致的,而 OSDMap 是强一致的。两者 epoch 不同步时,监控工具可能展示短暂不一致的状态。这个边界目前没有正式的 consistency model 文档。


九、参考资料

规范 / 官方文档(A)

源码(A)

论文(A)

站内对照

实验台账


上一篇:RADOS 全景 · 下一篇:OSD 与 Messenger

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。


By .