「核心」Ceph学习三部曲之八:分布式文件系统CephFS

Ceph分布式文件系统:CephFS

​ Ceph文件系统或CephFS是在Ceph的分布式对象存储RADOS之上构建的POSIX兼容文件系统。CephFS致力于为各种应用程序提供最新,多用途,高可用性和高性能的文件存储,包括传统用例(如共享主目录,HPC暂存空间和分布式工作流共享存储)。

​ CephFS通过使用一些新颖的架构选择来实现这些目标。值得注意的是,文件元数据与文件数据存储在单独的RADOS池中,并通过可调整大小的元数据服务器MDS集群提供服务,该集群可扩展以支持更高吞吐量的元数据工作负载。文件系统的客户端可以直接访问RADOS来读写文件数据块。

通过MDS集群协调对数据的访问,该集群充当客户端和MDS共同维护的分布式元数据缓存状态的授权机构。每个MDS都会将对元数据的改变写入到日志中。MDS不会在本地存储任何元数据状态。此模型允许在POSIX文件系统的上下文中客户端之间进行连贯且快速的协作。

CephFS基于MDS对元数据进行管理。它有如下特点:

  • 采用多实例消除性能瓶颈和提高可靠性;

  • 采用大型日志文件和延迟删除日志机制来提升元数据的读写性能;

  • 将Inode内嵌至Dentry中来提升文件索引效率;

  • 采用目录分片来重新定义命名空间的层次结构,并且目录分片可以在MDS实例之间动态迁移,从而实现细粒度的流控和负载均衡机制

在正式介绍MDS前,我们先了解以下文件系统基础知识。

0 了解文件系统

常见的本地文件系统有:Ext2/3/4,XFS,BTRFS,FAT,NTFS;常见的网络文件系统有NFS,CIFS。随着互联网快速发展,数据规模越来越大,传统的文件系统已经不能满足要求。分布式文件系统应运而生,比如CephFS、Lustre、HDFS、GFS、GlusterFS。

为了适配不同的文件系统,Linux采用VFS(虚拟文件系统)。

例如用户写入一个文件,使用POSIX标准的write接口,会被操作系统接管,转调sys_write这个系统调用(属于SCI层)。然后VFS层接受到这个调用,通过自身抽象的模型,转换为对给定文件系统、给定设备的操作,这一关键性的步骤是VFS的核心,需要有统一的模型,使得对任意支持的文件系统都能实现系统的功能。这就是VFS提供的统一的文件模型(common file model),底层具体的文件系统负责具体实现这种文件模型,负责完成POSIX API的功能,并最终实现对物理存储设备的操作。

img

VFS在系统中的位置如下图。

1 文件系统中的元数据

VFS为了适配不同类型的元数据,定义了4种基本类型:

  • $Superblock$:用于管理某一类文件的系统信息;
  • $Inode$:索引节点。类Unix文件系统中的一种数据结构,每个Inode保存文件系统中的一个文件系统对象的概要信息,但不包括文件名和文件内容本身Inode和文件名是一对多的关系
  • $Dentry$:目录项。是一个内存结构,由文件系统在内存中直接创立,它是类Unix系统的某个Inode的链接,包含了文件名、文件的Inode号等信息;
  • $File$:文件操作句柄,和进程相关,表示一个打开的文件,File和Inode之间是一对多的关系,因为多个进程可以打开一个文件。

由于Inode和Dentry需要后端文件系统提供服务功能,需要重点介绍:

具体而言:

$a$. Inode只记录数据块在存储介质上的位置和分布,以及文件对象属性(包括权限、数据块信息、时间戳等),不包括文件名和内容等变长数据。Inode结构大小固定,但我们发现要找到具体的存储位置,还缺少文件在目录树中的位置信息,因此需要引入Dentry。

$b$. Dentry在文件系统中起到链接不同Inode的作用。Dentry包含文件名、文件名Inode等信息。它们最终组成文件目录树的结构。它的原理是:本级的Dentry记录了本级目录或者文件名以及下一级目录或者文件的Dentry位置,此外,Dentry本身也需要在具体的Inode对象中,所以它也有自己的Inode号。Dentry本身像一张表,记录了Inode和文件名的对应关系。下面这张图很好的表示了inode和dentry的关系。

img

图片来源:https://www.cnblogs.com/mister-lv/p/6028836.html

2 硬链接和软链接

推荐阅读:什么是软链接,硬链接

为了实现文件共享,隐藏文件路径、文件安全和节省存储空间等功能或特性,需要引入链接这个概念。

硬链接

硬链接指多个文件名指向同一个Inode号(在MDS就是多个Dentry指向一个Inode),可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是,删除一个文件名,不影响另一个文件名的访问(只有一个Inode的所有文件都被删除时,文件才会真正被删除),注意目录不能用于创建硬链接(防止形成环)。目录中的隐藏的.和..就是典型的硬链接。

ln 源文件 目标文件 //创建硬链接
ls -li //查看链接

软链接

软链接是创建一个新的Inode,Inode的存储内容是另外一个文件路径名的指向,可以理解为windows的快捷方式。它的特点是可以灵活地实现诸多不做限制的要求:既可以对存在或者不存在的文件和目录创建软链接,也可以链接到不同的文件系统,还可以在删除链接时不影响指向的文件等。

ln -s 源文件 目标文件 //查看软链接
ll {文件名} // 查看所有链接
软链接创建好了,我们来看看怎么删除它

正确的删除方式(删除软链接,但不删除实际数据)

rm -rf  ./test_chk_ln

错误的删除方式

rm -rf ./test_chk_ln/ (这样就会把原来test_chk下的内容删除)

题外话:讲个笑话,各位小朋友千万不要模仿(除非你想删库跑路)!

扩展:

linux .so .o .a文件区别

3 日志

日志是一种特殊的文件,用于循环记录文件系统的修改,并定期提交到文件系统进行保存。一旦系统发生崩溃,日志可以起到存档点的作用。

常见的日志设计模式无外乎以下三种:

(1)writeback

writeback模式只有元数据被写入到数据中,这样虽然可以保证日志一致性,但是可能引起日志崩溃。

(2)ordered

ordered模式也是只将元数据写入到日志,但是前提是数据已经写入数据盘。但是在数据写入硬盘后而元数据写入前系统崩溃的情景,就会发生数据丢失。

(3)data

data模式将元数据和数据都写入日志,这样可以最大限度的防止文件系统崩溃所导致的数据丢失,但是因为数据写入两次,性能会下降。

日志提交有两种方式:超时提交和满时提交。超时提交,顾名思义指在规定时间之后,日志会自动同步;满时提交指日志存储空间到达上限才会触发同步。一般文件系统会同时采用这两种模式。


正式部分

$\S1$ CephFS

分布式系统通过多服务器实现负载均衡,为了快速索引元数据,必须将元数据和用户数据进行分离,这是Ceph最杰出的贡献之一。

为了实现负载均衡,有如下几种方式:

(1)静态子树分区

即通过手工的方式进行数据分配,典型的有Sprite、StorageTnak和PanFS。

(2)Hash计算分区法

​ 通过hash计算来分配数据存储位置。这种方式适合数据分布均衡且需要应对各种异常的场景,但不太适合数据分布固定的场景。

(3)动态子树分区

​ 通过实时监控集群节点的负载,动态调整子树分布于不同节点,这是Ceph默认的方式。对于有大量数据迁移的场景不合适。

虽然元数据不适合通过Hash计算的方式,但是为什么CephFS的元数据仍然基于Hash方式?

答案是Ceph元数据访问并不是之间从RADOS获取,而是存在一个元数据缓存区,其中元数据基于动态子树分区的方式进行分配,元数据的存在哪里就不是那么重要。

在此基础,我们得到CephFS的架构图。

为了加快数据访问效率,MDS将热点数据缓存在内存中。

CephFS存在以下三种形式的客户端接口:

(1)CephFS Kernel Object

为内核态接口,它使用mount -t ceph命令将CephFS挂载在操作系统指定目录下。

(2)Ceph FUSE

FUSE的全称是Filesystem in Userspace,即用户空间文件系统。它完全是在内核态实现的。

(3)User Space Client

为直接通过客户端应用程序调用CephFS提供的文件系统接口,比如Hadoop调用CephFS提供的Java文件系统接口实现文件转储。

$\S2 $ Ceph MDS服务器特质

​ 当进程打开文件时,客户端将请求发送到MDS群集。 MDS遍历文件系统层次结构以将文件名转换为文件inode,其中包括唯一的inode编号,文件所有者,模式,大小以及其他每个文件元数据。 如果文件存在并且已授予访问权限,则MDS返回索引节点的数目,文件大小以及有关用于将文件数据映射到对象的分条策略的信息。

​ MDS还可以向客户端发出一项功能,以指定允许哪些操作。 功能包括控制客户端读取,缓存读取,写入和缓冲区写入的能力和安全密钥。 随后的MDS参与文件I / O的工作仅限于管理功能,以保持文件一致性并获得适当的语义。

​ Ceph概括了一系列条带化策略,可将文件数据映射到一系列对象上。 为了避免文件分配元数据的任何需要,对象名称只需组合文件索引节点号和条带号即可。 然后使用CRUSH(见三部曲其一blog)将对象副本分配给OSD。 例如,如果一个或多个客户端打开文件进行读取访问,则MDS会授予他们读取和缓存文件内容的功能。 有了索引节点的数目,布局和文件大小,客户端可以命名和定位包含文件数据的所有对象,并直接从OSD集群读取。

2.1客户端同步

​ POSIX语义明智地要求读取反映先前写入的任何数据,并且写入是原子的(即,重叠的并发写入的结果将反映特定的发生顺序)。 当多个客户端同时使用多个写入器或混合使用读取器和写入器打开文件时,MDS将撤消以前发布的任何读取缓存和写入缓冲功能,从而强制该文件的客户端I / O同步。 也就是说,每个应用程序的读取或写入操作将一直阻塞,直到OSD确认为止,从而有效地将更新序列化和同步的负担与存储每个对象的OSD放在了一起。 当写入跨越对象边界时,客户端会在受影响的对象上获得排他锁(由其各自的OSD授予),并立即提交写入和解锁操作以实现所需的序列化。 对象锁类似地用于通过获取锁并异步刷新数据来掩盖大写操作的延迟。

2.2 元数据动态性

​ Ceph中的文件和目录元数据很小,几乎完全由目录条目(文件名)和索引节点(80字节)组成。 与常规文件系统不同,不需要文件分配元数据-使用索引节点号构造对象名称,并使用CRUSH将其分配给OSD。 这简化了元数据的工作量,并使我们的MDS可以有效地管理非常大的文件集,而与文件大小无关。
我们的设计进一步寻求通过使用两层存储策略来最小化与元数据相关的磁盘I / O,并通过动态子树分区[30]来最大化本地性和缓存效率。

2.3 元数据存储

日志的设计使得在MDS发生故障的情况下,另一个节点可以快速重新扫描日志以恢复故障节点的内存高速缓存中的关键内容(用于快速启动) 并以此重新覆盖文件系统状态。

​ 该策略提供了两全其美的优势:以高效(顺序)的方式将更新流式传输到磁盘,并大大减少了重写工作量,从而可以针对未来的读取优化磁盘上的长期存储布局访问

图1:Ceph根据当前工作负载将目录层次结构的子树动态映射到元数据服务器。 仅当多个目录成为热点时,它们才会在多个节点间以散列形式出现。

2.4 动态子树分区

​ 我们的主副本缓存策略使单个权威MDS负责管理缓存一致性并为任何给定的元数据片段序列化更新。

​ 目前有动态子树分区和静态子树分区两种方式。静态子树分区无法应对动态的工作负载和数据集,而散列则破坏了元数据的局部性和有效的元数据预取和存储的关键机会。

Ceph的MDS集群基于动态子树分区策略,该策略可在一组节点上自适应地分层分配缓存的元数据,如图1所示。每个MDS使用计数器来度量目录层次结构中元数据的统计分布, 它随指数时间衰减。 任何操作都会将受影响的索引节点及其所有祖先上的计数器递增到根目录,为每个MDS提供一个加权树,描述最近的负载分布。 定期比较MDS负载值,并迁移目录层次结构的适当大小的子树以保持工作负载均匀分布。

总结 MDS优势在于

  1. 以较大块对象形式存储减少了元数据的数量;
  2. 每个MDS独立更新自己的日志;
  3. 动态子树分区实现了文件系统的动态负载均衡;
  4. 元数据复制保证了MDS节点之间缓存一致性和MDS失败和超载下,相应元数据被迁移到正常MDS上;
  5. 锁机制保证了元数据复制;
  6. 流量控制解决了大量不可预测的用户的请求。

$\S3$ MDS实现原理

3.1 元数据与Object的关系

CephFS中将Inode编号设置为Object名称。而实际Object通常被设置为固定大小,如果Dentry大小大于这个大小,就需要多个object来保存。一个标准的Object集合是以相同Inode开头加上所有Stripe的Objects,它包括一个完整Dentry信息。

根目录默认Inode编号1,它存储目录下所有文件和目录的Dentry。查询文件的过程也是根据Inode编号从根目录搜索到叶子节点的过程。

需要注意的是,元数据不是直接写入到Object,而是先顺序写入到条带化、固定大小的日志中,再根据落盘政策写入到后端object。

rados -p meta ls//标准元数据集合

3.2 嵌入式Inode和Primary Dentry

为了提升性能,文件系统通常将Inode放置再Dentry附近,这样读取Dentry时可以同时将Inode获取,这便是嵌入式Inode。

在没有硬链接时,Dentry就是Primary Dentry。一般情况下存储Dentry对应Inode只占用一个Object;超过一个Object大小的,其扩展的Object也是存储在一起,依次读就可以获得Dentry和Inode信息,将此信息缓存并使用LRU算法进行淘汰。

3.3 Remote Dentry和Anchor

当存在硬链接时,第一个指向Inode的Dentry被称为Primary Dentry,后续的被称为Remote Dentry。

为了访问Remote Dentry, 人们提出了Anchor,它包括Path,Inode,Parent,Ref.前三者显而易见,Ref是被Inode引用的次数(即2.4节提到的计数器)。当进行目录重命名时,可能会影响整个链上的Inode,此时就需要一个事务来保证整个链上相关的Inode同时进行更新,将旧的Ref计数减少,新的Ref计数删除。如果Ref为0,说明Inode已经没有硬链接,可以从Anchor表删除,对Dentry的增加修改同理。

3.4 日志

MDS日志使用的是混合模式:

1)更新首先会写入到MDS的日志中;

2)将有改变的元数据标记为“dirty”,并在MDS缓存中置为“pinned”。

3)最终修改会更新到具体元数据对象中,但同时也会做延时处理直到从日志中剪掉,这使得日志可以变得非常大(数百兆)。

引入日志是CephFS的巨大创新,它不但能获取延迟回写和分组提交等提升性能的方法。

而且还能减少Dentry的更新:1. 大多数负载,比如多次修改同一个地方,或者临时文件创建和删除,其实在日志并未创建;2. 在日志的生命周期内,对给定Dentry的所有更新都被有效提交,所有这些更新被一次性提交。

日志除了故障恢复还支持在恢复MDS时启用其缓存保存大量热元数据,热数据来自日志,避免从冷缓存(即从后端RADOS中随机读取到缓存中)开始的效率加载而导致的大量等待获取元数据的I/O,从而加快MDS恢复。

每一个MDS会维护一个日志系统,其日志保存了最近创建和修改,但还未提交到Object文件中的内容,日志被切分为固定大小且有序的Object。

日志条目,MDS用它来跟踪元数据的变化信息。日志条目使用Metablob来描述单个元数据更新,每一个Metablob包含一个或多个目录的Fragment ID、Dentry和Inode。

3.5 MDS负载均衡的实现

许多分布式文件系统使用静态子树分区(Static Subtree Partition) 的方式来实现,即固定地将不同层目录树分配到不同的服务器上,这种方法需要人工调整,十分不灵活。

Ceph使用动态子树分区来实现横向扩展,动态子树能够根据负载状况来自动迁移目录树,同时为了实现多个MDS管理目录的负载而支持细粒度分区。Sage Weil重点研究了在MDS缓存中存储元数,并且具备伸缩性和容忍任意MDS节点异常。

3.5.1 目录分区

Ceph扩展了目录层次结构以允许单目录内容被分解为多个片段,称为Fragments。因为Inode和Fragments是一对多的关系,它存储在目录Inode的FragTree结构中,它基于一个内部顶点开始进行2的N次幂分割。如下图,目录树被分割为一个或者多个Fragments,其中树叶是单独的Fragments。

image-20210123211806521

​ 每一个Fragments都通过bitmask值来进行描述,类似ip和掩码。通过哈希文件和在FragTree中查找结果值,来实现目录Inode到Fragments 的映射。由于子树元数据被定义在一组目录Fragment中,所以可以通过在MDS之间迁移Fragment来实现负载均衡,任何Fragments在变大或者繁忙时都能分裂为2^n个子fragment。

3.5.2 子树分区

​ Ceph支持在MDS集群中,任意和自适应的对子树进行分区。在某些情况下,诸如在同一个目录下有多个文件是热点对象,目录定义子树的方式无法进行负载分离,但Fragment的分割功能允许将这些热点文件进行动态分离。

​ 根目录所在的MDS被标记为MDS-0,其它的MDS则根据当前整个集群元数据的负载进行子树分区。下图展现了MDS的缓存结构:

image-20210124193737077

3.5.3 元数据复制

元数据复制的目的在于1. 保证各MDS节点缓存之间的一致性;MDS之间进行元数据复制操作,从而当MDS失败或者负载过重时能将元数据复制到其它正常的MDS上。

3.5.4 锁机制

每个Inode有5种锁状态,每一个控制不同的相关字段,例如:link计数和anchor状态字段,文件所有者模式字段、文件size、文件mtime和fragment字段。

总结一下,MDS负载均衡的实现原理。每个MDS会监视统计Inode和Fragment在缓存中的热度。每个Inode会从读和写来统计热度,而fragment会另外统计readdir操作情况、元数据获取频率、写入到对象存储的频率来统计热度。

在客户端请求时,受影响的元数据计数器会增加,元数据祖先也会受到影响,反过来又影响到复制和迁移的策略。MDS节点之间会定时分析它们的负载水平。如果某个节点负载过高,它将根据热度统计计数器来选择合适的子树进行迁移。

关于迁移,首先,目的MDS将所有需要迁移的子树元数据副本导入进来,然后源MDS通过日志导出事件提交迁移。在源MDS迁移之前,任何其他MDS会接收到要复制子树的元数据的信息,以此来获取被迁移子树的元数据信息。

3.5.5 流量控制

设想有成千上万的客户端想访问任何集群中存储的元数据,如果它们集中在一个MDS,那么很难有效的处理请求。理想的情形是 对非频繁访问的元数据之间到权威MDS获取,而频繁访问的条目分发到多个MDS中。

这样做的关键在于客户端缓存了访问MDS的记录。首先利用客户端开始查询位置元数据的分布情况,然后再客户端访问后会收到MDS回复,即未来获取元数据的MDS编号。对非频繁访问的元数据之间到权威MDS获取,而频繁访问的条目则MDS集群会告知客户端随机到所有MDS中去获取,这样就实现了客户端访问MDS的流量控制。

$\S4$ MDS故障恢复

Ceph MDS元数据基于日志进行故障恢复。日志以segments的方式划分记录,并以子树事件作为segments开始部分,子树事件用于描述MDS在这个时间点对哪些权威子树进行了更改、新增操作。

在日志需要一个新的存储对象来保存新的修改时,Segment会被立即创建,新创建的Segment同样也会被链接到列表管理。当日志空间不够,需要修剪Segment时,则删除整个关联元数据对象Segment,以回收磁盘空间。由于Segment通过链接列表管理,因此MDS通过简单的遍历就可以找到所有相关的Segment,以最小的开销来修剪它的日志。

4.1 故障检测

每一个MDS会定期将心跳信息发送给集群的主Monitor。如果一个MDS超过规定时间未被检测到,会被声明为Down。如果MDS与Monitor交互中没有受到反馈,则会将自己置为无效状态。

4.2 MDS恢复流程

MDS利用RADOS作为共享存储后端,如果一个MDS被标记为Down,那么只需要在另外一个节点上启用新的MDS就可以恢复,包括四个节点,Replay、Resolve、Reconnect、Rejoin。

(1)Replay

这是日志回放阶段,它开始于日志最后一个截断点,每一个日志事件都会进行有序读取,并通过调用replay()方法恢复其状态。下一步进入Resolve状态。

(2)Resolve

这个阶段主要工作是解决跨多个MDS出现权威元数据分歧的场景。每个恢复MDS向其它的MDS广播Resolve信息,包括权威子树信息、失败是导入未知位置子树信息、从属节点向目标节点发起 更新请求等。

(3)Reconnected

恢复中MDS与之前的客户端重新建立链接,并且需要查询之前客户端发布的文件句柄、重新在MDS缓存中创建一致性功能和锁状态。

(4)Rejoin

最后阶段是恢复分布式缓存和锁状态。恢复中的MDS会发送弱Rejoin信息到所有MDS节点上,告诉其它节点本MDS恢复了哪些MDS信息;而正常MDS会发送强Rejoin信息到恢复节点,即自己拥有哪些元数据副本信息以及正确的锁状态信息。


部署和操作CephFS

一个Ceph文件系统至少需要两个RADOS池,一个用于数据,另一个用于元数据。配置这些池时,您可以考虑:

  • 对元数据池使用更高的复制级别,因为此池中的任何数据丢失都可能导致整个文件系统无法访问。
  • 对元数据池使用较低延迟的存储(例如SSD),因为这将直接影响在客户端上观察到的文件系统操作的延迟。
  • 用于创建文件系统的数据池是“默认”数据池,并且是用于存储所有inode追溯信息的位置,用于硬链接管理和灾难恢复。因此,在CephFS中创建的所有inode在默认数据池中至少具有一个对象。如果为文件系统计划了擦除编码的池,通常最好对默认数据池使用复制池,以提高小对象的读写性能以更新回溯。另外,可以添加另一个可擦除编码的数据池(另请参见擦除代码),该数据池可用于目录和文件的整个层次结构(另请参见文件布局)。

请参阅以了解有关管理池的更多信息。例如,要使用文件系统使用默认设置创建两个池,可以运行以下命令:

$ ceph osd pool create cephfs_data
$ ceph osd pool create cephfs_metadata

通常,元数据池最多具有几GB的数据。因此,通常建议使用较小的PG数量。实际上,大型群集通常使用64或128。

注意:

文件系统,元数据池和数据池的名称在集合[a-zA-Z0-9_-。]中只能包含字符。

创建文件系统

创建池后,可以使用以下命令启用文件系统:fs new

$ ceph fs new <fs_name> <metadata> <data>

例如:

$ ceph fs new cephfs cephfs_metadata cephfs_data
$ ceph fs ls
name: cephfs, metadata pool: cephfs_metadata, data pools: [cephfs_data ]

创建文件系统后,您的MDS将能够进入活动状态。例如,在单个MDS系统中:

$ ceph mds stat
cephfs-1/1/1 up {0=a=up:active}

创建文件系统并且MDS处于活动状态之后,就可以挂载文件系统了。如果创建了多个文件系统,则将选择安装时使用的文件系统。

如果创建了多个文件系统,并且客户端在挂载时未指定文件系统,则可以使用ceph fs set-default命令控制它们将看到的文件系统。

向文件系统添加数据池

请参阅将数据池添加到文件系统

在CEPHFS中使用擦除编码池

您可以将擦除编码池用作CephFS数据池,只要它们启用了覆盖即可,操作如下:

ceph osd pool set my_ec_pool allow_ec_overwrites true

请注意,仅当OSDS与BlueStore后端一起使用时才支持EC覆盖。

您不得将擦除编码池用作CephFS元数据池,因为CephFS元数据是使用EC池无法存储的RADOS OMAP数据结构存储的。