3.3.1 MDS设计原理
1. 元数据部署设计
CephFS采用数据I/O路径与元数据I/O路径分离的全分布式设计模型,图3-32所示为 CephFSI/O模型,由 MDS集群管理文
件元数据,元数据和文件数据都存储在底层 RADOS 对象(system-object)中,这样可以充分利用 RADOS的容灾特性,大大简化MDS的容灾和集群设计。
(1) MDS元数据存储
前面提到 MDS元数据存储于 RADOS对象中,那么这里涉及一个重要的问题:MDS元数据(inode、dentry)是如何存储在 Object中的?
在 RADOS中,Object代表一个完整而独立的数据,每一个 Object 都可以通过全局唯一的对象标识(Object ID,OID)来进行标识。因此,如果将 inode编号设置为 Object的 OID,就可以顺利解决这个问题,即可实现MDS根据元数据信息(inode编号)在RADOS中查找定位。如图3-33所示,元数据实际上是以目录为单位存储于 RADOS对象中的,根目录默认的inode编号为 1,即标识为1的 Object中存储了根目录下所有文件和子目录的dentry信息(这里要注意,子目录为当前父目录下的同一层级的目录)。dentry 对象中记录了文件和子目录的名称,以及对应的inode编号。当查询的元数据对象为目录时,则根据 inode编号可以直接找到下一级目录下记录文件和目录的 dentry信息位于哪个 Object中,然后继续进行查找;当查询的元数据对象为 regular文件时,则根据inode编号可以找到存储文件实际内容的 Object地址,完成元数据查找。
图 3-32CephFS1/o模型
图 3-33 元数据存储结构
通常兼容 POSIX标准的文件系统,如Linux的 vfs文件系统,将 inode和 dentry设计为独立的对象,这主要出于 dentry与 inode 并非总是一对一关联的原因,如硬链接场景。在CephFS中,出于对元数据处理性能的考虑,将 inode与 dentry 对象进行结合存储,放置于 Object中,这样,一次读取即可获取 dentry和 inode信息。因此,这种与 RADOS存储方式的良好结合,能达到较优的元数据续写效率,如能较快地加载目录内容。
(2) MDS元数据集群
CephFS作为一个存储 PB 级数据的分布式文件存储系统,它充分考虑了对元数据服务器的要求,设计了MDS 集群来集中管理文件元数据,支持主备和多活模式,本文主要介绍MDS集群多活模式。
为了方便集群扩展,多活 MDS必须对整个文件系统的元数据管理进行分区,如图3-34所示。为此,CephFS 引入了子树分区设计,将这个文件系统目录树分区成子树,分布到各个 MDS中,当MDS的 I/O负载不均衡的时候,通过子树迁移来平衡MDS间的负载。目前 MDS支持的分区有两种方式:静态子树分区和动态子树分区。
图 3-34 子树分区
◆ 静态子树分区
通过手动分区的方式,固定地将不同层级的目录子树分配到各个MDS上,当 MDS间的负载出现不均衡时,再手动调整,进行 MDS 间的子树迁移来修正负载均衡。可以看出,各个 MDS 节点负载和子树的位置都是静态固定的,很显然,这种方式拥有很好的元数据定位能力,能较好地适用于数据位置较为固定的场景。但随着业务量不断扩大,集群规模需 要扩展时,还需要手动重新分配子树到新增的 MDS 节点,因此元数据负载均衡能力较差。
◆ 动态子树分区
根据MDS集群各节点的负载情况,动态地调整子树分片分布到不同的 MDS节点上,达到整个集群的负载均衡。负载较高的 MDS 节点根据本地目录的热度决定迁移子树的大小,将部分子树迁移到负载较轻的 MDS节点上。该方式提供了较好的元数据负载均衡能力, 适用于较多的业务场景,特别是那些元数据迁移量较小的场景,也是 CephFS默认的分区方式。
2. MDS负载均衡
动态子树分区如何实现负载均衡呢?这里涉及两个重要的过程:负载热度计算和子树迁移,前者触发后者,由后者最终完成负载均衡动作。
在深入介绍 MDS负载均衡之前,首先介绍一下 CephFS中一项重要的技术:目录分片。如果子树按照目录粒度进行分区,并不能解决超大目录(即一个目录下存在大量文件
和子目录)或者热点目录(即一个目录下存在多个热点文件)的负载分离问题。为了解决这个问题,CephFS 引入了目录分片技术,即扩展了目录层次结构,将单个目录分解为多个目录分段(fragment)。这样,在目录内容较多的情况下,可以将目录动态地分割为多个分片,实现目录分片粒度的负载均衡。从图3-34也可以看出,fstab文件以分片的粒度从 etc目录中分离,迁移到 MDS2 中。目录分片技术还有一个优点,即能加速超大目录的预读加载,保证客户端能快速访问目录内容,例如 readdir过程。
(1)负载热度计算
与 inode 和目录分片关联的热度计数器记录了缓存元数据的受欢迎程度,其中,inode元数据记录 read和write 两种操作热度计数,目录分片(fragment)还会记录 readdir操作的热度计数,以及元数据从 RADOS 读出和写入的热度。除了维护其自身的热度之外,每个目录分片还维护 3个额外的负载向量。
◆ 向量 1 记录当前子树所有嵌套元数据的热度;
◆ 向量 2 记录当前节点权威元数据的所有嵌套元数据热度;
◆ 向量 3 记录当前节点权威子树的元数据热度。
MDS 集群节点之间会定期发送心跳消息来共享它们的总体热度负载情况,以及每个子树的直接祖先管理元数据的累积热度,每个 MDS 可以根据这些信息计算出自身热度与平均热度之间的关系来确定是否需要迁移元数据,同时又能确定需要迁移的合适的子树元数据。
(2)子树迁移
本质上,MDS仅作为一个内存数据缓存池,因此MDS 之间动态迁移的是缓存在内存中的子树元数据。子树从源 MDS节点迁移到目的MDS节点,大致可分为 4个步骤,具体如下。
1)discover阶段
源 MDS节点将需要迁移的目录元数据发送到目的 MDS节点缓存。2)prepare阶段
目的MDS节点将迁移的目录子树进行冻结,阻塞客户端对该目标子树的所有I/O请求。3)export阶段
源 MDS 节点将目标目录下的内容(子目录和文件)元数据发送给目的 MDS节点缓存。4)finish阶段
目的MDS节点解冻目录子树,更新客户端目录-MDS映射关系,处理被阻塞的I/O请求,以便客户端可以继续对文件进行操作。