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

【Ceph RADOS】CephFS / MDS:可恢复元数据服务、subtree 分区与 POSIX 代价

文章导航

分类入口
storagedistributed
标签入口
#ceph#cephfs#mds#posix#subtree#journal#capability#squid#v19.2.5

目录

CephFS 把一个 POSIX 文件系统建在 RADOS 之上。块存储(RBD)只要求对象写入正确,文件系统要求更多:原子目录操作、inode 一致性、fsync 语义、硬链接、目录快照。这些保证在分布式场景下不是免费的。

核心问题:MDS 宕掉后元数据从哪恢复、capability 如何把缓存一致性钉在跨客户端协作上、多 active MDS 的 subtree 迁移何时变成 thrashing、以及 fsync / stat 最终触碰多少网络往返。

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

篇目 核心内容
第 12 篇 · RBD image→对象、krbd vs librbd、快照/克隆边界
第 13 篇 · CephFS / MDS 可恢复 MDS、caps、subtree thrashing、POSIX 代价
第 14 篇 · RGW 索引/数据池、S3→RADOS 映射

版本锚定:Ceph Squid v19.2.5docs.ceph.com/en/squid/cephfs/;源码 src/mds/ @ v19.2.5)。


一、「无状态 MDS」该怎么读

Squid 文档原话(CephFS 首页):MDS 把元数据变更聚合成对 RADOS 上 journal 的高效写;「no metadata state is stored locally by the MDS」——MDS 进程本地磁盘不持久化元数据权威态。这常被口语化成「无状态 MDS」,容易误读成「MDS 内存里什么都没有、随时可杀」。

更准确的工程表述是:

层次 有没有状态 落在哪
权威持久态 元数据池:MDS journal + dirfrag OMAP + inode backtrace 等
热缓存 / 进行中请求 MDS 进程内存:MDCacheMDRequest、已发放 caps
本地磁盘权威副本 故意不存;宕机靠 journal replay 重建内存视图

因此:MDS 是可恢复状态机,不是无状态微服务。杀进程会丢未 flush 的内存缓存,但已 journal 的变更可被 standby replay;客户端未收到 reply 的请求必须重发。把「无本地盘」说成「完全无状态」,会低估 failover 时的 journal 积压与 cap 重发成本。

谱系锚点:Weil et al.「Ceph: A Scalable, High-Performance Distributed File System」(OSDI 2006)提出动态 subtree 分区(DPSS)与「客户端直接访 RADOS 数据、MDS 只权威元数据」;与 GFS 单 master(Ghemawat et al., SOSP 2003)对照。CRUSH 放置另见 Weil et al.(SC 2006),本篇不展开。


二、CephFS 的 RADOS 存储布局

数据池与元数据池

CephFS 至少两个池:元数据池数据池。元数据池存 inode、目录条目、MDS journal;数据池存文件内容对象。客户端数据 I/O 直连 OSD,不经 MDS 转发——这是 OSDI’06 架构相对「网关型」文件系统的关键分叉。

文件内容默认按 4MiB 对象切分,OID 形如 {inode_hex}.{stripe_seq}

1000000000000.00000000
1000000000000.00000001

元数据池另含:MDS journal 对象序列(mds.{rank}.*)、dirfrag OMAP、snap 相关元数据。


三、MDS Journal:写路径与恢复

Journal 结构

Journal 写入元数据池 RADOS 对象序列(默认段约 4MiB)。journaler 维护 write_pos(追加点)与 expire_pos(已 flush 到 dirfrag/inode 对象后可截断点)。路径:

  1. 内存完成 MDRequest(改 MDCache、目录树)。
  2. 序列化 event,append 到 journal(RADOS write)。
  3. journal ACK 后,才向客户端 reply / 发 cap grant。
  4. 后台把脏元数据 flush 到 dirfrag 等对象,再推进 expire_pos

失败模式:步骤 2 完成后宕机 → standby 从 expire_pos replay。客户端未收到 reply 的请求重发;挂载短暂 block。replay 时间与 journal 未 trim 量成正比;元数据池慢时段数上涨,failover SLO 崩。可 ceph tell mds.{rank} flush journal 强制 flush(阻塞调用方)。

站在排障视角:MDS「看起来挂了」时,先分清是进程死了(要 replay)、是 journal 写不动(元数据池 commit latency)、还是 cap revoke 卡死某个客户端。三者症状都是「文件系统卡住」,机制入口完全不同——前者看 MDS daemon,中者看元数据池 OSD,后者看 cap_revoke_eviction(第 15 篇)。


四、Active-Standby 与多 Active MDS

默认:rank 0 active + standby(可 standby-replay 尾随 journal,缩短接管时的 replay 窗口)。ceph fs set {fs} max_mds N 开多 active:每 rank 管一棵 subtree;跨 rank rename 要两阶段协调。只在元数据 IOPS 真成瓶颈时开——subtree 迁移本身有 barrier 税,盲目加 rank 往往先买到 thrashing,而不是线性扩展。

单 active 的隐含假设是:元数据热点可以纵向加内存与更快的元数据池磁盘。多 active 的隐含假设是:热点可被空间切开且迁移开销可摊销。HPC scratch、CI 产物目录、大量小文件创建,常常违反第二假设。


五、Subtree 分区与 thrashing

Subtree 与 auth MDS

目录树切成 subtree,每棵有 auth MDS。inode backtrace 记录归属,客户端与 MDS 据此路由。负载高时 Migratorsrc/mds/Migrator.cc)选目录发起 EXPORT_DIR:传输缓存与 caps,迁移中加 barrier,完成后目标变 auth,源侧收回旧授权。

迁移时长与活跃 inode 数量、持有的 cap 数量相关。barrier 窗口内,该子树上的元数据 op 会排队;对业务表现为「某一个目录树突然变慢」,而 ceph -s 可能仍然 HEALTH_OK——因为 RADOS 数据面并未降级。

Thrashing(震荡)机制

thrashing 不是「负载高」的同义词,而是:迁移触发阈值与负载采样周期耦合,导致同一目录短时间多次 export/import。典型诱因:

后果:barrier 期间该子树 op 排队;客户端 cap 反复 revoke/grant;ceph fs status 上 Ops 抖动但业务吞吐下降。缓解方向是调高迁移门槛、拉长采样、或把热点目录钉到固定 rank——不是盲目加 max_mds。Weil OSDI 2006 的动态 subtree 假设负载可平滑迁移;高 inode 创建率场景是该假设的反例,社区均衡权重仍在迭代,尚无闭合最优策略。


六、Capability:客户端缓存与一致性

MDS 向客户端发放 capability(cap),授权缓存或缓冲写。关键类型:

Cap 含义
Fc / Fr 缓存读 / 可读
Fb / Fw 缓冲写 / 可写
Fsx 独占写
Ls 链接与 inode 属性相关

Fb 的写客户端未 flush 前,其他客户端同文件读会被卡住(revoke 路径)。这是分布式下逼近 POSIX 一致读写的核心税:正确性靠「谁持有哪些 cap」的显式协议,而不是靠每次都问 MDS。

fsync()(kclient / ceph-fuse)大致串行:脏数据落到 OSD 并拿到 ACK,再向 MDS 做 cap flush,使 journal 落 RADOS。两段往返叠在一起,比本机 fsync 贵一个数量级以上。频繁小 fsync 的数据库不该直接落 CephFS——这是机制结论,不是偏好。

Cap 失败模式:客户端慢响应会推高 cap_revoke_eviction;网络分区后「假死」客户端被踢,恢复时可能丢未 flush 的缓冲写(取决于挂载选项与应用是否真正 fsync)。排障时若只盯 OSD 延迟而忽略 MDS cap 状态,会把「元数据锁等待」误诊成「磁盘慢」。


七、POSIX 代价模型

stat:有有效 cap 则本地返回;否则走 GETATTR。大目录 ls -l 靠 readdir batch 摊薄往返;精确 nlink 等字段仍可能额外 roundtrip。

目录分片:单 dirfrag OMAP 过大时 split;百万文件目录是已知痛点,split 过程中目录 op 短暂 stall。运维上这常被描述成「CephFS 卡住一下」,根因却在元数据对象形态,而不在数据池容量。

硬链接 / 跨 rank rename:backtrace 追踪多路径;distributed rename 需要锁定两个目录权威。跨 rank 的原子 rename 是「保留 POSIX」账单上最显眼的一行。

目录快照mkdir .snap/):目录级;依赖客户端 flush 协作,与 RBD「OSD 侧纯 COW、不经客户端」不同。硬链接跨 snap 边界的语义重建是已知摩擦(见下节)。

把这些条目加总:CephFS 的税主要付在元数据面(journal、caps、subtree、dirfrag),数据面反而相对「直」。用 RBD 的延迟经验直接外推 CephFS,会漏掉最贵的那一段。


八、MDS 快照与 RBD 快照对照

维度 RBD snap CephFS .snap
粒度 整 image 目录子树
一致性协作 需应用 / fsfreeze / qemu-ga quiesce MDS 通知持 cap 客户端 flush
COW 落点 PrimaryLogPG snapset 数据池对象 + MDS snap 元数据
硬链接 不适用 跨 snap 边界重建不完整是已知限制

CephFS 快照更贴近「用户可见的目录时间点」,也更依赖客户端诚实;RBD 快照更「存储层自闭」,但不知道文件系统内部是否一致。两者都借用 RADOS SnapContext 思想,却不能互相替代。


九、挂载方式

方式 实现 要点
kclient fs/ceph/ POSIX 完整度与性能更好;features 跟内核版本绑定
ceph-fuse 用户态 FUSE 可移植、易调试;路径多一层 FUSE

Squid 生产 Linux 挂载优先核对内核是否覆盖所需 cap 与 snap 行为;容器无权限挂内核文件系统时才退到 fuse。fuse 便于调试 journal/cap 问题,但不应默认当成与 kclient 等价的性能路径。


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

谱系:OSDI 2006(Weil et al.)定义「MDS 权威元数据 + 客户端直访数据 + 动态 subtree」;工程实现把权威态钉在 RADOS journal,进程内存是可丢缓存。官方文档的「no local metadata state」必须和本篇第一节一起读,避免「无状态」误导。CRUSH(SC 2006)回答对象放哪,不回答 inode 锁怎么发——两篇论文分工不同。

争论:完整 POSIX(含原子 rename、精确 nlink)对比弱化语义换扩展性(HDFS/GFS 阵营)。Ceph 选前者,跨 rank 付两阶段税。官方 docs.ceph.com/en/squid/cephfs/posix/ 列出已知偏差(mtime 粒度、O_SYNC 范围等)——以文档为准,不靠传闻。

开放问题(可检验):

  1. thrashing:固定一组 mds_bal_* 参数,在「每秒创建大量 inode、目录扇出固定」负载下,统计 10 分钟内同一 dirino 的 export 次数;若反复超过自定阈值,判均衡不稳定——是否存在参数使该计数趋近零?
  2. fsync 尾延迟:元数据池 commit latency 与客户端 fsync P99 是否同峰?若同峰,瓶颈在 journal 串行写而非数据池。
  3. fragment split 可观测性:split stall 是否能在不抓 MDS perf dump 的情况下被 ceph fs status 暴露?Squid 上仍偏运维盲区。

再补一条工程读法:把「文件系统卡住」拆成三条互不替代的假设——元数据服务进程死亡需要日志重放、日志写不动对应元数据池提交延迟、能力证回收卡死对应客户端迟迟不归还缓冲写权限。三者在监控上看都像「目录树不动」,但入口分别是守护进程、对象存储守护进程延迟、以及元数据服务内部计数器。混为一谈会把能力证等待误诊成磁盘慢。

与第十二篇对照:块设备快照的写时复制落在主日志归置组,文件系统快照还要客户端协作刷新。选接入层时,先问应用要不要频繁同步写,再问要不要目录级时间点,最后才问挂载方式。顺序反了,容易在已经付了能力证税之后才发现同步写不可接受。

单活跃元数据服务足以覆盖许多中等元数据速率场景;多活跃只有在能够证明热点可被空间切开、且迁移屏障可接受时才值得打开。否则多出来的秩只会增加跨权威目录改名的两阶段协调面,把本来局部的抖动变成全局协调税。

参考资料

论文 / 官方文档(A 级)

系列内

上一篇:RBD · 系列目录 · 下一篇:RGW

读完这篇,下一步读什么

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


By .