注意
本文档适用于 Ceph 的开发版本。
MDS 日志记录
CephFS 元数据池
CephFS 使用一个单独的(元数据)池来管理 Ceph 文件系统中的文件元数据(inode 和 dentries)。元数据池包含有关 Ceph 文件系统中文件的所有信息,包括文件系统层次结构。此外,CephFS 维护与文件系统中其他实体相关的元信息,例如文件系统日志、打开文件表、会话映射等。
本文档描述了 Ceph 元数据服务器如何使用和依赖日志记录。
CephFS MDS 日志记录
CephFS 元数据服务器在执行文件系统操作之前,会将元数据事件流式传输到元数据池中的 RADOS 中。活动 MDS 守护程序管理 CephFS 中文件和目录的元数据。
CephFS 使用日志记录的原因有以下几点
一致性:在 MDS 故障转移时,可以重放日志事件以达到一致的文件系统状态。此外,需要对支持存储进行多次更新的元数据操作需要进行日志记录以实现崩溃一致性(以及其他一致性机制,例如锁定等)。
性能:日志更新(大部分)是顺序的,因此对日志的更新速度很快。此外,更新可以批量写入,从而节省了更新文件不同部分所涉及的磁盘寻道时间。拥有一个大的日志还有助于备用 MDS 预热其缓存,这在 MDS 故障转移期间间接提供了帮助。
每个活动的元数据服务器都在元数据池中维护自己的日志。日志条带化到多个对象上。不再需要(被视为旧)的日志条目由元数据服务器进行修剪。
日志事件
除了记录文件系统元数据更新之外,CephFS 还记录各种其他事件,例如客户端会话信息和目录导入/导出状态。元数据服务器使用这些事件来根据需要重新建立正确的状态,例如,当重放日志事件并且日志中的特定事件类型指定客户端实体类型在重启之前与 MDS 存在会话时,Ceph MDS 会尝试重新连接客户端。
为了检查日志中记录的此类事件列表,CephFS 提供了一个命令行实用程序 cephfs-journal-tool,可以按如下方式使用
cephfs-journal-tool --rank=<fs>:<rank> event get list
cephfs-journal-tool 也用于发现和修复损坏的 Ceph 文件系统。(有关更多详细信息,请参阅 cephfs-journal-tool)
日志事件类型
以下是 MDS 记录的各种事件类型。
EVENT_COMMITTED:将请求 (id) 标记为已提交。
EVENT_EXPORT:将目录映射到 MDS 级别。
EVENT_FRAGMENT:跟踪目录碎片化(拆分/合并)的各个阶段。
EVENT_IMPORTSTART:当 MDS 级别开始导入目录片段时记录。
EVENT_IMPORTFINISH:当 MDS 级别完成导入目录片段时记录。
EVENT_NOOP:用于跳过日志区域的无操作事件类型。
EVENT_OPEN:跟踪哪些 inode 具有打开的文件句柄。
EVENT_RESETJOURNAL:用于在截断后将日志标记为 reset。
EVENT_SESSION:跟踪打开的客户端会话。
EVENT_SLAVEUPDATE:记录已转发到(从属)mds 的操作的各个阶段。
EVENT_SUBTREEMAP:目录 inode 到目录内容(子树分区)的映射。
EVENT_TABLECLIENT:记录 MDS 对客户端表(快照/锚点)视图的转换状态。
EVENT_TABLESERVER:记录 MDS 对服务器表(快照/锚点)视图的转换状态。
EVENT_UPDATE:记录 inode 上的文件操作。
EVENT_SEGMENT:记录新的日志段边界。
EVENT_LID:标记没有逻辑子树映射的日志的开始。
日志段
MDS 日志由逻辑段组成,在代码中称为 LogSegments。这些段用于将多个事件的元数据更新收集到一个逻辑单元中以进行修剪。每当日志尝试提交元数据操作时(例如,将文件创建作为 omap 更新刷新到 dirfrag 对象),它都会以来自 LogSegment 的可重放更新批次进行操作。如果 MDS 在对各种元数据对象进行一系列更新期间发生故障,则更新必须是可重放的。批量执行更新的原因是为了对同一元数据对象(dirfrag)进行分组更新,其中多个 omap 条目可能在同一时间段内更新。
一旦修剪了一个段,它就被认为是“已过期”。当日志记录器将所有更新刷新到支持的 RADOS 对象时,过期的段就有资格被删除。这是通过更新日志记录器的“过期位置”以推进到过期段的末尾来实现的。一些过期的段可能会保留在日志中,以在 MDS 重启时提高缓存局部性。
在 CephFS 的大部分历史中(直到 2023 年),日志段由子树映射(ESubtreeMap 事件)划分。主要原因是日志恢复必须在重放任何其他事件之前从子树映射的副本开始。
现在,日志段可以由作为 SegmentBoundary 的事件划分。这些包括 ESubtreeMap、EResetJournal、ESegment (2023) 或 ELid (2023)。对于 ESegment,这种轻量级段边界允许 MDS 不那么频繁地记录子树映射,同时保持日志段较小以保持修剪事件较短。为了保持日志重放看到的第一个事件是 ESubtreeMap 的约束,那些以该事件开头的段被认为是“主要段”,并且添加了一个新的约束来删除过期段:日志的第一个段必须始终是主要段。
ELid 事件的存在是为了将 MDS 日志标记为“新”,其中需要逻辑 LogSegment 和日志序列号才能继续进行其他操作,特别是 MDSTable 操作。MDS 在创建或关闭级别时使用此事件。从这个初始状态重放级别时不需要子树映射。
配置
日志段的目标大小(以事件数计)由以下参数控制
- mds_log_events_per_segment
MDS 日志段中的最大事件数
- 类型:
uint- 默认值:
1024- min:
1
自上次主要段以来的次要 mds 日志段数由以下参数控制
- mds_log_minor_segments_per_major_segment
自上次主要段以来的次要 mds 日志段数,之后将启动/记录主要段。
- 类型:
uint- 默认值:
16- min:
4
这控制了 MDS 修剪过期日志段的频率(值越高,MDS 更新用于修剪的日志过期位置的频率越低)。
目标最大段数由以下参数控制
- mds_log_max_segments
在开始修剪之前,日志中的最大段数(对象)。设置为
-1以禁用限制。- 类型:
uint- 默认值:
128- min:
8
由于非主要段等待修剪直到下一个主要段,MDS 通常会略高于此数字。