深入解析 Crimson OSD 生命周期状态机从 preboot、booting 到 active 的完整启停流程【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址: https://gitcode.com/gh_mirrors/ce/ceph本文以 Ceph 新一代 OSD 实现 Crimson基于 Seastar 协程的异步 OSD 守护进程的OSDState状态机为主题系统讲解 OSD 从启动注册、健康检查、等待激活到优雅下线的全部状态转换及其背后消息协议。读完本文你将掌握 Crimson OSD 五个核心状态preboot、booting、active、prestop、waiting_for_healthy的语义、状态迁移触发条件并能对照源码src/crimson/osd/state.h、src/crimson/osd/osd.cc追踪MOSDBoot、MOSDMarkMeDown等消息在启动与停机路径中的实际作用。为什么 Crimson OSD 需要显式的状态机经典ceph-osd的启动与停止逻辑分散在OSD::init()、OSD::handle_osd_map()等大量回调中状态边界并不直观。Crimson 采用 Seastar 的sharded架构把 OSD 拆分为多个 shard 并行运行因此需要一个集中、可广播、可被各 shard 查询的守护进程级状态。OSDState类src/crimson/osd/state.h正是为此设计它继承seastar::peering_sharded_serviceOSDState每个 shard 持有一个本地实例状态枚举定义了 7 个值state.h#L30-L38INITIALIZING、PREBOOT、BOOTING、ACTIVE、PRESTOP、STOPPING、WAITING_FOR_HEALTHYPRIMARY_COREcore 0是状态迁移的权威执行者set_active()/set_stopping()通过invoke_on_all把状态广播到所有 shardstate.h#L95-L111任意 shard 都可以只读查询is_active()、is_stopping()并通过when_active()挂起等待直到 OSD 进入activestate.h#L68-L74。从源码结构看OSDState比文档状态图多出INITIALIZING与STOPPING两个内部状态前者表示 OSD 仍在初始化、尚未订阅 osdmap_handle_osd_map()会因此直接丢弃收到的地图见 osd.cc#L1183-L1186后者表示已进入最终停止流程。状态机总览原始设计文档 doc/dev/crimson/osd.rst 用 graphviz 描绘了如下状态迁移本文以 Mermaid 状态图呈现同一语义状态进入条件退出条件语义waiting_for_healthy心跳不可达或内部心跳失败tick周期性复查恢复健康后转preboot自检等待健康preboot守护进程启动 / 被标记 down 后地图就绪后发送MOSDBoot转booting准备注册booting发出MOSDBoot收到标记自己为 up 的 osdmap 转active等待被 quorum 认可activeosdmap 标记自己为 upstop()/ 被标 down / SIGINT / 不健康正常服务业务prestop收到stop请求收到更新后的 osdmap 或MOSDMarkMeDown优雅下线前的收尾各状态语义详解waiting_for_healthy先证明自己健康再谈服务如果 OSD 守护进程无法连接到自己的心跳heartbeat对端或者自身内部心跳失败它就被判定为不健康进入waiting_for_healthy状态并周期性检查自身的网络可达性与内部心跳状态直到恢复健康后才继续启动流程。对应到代码心跳子系统由 src/crimson/osd/heartbeat.h 的Heartbeat类实现它实现了crimson::net::Dispatcher接口监听心跳消息并维护与对端 OSD 的连接会话。需要说明的是从当前源码看waiting_for_healthy的完整实现仍处于待完善状态在 osd.cc#L1329-L1336 有一处注释明确写着TODO: Missing start_waiting_for_healthy() counterpart关联跟踪单 66832当前实现是在未停止时持续订阅下一份 osdmap。这意味着文档描述的是该状态的既定设计意图而代码中进入waiting_for_healthy并周期性 tick 自检的完整闭环仍在演进中。preboot向 Monitor 报告我准备好了preboot是 OSD 启动后的第一个正式状态。OSD 向已连接的 Monitor 发送MOSDBoot消息告知集群它已准备好对外服务从而让 quorum 在 osdmap 中把它标记为up。源码中的启动入口是OSD::start_boot()osd.cc#L641-L648seastar::future OSD::start_boot() { pg_shard_manager.set_preboot(); return monc-get_version(osdmap).then(this { auto [newest, oldest] ret; return _preboot(oldest, newest); }); }它先调用pg_shard_manager.set_preboot()置位状态该调用最终转发到OSDState::set_preboot()见 pg_shard_manager.h#L120再从 Monitor 查询当前 osdmap 版本区间进入_preboot()。_preboot()osd.cc#L650-L682是一系列准入检查只有全部通过才会真正发送 boot 消息osdmap 尚未取得首个地图get_epoch() 0时等待初始 osdmap若 osdmap 标记本 OSD 已被销毁is_destroyed抛出异常终止若设置了NOUP标志则等待其清除要求SORTBITWISE标志已设置要求集群require_osd_release不低于 octopus此处可看出 Crimson 的基线版本约束当地图 epoch 接近最新时按osd_map_message_max配置决定是否立即_send_boot()否则通过osdmap_subscribe补齐缺失的地图。booting等待被 quorum 认可在正式被标记为up之前OSD 必须停留在booting状态。_send_boot()osd.cc#L684-L724完成两个动作调用pg_shard_manager.set_booting()把状态从preboot推进到booting组装并发送MOSDBoot消息携带 superblock、当前 osdmap epoch、boot_epoch、心跳front/back地址、集群地址以及CEPH_FEATURES_ALL特性位。值得注意的是该消息的 metadata 中显式写入了osd_type crimsonosd.cc#L715-L717。从注释可知这与OSDMonitor::preprocess_boot的校验逻辑相呼应——Monitor 只有在 osdmap 设置了允许 crimson 的allow_crimson标志时才会受理注册这是集群侧对 Crimson OSD 的准入控制。此外还附带从对象存储读取的osd_objectstore类型如seastore。active正式对外服务当 OSD 收到一份把自己标记为up的 osdmap 时迁移到active状态此后才有资格处理客户端 I/O、参与 PG 的 peering 与 recovery。激活判断集中在committed_osd_maps()osd.cc#L1256-L1354。每消费一份新地图OSD 都会检查三个条件if (bind_epoch up_from osdmap-get_addrs(whoami) public_msgr-get_myaddrs() pg_shard_manager.is_booting()) { INFO(osd.{}: activating..., whoami); co_await pg_shard_manager.set_active(); beacon_timer.arm_periodic( std::chrono::seconds(local_conf()-osd_beacon_report_interval)); tick_timer.arm(std::chrono::seconds(TICK_INTERVAL)); }即本 OSD 在地图中处于up、绑定 epoch 早于up_from、公开地址与地图记录一致、且当前正处于booting——四者齐备才调用set_active()广播激活同时启动周期性beacon间隔由osd_beacon_report_interval配置控制和 tick 定时器。send_beacon()只在is_active()时发送MOSDBeaconosd.cc#L1617-L1630这也是 Monitor 判断 OSD 存活的依据之一。active状态并非永久稳定文档明确指出存在三类退出路径管理员手动停止或 osdmap 中标记stopcommitted_osd_maps()检测到osdmap-is_stop(whoami)后调用shutdown()osd.cc#L1339-L1340shutdown()内部通过abort_source.request_abort()触发整体中止osd.cc#L1609-L1615被标记 down 或地址不匹配should_restart()osd.cc#L1560-L1582检查地图是否仍把本 OSD 标为 up、地图中的 public/cluster 地址是否与本地 messenger 实际绑定地址一致任一不满足就触发restart()——取消各定时器、清零 up epoch然后重新start_boot()osd.cc#L1597-L1607即状态回到preboot这也对应图中active - preboot的迁移进程收到 SIGINT直接终止见下文停机流程。prestop优雅下线的告别仪式无论何种原因收到stop请求OSD 都无条件转入prestop。但在真正告别之前它会向 Monitor 发送MOSDMarkMeDown请求得到确认——确认的方式是收到更新后的 osdmap其中自己已不在up集合或另一条MOSDMarkMeDown消息。源码中这一流程由OSD::prepare_to_stop()承载osd.cc#L1780-L1803if (osdmap osdmap-is_up(whoami)) { pg_shard_manager.set_prestop(); const auto timeout ... local_conf().get_valdouble(osd_mon_shutdown_timeout); try { co_await seastar::with_timeout( ... , monc-send_message( crimson::make_messageMOSDMarkMeDown( monc-get_fsid(), whoami, osdmap-get_addrs(whoami), osdmap-get_epoch(), true)).then([this] { return stop_acked.get_future(); })); } catch (seastar::timed_out_error) {} }要点包括只有当前地图仍把自己标为up时才需要走确认流程否则无需等待set_prestop()之后OSD 停止接收新业务发送MOSDMarkMeDown并等待stop_ackedpromise 完成等待时长受配置osd_mon_shutdown_timeout约束超时则放弃等待继续关闭两种确认分别由两个代码路径兑现handle_mark_me_down()在收到 Monitor 回传的MOSDMarkMeDown时调用got_stop_ack()osd.cc#L1473-L1482committed_osd_maps()在处理到一份已不再把本 OSD 标为 up 的地图时同样调用got_stop_ack()osd.cc#L1323-L1326。got_stop_ack()定义于 osd.h#L259-L263。停机链路从信号到状态机的完整闭环prestop之上还有STOPPING状态。OSDState::set_stopping()会把本地状态置为STOPPING同时向所有等待when_active()的协程抛出system_shutdown_exceptionstate.h#L50-L54让那些等待激活的异步任务以异常方式尽快终止避免悬挂。进程级停机入口在 src/crimson/osd/main.ccosd.start().get(); INFO(crimson startup completed); should_stop.wait().get(); // 等待 SIGINT / SIGTERM INFO(crimson shutting down); osd.stop().get(); // 触发 OSD::stop()should_stop是 Seastar 应用库的stop_signalsrc/crimson/osd/stop_signal.h注册了 SIGINT 与 SIGTERM 的处理器收到信号后置位 abort、广播条件变量wait()随即返回进而调用OSD::stop()。OSD::stop()osd.cc#L855-L885内部先调用prepare_to_stop()即上面所述prestop确认流程再依次停止 public/cluster messenger、admin socket、heartbeat、对象存储、mon/mgr 客户端及各 sharded service。这正是状态图中active - end (kill(SIGINT))的代码级还原。值得注意的是SIGHUP 在 Crimson 中被显式忽略main.cc#L190-L192即不随信号重读配置这与经典 OSD 的行为不同也说明 Crimson 当前不依赖 SIGHUP 做配置热更新。状态机的并发安全设计由于 OSD 状态迁移set_preboot、set_booting、set_prestop都带有ceph_assert(seastar::this_shard_id() PRIMARY_CORE)断言state.h所有状态写入被严格限制在 core 0避免多核并发写状态造成竞态而is_active()、is_stopping()则允许任意 shard 只读访问用于本地快速判断是否继续派发业务。此外MOSDMap的消费在 osd.cc#L1159-L1173 通过handle_osd_map_lock唯一锁串行化——因为committed_osd_maps()中的状态判定is_booting()、is_preboot()、is_prestop()依赖于地图按序消费同一时刻只允许一个地图处理流程推进状态机。这种单一写入者 多读副本 事件串行化的设计是 Crimson 将经典 OSD 生命周期管理适配到 Seastar 多 shard 模型下的核心思路也是理解 doc/dev/crimson/osd.rst 状态图在真实运行时的必要背景。小结Crimson OSD 的生命周期可以用一条主线概括启动后进入preboot通过_preboot()一系列集群准入检查后发送MOSDBoot转入booting收到把自己标记为 up 的 osdmap 后激活为active并开启 beacon/tick 周期任务此后可能因停止请求进入prestop发送MOSDMarkMeDown等待确认直至STOPPING或因健康问题回到waiting_for_healthy、因被标记 down 而回到preboot。对照 src/crimson/osd/state.h 与 src/crimson/osd/osd.cc 阅读本文可以清晰地看到每一个状态转换背后对应的消息与函数调用为排查 Crimson OSD 启动失败、无法激活或优雅停机超时等问题提供直接依据。【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址: https://gitcode.com/gh_mirrors/ce/ceph创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
