一次 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/cephtagv19.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.h、Paxos.cc @
v19.2.5),而非 Raft 或教科书 Multi-Paxos
照搬。可核对特征:
- Leader / Peon:Probing / Election 产生 Leader;仅 Leader 发起提案;Peon 转发写请求。
- 两阶段提交感:Leader 把变更封成
MonitorDBStore::Transaction,Collect → 多数 Accept → Commit。 - 读租约
MMonLease:Peon 在mon_lease(默认 5s)内可服务只读,降低读放大;租约与osd_heartbeat_grace共同构成「故障发现下界」的一部分。
部署通常用奇数个 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 |
全局标志,如
NOUP、NODOWN、NOSCRUB、NOREBALANCE |
epoch 递增触发条件(不完全列举,见
src/mon/OSDMonitor.cc @ v19.2.5):
- OSD 上报心跳超时被标记为
down - OSD 启动并通过 Boot 流程被标记为
up+in - OSD 被管理员手动
out或in - Pool 创建、删除或参数修改
- CRUSH 规则变更
每次 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
本地),只记录:
- PG
状态字(
active+clean、peering、degraded等) - per-PG 对象数、使用字节数、misplaced/degraded 对象数
- PG Primary OSD 的上报 epoch
ceph pg dump(实际输出由 MGR 提供)和
ceph status 里 PG 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/)。内置模块包括:
dashboard:Ceph Dashboard Web UI 后端prometheus:暴露 Prometheus 指标端点(/metrics)balancer:自动均衡 PG 分布(upmap、crush-compat两种模式)pg_autoscaler:根据使用量自动调整pg_numrbd_support:RBD 快照调度
模块通过 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 的
Objecter(src/osdc/Objecter.h @
v19.2.5)本地缓存 OSDMap。发出的 MOSDOp
携带客户端当前 epoch。OSD 侧在处理前做 map 检查(见
docs.ceph.com/en/squid/dev/osd_internals/map_message_handling/):
- 客户端偏旧:OSD
可通过回复路径附带更新的
MOSDMap(增量或全量),Objecter 合并后按新 acting set 重算目标并重发。 - 客户端偏新 / OSD 尚未追上:请求可能被推迟或要求等待 OSD 追上 map;Objecter 侧表现为订阅更新后的重试,而不是应用层显式错误。
具体 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。
七、小结
- MON quorum 是集群状态的权威来源:使用 Paxos 变体而非 Raft,quorum 过半数才能提交变更;MON 数量应为奇数,3 或 5 为常见部署。
- OSDMap 内嵌 CRUSH map:每次 OSD
状态变更都递增 epoch,增量
map(
OSDMap::Incremental)避免全量传输;flags字段控制全局操作开关如NOSCRUB、NOREBALANCE。 - PGMap 由 MGR 聚合:非强一致,反映的是
OSD 上报的最终一致状态;与 OSDMap 的 epoch
语义不同,
ceph status显示的 PG 统计来自 MGR 而非 MON。 - MGR 是管理面的执行层:主备切换在秒级完成,数据读写路径不依赖 MGR;MGR 宕机期间仅影响 Dashboard / Prometheus 等监控面,不影响 I/O。
- epoch 追赶对应用透明:Objecter 合并
MOSDMap并重路由;表现为延迟毛刺。配置有限osd_op_timeout/rados_osd_op_timeout,避免默认「无超时」在宕机 Primary 上无限挂起。 - 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 变更),但根本上仍是中心化架构。
开放问题
超大规模 MON 瓶颈:当集群扩展到数万 OSD 时,每次 OSD down/up 都触发 OSDMap epoch 递增,MON 的 Paxos 吞吐成为限制因子。目前没有去中心化 map 的生产实现;Crimson 系列在探索是否能减少 map 变更频率,但尚无公开论文级解法。可读入口:OSDI/SOSP 方向的 scalable coordination 研究,以及 Ceph 社区 dev mailing list 的 large-scale deployment 讨论。
PGMap 的最终一致性边界:PGMap 由 MGR 从 OSD 上报聚合,本质上是最终一致的,而 OSDMap 是强一致的。两者 epoch 不同步时,监控工具可能展示短暂不一致的状态。这个边界目前没有正式的 consistency model 文档。
九、参考资料
规范 / 官方文档(A)
- Ceph Squid Architecture —
docs.ceph.com/en/squid/architecture/。 - Ceph Squid MON 配置 —
docs.ceph.com/en/squid/rados/configuration/mon-config-ref/。 - Ceph Squid Map/PG message handling —
docs.ceph.com/en/squid/dev/osd_internals/map_message_handling/。 - Ceph Squid PastIntervals —
docs.ceph.com/en/squid/dev/osd_internals/past_intervals/。
源码(A)
src/mon/Monitor.h、src/mon/Monitor.cc@ v19.2.5:MON 主循环与 Paxos 集成。src/mon/Paxos.h、src/mon/Paxos.cc@ v19.2.5:Paxos 变体实现。src/mon/OSDMonitor.h、src/mon/OSDMonitor.cc@ v19.2.5:OSDMap 变更提案处理。src/osd/OSDMap.h、src/osd/OSDMap.cc@ v19.2.5:OSDMap 结构定义。src/mgr/Mgr.h@ v19.2.5:MGR 主结构。src/osdc/Objecter.h@ v19.2.5:客户端 epoch 缓存与 map 追赶逻辑。
论文(A)
- Sage A. Weil. “Ceph: Reliable, Scalable, and High-Performance Distributed Storage.” PhD Thesis, UC Santa Cruz, 2007.
- Giuseppe DeCandia et al. “Dynamo: Amazon’s Highly Available Key-value Store.” SOSP 2007(对照:gossip-based 去中心化模型)。
- Leslie Lamport. “The Part-Time Parliament.” ACM TOCS, 1998(Paxos 原始论文)。
站内对照
实验台账
- 本篇无命令输出;
ceph -s/ceph osd dump等命令语义在正文中描述,需多节点或 vstart 集群才能执行。
← 上一篇:RADOS 全景 · 下一篇:OSD 与 Messenger
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【Ceph RADOS】RADOS 内核全景:五轴坐标系与 18 篇路线
补齐站内 distributed/38 之上的 RADOS 内核层缺口;定义 Map 传播、通信、一致性、本地存储、接入代价五条坐标系,给出 18 篇阅读路线与系列边界。
【Ceph RADOS】CRUSH 加深:rule/bucket/weight 机制与失败域语义
相对 distributed/38 的 CRUSH 算法概览,深入 bucket 类型的算法复杂度差异、rule step 语义、weight 体系与 primary_affinity,分析 chooseleaf 与失败域的精确定义及 CRUSH 与一致性哈希的本质区别。
【Ceph RADOS】PG 与 Peering:PrimaryLogPG、PG log 与状态机
拆解 PG 命名与生命周期、PrimaryLogPG 的日志式复制设计、pg_log_entry_t 格式、last_epoch_started 与 last_epoch_clean 的语义差异,以及 peering 状态机从 Reset 到 Active 的完整路径。
【Ceph RADOS】客户端写路径:从 librados 到副本应答与 stale read 边界
追踪一次 RADOS 写从 librados IoCtx 构建 MOSDOp、Objecter 路由、Primary 的 do_op() 执行、副本 MOSDRepOp 应答链,到客户端可读性边界与 stale read 触发条件的完整路径。