Sage A. Weil Scott A. Brandt Ethan L. Miller Darrell D. E. Long Carlos Maltzahn
我们开发Ceph,一个分布式文件系统,它提供了优秀的性能、可靠性和可伸缩性。Ceph通过用一个伪随机数据分布函数(CRUSH)替代分布 表来最大化的分离数据与元数据管理,这个算法用于异构和动态不可靠的对象存储设备(OSD)集群。我们利用设备上半自治的OSD智能分布数据副本,故障检 测和恢复,这些OSD运行专门的本地对象文件系统。动态分布元数据集群提供了非常有效的元数据管理并无缝地适应各类文件系统的工作负载。多种工作负载下测 试显示, Ceph具有良好的I / O性能和可扩展的元数据管理,支持超过每秒250000次元数据操作。
系统设计者一直试图提高文件系统的性能,文件系统的性能直接影响应用的整体性能。科学和高性能计算社区是推动分布式存储系统的性能和可伸缩性的 主要力量, 他们会几年预测一下通用需求。传统的解决方案(比如NFS),以提供一个简单的模型,其中服务器端export文件系统,客户可把它映射到本地user- space。虽然被广泛使用,但集中式的客户机/服务器模型已被证明是可扩展性的一个重要障碍。
最近越来越多的分布式文件系统采用了基于对象的存储架构,智能对象存储设备(OSD)取代传统硬盘, OSD可将CPU、网络、本地缓存与底层磁盘或RAID这些资源整合。OSD用大得多的读写字节大小范围(往往大小不等)的对象取代传统的块接口,利用设 备本身负责底层块分布。客户通常与元数据服务器交互(MDS)执行元数据操作(open、rename),而直接与OSD交互负责执行文件I / O(read,write), 显著改善整体的可伸缩性。
由于很少或根本没有分布元数据工作负载,系统采用这种模式仍然有可扩展性限制。继续依赖传统文件系统的分布列表和inode表或不把智能利用到OSD上会进一步限制系统可扩展性、性能、可靠性和成本。
我们提出Ceph,这个分布式文件系统提供优秀的性能和可靠性,同时承诺优秀的可扩展性。我们的架构是基于PB级别以及本质上动态的假设,动态的定义:大系统不可避免地逐步建立,节点失败是常态,而非例外, 工作负载随时间不断变化。
Ceph分离数据和元数据操作,通过CRUSH生成代替传统文件系统的文件分布表。这使得Ceph在OSD中利用智能解决数据访问的复杂性,更新 序列化,复制和可靠性、故障检测和恢复。Ceph利用高度自适应分布式元数据集群,显著提高元数据访问的可扩展性,通过它也提高整个系统的可扩展性。通过 讨论目标和一些工作负载的假设促使我们选择合适的架构设计,分析这些设计对系统的可扩展性和性能的影响,并用我们的经历来实现一个系统原型。
图1
Ceph文件系统有三个主要组件:客户端:暴露near-POSIX的文件系统接口给主机或进程; OSD集群:存储所有数据和元数据; 元数据服务器集群:管理名空间(文件名和目录),协调安全与一致性(见图1),说Ceph接口为near-POSIX在于,它为了更好地结合应用的需要和 提高系统的性能,它扩展了POSIX接口,并可有选择地放松一致性语义。
此架构目标是可扩展性(数百PB甚至更多),性能和可靠性。可扩展性包括整个系统的存储容量和吞吐量以及每个客户端,目录或文件的性能。我们的目标工作负 载可能包括数万或数十万host并发读取或写入同一个文件或在同一目录中创建文件。这样的场景常见于超级计算集群上运行的科学计算应用程序,今天的超级计 算也就是明天的通用工作负载。更重要的是,我们设定的情景是:分布式文件系统工作负载本质上是动态的,会有大的数据变化和动态的元数据访问,和随时间变化 的数据。Ceph直接解决扩展问题, 同时实现高性能、可靠性和可用性,是通过三个基本设计实现的:分离数据和元数据,动态分布式元数据管理和可靠的自动分布的对象存储。
分离数据和元数据
Ceph最大化的分离文件元数据管理和文件数据存储。元数据操作(open、rename等)由元数据服务器集群管理,而客户可直接通过OSD 执行文件I / O(读和写)。基于对象存储可很好的改善文件系统的可扩展性,可小块分配数据到存储设备。相比现有基于对象的文件系统取代长的文件块列表为对象列表, Ceph中完全消除分配列表的设计。相反,文件数据条带化到可计算到的命名对象, 是通过CRUSH算法分配对象存储设备。这样可通过一个文件计算(而不是查找)得到对象的名称和位置, 可避免维护和分发对象列表, 简化系统的设计, 并减少了元数据集群工作负载。
动态分布式元数据管理
因为文件系统元数据的操作占据典型文件系统一半的工作负载,所以有效率的元数据管理肯定能提高系统整体性能,Ceph利用了一个新的元数据集群 架构,基于动态子树划分,它适应性的智能的分配职责,可在十个甚至上百个MDS上管理文件系统目录结构,一个动态的层次分明的分区在每MDS工作负载中被 保留位置,可促进有效更新和预取,可共同提高工作负载性能,值得注意的是,元数据服务器的负载分布是基于当前的访问状态,使Ceph能在任何工作负载之下 有效的利用当前的MDS资源,获得近似线性扩展性能
可靠的自动分布的对象存储 RADOS
由上千台设备组成的大系统是动态的: 他们数量慢慢增加,新存储加入,旧的存储舍弃,设备的故障也会频繁发生,同时大的数据块被创建,迁移和删除,所有这些影响因素都需要分布式数据能够有效的 利用现有资源并维持所需水平的数据备份,Ceph承担着对存储数据的OSD集群进行数据迁移,备份,故障检测,故障修复的责任。OSD可提供独立的逻辑对 象存储给客户端和元数据服务器,这样使得Ceph能更加有效的利用计算资源处理能力(CPU和内存),以使每个OSD实现可靠、高可用性的线性扩展性能的 对象存储。 接下来会描述Ceph客户端,元数据服务器和分布式对象存储的操作和他们怎样被Ceph架构中优秀特性所影响,我们也会描述Ceph原型的 现状。
通过描述客户端操作可介绍Ceph的各个组件的操作以及这些组件与应用程序的交互。Ceph客户端跑在每个主机上,这些主机上会跑应用,客户端 会给予应用一个文件系统接口,在Ceph原型中,客户端的代码运行在用户空间,并且可以通过直接Link到它或者利用FUSE【一个用户态的文件系统接 口】把它作为一个挂载点文件,每个客户端包含它自己的文件数据缓存,独立于内核页或缓冲区缓存,可被客户端上的应用程序直接使用。
当一个进程打开一个文件,客户端发送一个请求给MDS集群,MDS转换文件名为文件inode,这个inode包含唯一的inode号、文件所 有者信息,模式,大小和其他的per-file元数据,如果文件存在并且访问被允许,MDS会返回inode号、文件大小以及文件数据到对象的条带化映射 策略的相关信息,MDS可能给客户端一个权限(如果之前没分配),这个权限指明何种操作被许可,现在权限包含四种:分别控制客户端的读,缓存读,写,缓存 写,将来可能包含加密钥(让客户向OSD证明他们被授权去读写数据『现在的原型相信所有的client)。此外,MDS还会管理文件的一致性语义。
Ceph用一系列条带化策略管理文件数据到对象序列的映射,为了避免任何文件分布的元数据, 对象名称简单地由文件inode编号和条带编号结成,然后对象副本利用CRUSH算法分布到OSD集群,举例来说, 当一个或多个客户端打开一个文件以进行读访问, MDS负责授权客户端读和缓存文件内容。当拥有inode号、文件格式和文件大小, 客户端能够得到包含文件数据的所有对象的名称和位置,并且是直接从OSD集群读取的。任何对象或字节范围如果不存在就被定义为“文件洞”, 或空。类似地, 如果一个客户端打开一个文件进行写操作,则被授权buffer写,它在文件中任何字节生成的任何数据都会被写入适当的OSD中的对象上,客户端不再关闭文 件,而是提供给MDS新的文件大小(最大的写入字节数)。
POSIX语义要求读先于写,写是原子写(如: 内容的覆盖,并发写会有顺序问题),当一个文件被打开,被多个客户端写或者读+写,MDS将撤销之前的问题读缓存和写缓冲,强制客户端进行文件同步 I/O,每个应用读与写操作都会阻塞直到被OSD授权,OSD存储对象能够有效的分担更新序列化和同步的负载。当一个写跨越一个对象边界,客户端获得所有 受影响对象的独占锁(相关OSD赋予他们的), 立刻提交写和解锁操作去达到序列化要求,对象锁也用于掩盖需要锁和大型异步写的延迟。
毫不奇怪,同步I/O对于应用来说是一个性能的杀手,尤其进行小文件的读写操作时,因为存在至少一个到osd的来回的延迟消耗,尽管读写共享在 平常操作中比较少,更多时候用于科学计算,在这些地方性能表现很好,因为这个原因,当应用对一致性要求不高时往往可放松一致性语义,Ceph也实现了放松 一致性语义,可以通过全局配置项配置,其他文件系统也做了这个设计, 但这个设计确实不是一个很好的方案。
因为这个原因, POSIX上一些高性能计算的扩展被HPC社区应用(高性能计算), Ceph实现了其中一个子集。重要的是, 这些扩展包含一个O LAZY标签, 这个标签表示是否允许放松共享文件的一致性要求。追求性能的应用会管理一致性(例如,通过写入相同文件的不同部分, 一个在HPC中的普遍模式),然后I/O同步可实现缓冲写和缓存读同时进行,如果需要,应用可通过以下两个调用来做显式同 步:lazyio_propagate刷新数据到对象存储,同时lazyio_synchronize确保之前数据刷新应用到之后的读。Ceph同步模型 因此简易地通过在客户端之间通过同步I/O、扩展应用接口放松一致性语义来提供正确的读写和共享写。
客户端与文件系统名空间的交互由MDS集群来管理,读操作(如 readdir, 文件信息操作stat)和更新操作(如删除unlink,模式修改chmod)由MDS同步应用以保证序列化,一致性,安全。为了简单,没有元数据锁提供 给客户端,尤其对于HPC工作负载,回调好处很小,相反,会带来很高的潜在复杂性。
Ceph优化了大多数通用的元数据访问场景,一个readdir操作后紧跟着一个对每个文件的stat(例如ls -l)是一个特别普遍的访问模式,也是一个在包含很多文件的大目录中臭名昭著的性能杀手的操作。一个readdir操作在Ceph中仅需要一个MDS请 求,它会获取文件夹目录,包括inode内容,默认情况下,如果一个readdir后跟着一个或多个stats操作,会提供一个暂时的缓存返回,否则缓存 会被舍弃,尽管一个此时inode的修改不会被发现,一致性稍微减弱,但换来性能的提高还是值得的,这一切由文件系统扩展接口readdirplus扩展 实现,它会通过目录入口返回lstat结果(就像一些OS中getdir已经实现的)。
Ceph可以通过缓存元数据时间更久来使得一致性被进一步的消弱,有点像早期版本的NFS,它的缓存时间达到30秒,然而,这个方法破坏了一致 性,有时一致性对应用很重要,比如利用stat去判断一个文件是不是被更新,这时会发生错误或者等待旧的缓存数据直到timeout。
这时我们选择再次纠正操作行为和扩展接口,下面通过一个文件上的stat操作讲解,这个文件同时被多个客户端进行写操作,为了返回一个正确的文 件大小和修改时间,MDS要立刻停止文件更新并从所有写操作中收集最新的大小和修改时间值,通过stat返回最高值。尽管停止多个写看起来很不可思议,但 为了保证序列化也是必要的,(对于一个单一的写,一个正确的值可通过写客户端得到,不用中断别的进程),对于不要求一致性行为的应用,POSIX接口不符 合他们的需求,可以使用statlite,他会利用一个bit去指出某个inode不需要保持一致性。
元数据的操作通常要占一半的操作时间,并且位于关键路径上,所以使得MDS集群对于整体性能很重要,MDS同时也面临分布式文件系统中扩展性的 挑战,尽管性能和I/O在存储设备任意增加时可被度量,但元数据操作需要更大程度的相互依存关系, 使MDS扩展时的一致性和一致性管理更加困难。 文件和目录元数据在Ceph中很小,几乎完全由目录入口(文件名)和inode(80字节)组成。与传 统的文件系统不同, Ceph中不需要文件分布元数据,对象名称使用inode号, 并通过CRUSH算法分布到OSD。这简化了元数据的工作负载, 并允许MDS有效管理一个非常大的文件集合(不考虑文件大小)。我们的设计通过使用一个双向的存储策略,和通过动态子树分区最大化的本地化和缓存效率进一 步减少元数据相关的磁盘I / O。
虽然MDS集群旨在满足大多数内存缓存的请求, 但为了安全,元数据更新必须提交到磁盘。每个MDS快速将一组大、有界、懒刷新日志的更新元数据分布式上传至OSD集群。per-MDS日志,每个几百兆 字节, 也存储重复的元数据更新(对多数场景很常见), 这样当旧日志最后刷新到长期存储,许多已经变得过时了。虽然在我们的原型中MDS恢复还没有实现, 但是MDS失败时的日志设计已经实现, 另一个节点可以快速重新扫描日志去恢复失败节点的内存缓存的关键内容(为了快速启动), 这样得以恢复文件系统。
这些元数据管理策略提供了最好的两个状况:通过有效(顺序的)的方式提交更新到磁盘;以及大大减少重写工作负载, 使长期磁盘存储布局优化以应对未来的读访问。特别是, inode在目录中被直接嵌入,允许MDS通过一个OSD请求预取整个目录, 并利用出现在大多数工作负载中的高深度的目录位置。每个目录的内容作为元数据日志和文件数据将使用相同的条带化和分布策略写入OSD集群。Inode编号 在元数据服务器范围内分布,在我们的原型中被认为是不可变的, 尽管将来他们可能会在文件删除时回收。一个辅助锚表使极少inode有多个硬链接且可寻址的inode号,但这些不妨碍singly-linked文件与 一个庞大而繁琐的inode表的情况。
图2 Ceph基于当前的工作负载来动态映射目录层级子树到元数据服务器,每个目录变为热点时会把元数据哈希到多个节点上
对于任何给定的元数据,primary-copy缓存策略使用一个授权MDS管理缓存一致性和序列化更新。大多数现有的分布式文件系统使用某种 形式的静态子树分区来做这些管理(通常把数据集分成更小的静态“卷”), 最近的一些或者实验用的文件系统已使用散列函数分布目录和文件元数据, 有效地减少载荷分布的本地性。这两种方法主要的缺陷在于:静态子树分区无法应对动态工作负载和数据集,而散列函数方法会破坏元数据的本地性和元数据获取、 存储的有效性。
Ceph的MDS集群是基于动态子树分区策略, 自适应地分配缓存元数据,分层分布在一组节点上, 如图2所示。通过使用计数器统计每个MDS中元数据的访问量。任何操作使得受影响inode及其上层节点直到根目录的计数都增加, 从而提供每个MDS一个权值,来描述最近的载荷分布。定期比较MDS权值, 通过迁移以保证元数据工作负载均匀分布。共享的永久存储和及其名空间锁的结合保证这样的迁移是可行的,转移一些内存缓存的内容到新的节点, 对相干锁或客户端功能影响最小。新导入元数据将写入新MDS的日志中, 同时,还有日志分录新旧MDS以确保迁移是安全的 (类似于两阶段提交)。子树分区保持以最小化前缀复制开销同时也要保护本地性。
跨多个MDS节点复制元数据时, inode内容分为三组,每个都有不同的语义一致性:安全(owner, mode)、文件(size, mtime)、不可变性质(inode number, ctime, layout), 当不可变性质不变, 安全和文件锁被独立的状态机管理,都拥有不同状态集,并且根据不同的访问和更新在不同状态之间转换。例如, onwer和mode用于路径访问的安全检查,很少改变,只需要很少几个状态, 但是客户端多种访问模式的集合, 因为它反应在MDS中, 可以控制客户端的访问能力。
分区的目录层次结构跨多个节点,可平衡工作负载, 但不是总能应付热点问题 (多客户端访问相同的目录或文件)。只有当成为热点时,Ceph会使用元数据分散分布,一般情况下不引起相关开销以及目录本地性的损失。大量读访问的目录 (如,多open)就会被复制到多个节点。目录特别大或要一个大量字节的写(如,多文件创建)将会将其内容利用名称散列算法分布到集群, 均衡分配,代价是失去目录本地性。这个自适应方法允许Ceph分区有范围比较大的粒度, 同时可获取粗和细粒度分区的好处, 这种策略在一些情景和文件系统非常有效。
每个MDS响应为客户端提供涵盖数据和任何有关inode及其祖先的副本的更新信息,允许客户端知道与之交互的部分文件系统的元数据分区。未来 的元数据操作将直接基于给定路径前缀最后部分对主数据(更新时)或一个随机的副本(读取时)操作。通常客户端知道不常访问元数据的位置(没有副本),并可 以直接与MDS交互。当客户端访问访问频繁的元数据时, 元数据在多个MDS节点中, 客户端可知道特定的元数据驻留在哪个的MDS,这样热点问题就不会存在。
客户端和元数据服务器视对象存储集群(包含成千上万的osd)为一个逻辑的对象存储及名空间。RADOS(可靠的自动分布式对象存储)可管理分布式情况下的对象复制,集群扩展,故障检测和恢复,能够达到线性扩展性能。
图3 文件被条带化为多个object,通过CRUSH算法分布到对象存储中
Ceph可分布PB级数据到由上千台设备组成的集群中,从而设备存储和带宽被有效的利用,为了避免负载的不均衡(例如,最新加入的设备很可能没 被使用)或负载不对称(比如,新的访问频繁的数据都放到最新设备上)。Ceph采用一个策略,它随机分配新数据到任意设备,随机迁移一存在数据到新设备, 如果设备删除,也会重新分布数据,这个方法是健壮的,在现有的工作负载中表现不错。
Ceph首先通过简单哈希算法映射对象到配置组(PGs),通过一个自适应的位掩码控制PG数量,选择一个值设定来平衡每个OSD的利用率(近 似100PGs分布这些OSD上), 这个值就是每个OSD上副本相关元数据的数量。放置组然后通过CRUSH算法分配到所有OSD,这个算法是一个伪随机数据分布算法, 能够有效的有序映射每个PG到多个OSD, 这个OSD组会存储对象副本, 这个方法不同于传统方法(包括其他的对象文件系统), 数据放置组不依赖任何的块或对象列表元数据。为了定位每个对象, CRUSH只需要放置组和OSD 的cluster map(非常简单,负责分层描述存储集群中设备组成)。这个方法有两个重要的优点: 第一个, 完全分布式, 任何部分(客户端, OSD或者MDS)都能独立计算object的位置。 第二点,MAP的更新会很少,几乎能够消除分布式元数据的交换。因为这些, CRUSH能够同时解决数据应该存在哪的问题和已存数据在哪的问题,通过设计,存储集群的一些小变化对已存在的PG mapping影响很小,使的因设备故障或者集群扩展导致的数据迁移也很少。
Cluster map结构遵循集群的物理或逻辑组成并能描述可能的故障,举个例子,一个四层架构服务器系统, 由很多OSD,机架柜, 成排的机壳组成,每个OSD有个weight值来衡量它上面的数据量,CRUSH算法映射PG到OSD基于Placement规则, 这个规则定义了副本数和任何其他的placement限制条件, 例如,复制每个PG到三个OSD,每个都在相同的row(限制行间的副本传输),但是会分散到每个机架, 尽量减少接触电源电路或边缘开关故障。Cluster map也包含down或inactive的设备列表和时戳数量, 这个会随着map变化会增加, 所有OSD请求通过客户端的时戳标示,这样会知道现在数据的分布,递增的map更新会被相关的OSD分享,如果客户端的map是过期的,可利用OSD 的回复来判断。
与像Lustre这样的系统相比,Lustre是利用RAID和SAN设备的容错机制来实现可靠的OSD,而Ceph中,我们假设一个PB级或 EB级系统故障是正常发生的, 而不是异常发生的,并且在任何时间都有可能几个OSD变的不可用,为了在可扩展的情况下保证系统可用和数据安全,RADOS使用多个primary- copy管理数据副本,同时用一些步骤来最小化性能影响。
数据以放置组为大小备份,map到n个OSD上(称为n-way副本),客户端完成所有的写到主OSD上的对象PG中(主host),这些对象 和PG被分配新的版本号,然后写到副本OSD上,当每个复制都完成并响应给主节点,主节点完成更新,客户端写完成。客户端读就直接在主节点读,这个方法节 省了客户端的副本之间的复杂同步和序列化,其他方式在存在其他写或故障恢复时是非常麻烦的。Ceph的方案使得副本占用的带宽从客户端转移到OSD集群的 内部网络,OSD内部有更好的网络资源。在这个设计里干预式的错误被忽略, 任何随后的恢复都可可靠的保持副本一致性。
图4:RADOS会响应ack当所有的OSD复制都完成到缓存中, 当全部写入磁盘后会给客户端一个最后的commit通知
在分布式文件系统中,为什么数据要写入共享存储,基本上就两个原因,第一,客户端想让它们的更新对其他客户端可见,这个应该快速,写可见要很 快, 尤其当多个写或者混合读写强制客户端去同步操作时;第二,客户端希望知道写数据是不是被安全的备份了,磁盘是不是正常运行或者发生故障。RADOS 把确认更新时的同步和数据安全分开,让Ceph实现高效更新和良好的数据安全语义。图4描述了对象写中消息传递,主要是转发更新到副本,响应一个ack在 更新到所有的osd的内存缓存中,允许同步的客户端上的POSIX 的call去返回,当数据被安全的写入磁盘, 最后一个commit会响应给client, 也许是几秒之后,ceph发送ack给客户端只要在更新被完全写入副本并能够容忍任何OSD单点故障,尽管这样会增加时延,默认情况下,为了避免断电数据 丢失,客户端也会缓存写直到他们被提交。 这种情况下要恢复的话,要在接受新更新前,回滚到前一次的确认时。
及时的故障检测对保证数据安全非常重要,但是对于扩展到数千个设备的集群来说变得很困难,对于某些故障, 比如磁盘错误或者数据毁坏, OSD可以自己反馈自身状态,当一个OSD网络上不可达,这种情况则需要实时监控,在多数情况下,RADOS让存储相同PG的OSD互相监控,备份之间相 互通路则可确认彼此是可用的, 这种方法没有额外的通信开销,如个一个OSD最近没有收到一个同伴的消息,一个对这个同伴的ping操作会被发出。
RADOS可确认两种OSD活性,是否OSD可访问和是否通过CRUSH算法被分配数据,一个没有响应的OSD会被标记为down,任何作为主 节点的责任(一致性更新, 副本) 会暂时交给它的放置组副本所在的下一个OSD,如果这个OSD没有能快速修复,会被mark out, 同时其他的OSD会加入,复制out OSD上每个PG的内容。客户端到故障OSD的操作会被转向新的主节点。
因为大量的网络异常会导致OSD的连接出现各种问题,一个很小的监控集群会收集故障报告, 并集中过滤瞬间错误或者系统自身的错误(比如网络),监控器(部分被实现了)利用选举、active伙伴监控、短期租用、两阶段提交去集中提供一致并有效 的对cluster map的访问,map更新来反映任何故障和恢复,受影响的OSD提供递增的map更新,并利用现有的inter-OSD信息沟通在集群中扩散消息,分布式 检测可以快速的检测,不产生过高的开支,同时通过集中式仲裁解决矛盾发生。很重要的是,RADOS避免因系统问题导致的大范围的数据重复制, 通过标记OSD为down而不是out(例如因电力问题导致的半数OSD故障)
OSD的cluster map因OSD故障、修复、集群变化(比如新存储设备的部署加入)会变化, Ceph通过相同方法捕获所有这些变化,为了快速的修复,OSD中每个PG包含每个对象verision号和最近的变更日志(名字+更新或删除的对象的 version号)。
当一个活的OSD收到一个更新后的cluster map,它会遍历本地存储的PG并利用CRUSH算法来计算自己对于这个PG是什么角色, 是主节点还是副本节点,如果PG所在的OSD列表发生变化,或者某个OSD刚刚启动,这个OSD则必须与PG所在的其他OSD组成伙伴关系。对于副本 PG, 所在的OSD会提供当前的PG version给主OSD。如果OSD为PG的主节点,它收集现在的(和过去的)副本PG版本号,如果主节点不是最新的PG状态, 它会从PG所在的OSD检索最近PG变化日志(或者一个完全的PG内容概述,如果需要), 这样可以得到最新的的PG内容,主节点会发送给每个副本节点一个新的日志更新(如果需要, 可以是一个完全的内容概述),这样主节点和副本各方都能知道PG的内容。 只有当主节点决定了正确的PG内容并共享它,然后才能通过副本I/O到对象,OSD然后从它的伙伴节点检索丢失或过期的对象, 如果一个OSD检索到一个到一个老对象或者丢失的对象的请求, 它会先延时处理然后迁移这个对象到修复队列的前面。
举例来说, 假设OSD1崩溃,并标记为down, OSD2接过PG主节点的责任,如果OSD1修复好了,监控器会把它标记为up, 当OSD2收到新的cluster map更新,他会发现它现在不在是PG的主节点了, 这时会把PG的最新version发给OSD1. OSD1将会检索最近的PG的日志, 告诉OSD2它的内容没问题, 然后开始处理请求, 同时所有更新的对象在后台被恢复。 因为故障修复由独立的OSD驱动,每个被故障OSD影响的PG将会于(非常近似)替代OSD建立时修复, 这个方法基于快速修复机制(FaRM), 减少了修复时间并提高了整体数据安全。
尽管大量的分布式文件系统利用本地文件系统,比如ext3管理底层存储,我们发现它们的接口和性能很难满足对象存储的工作负载,已有的 kernel接口限制了我们理解什么时候对象更新被安全提交到磁盘。同步写或者日志可提供安全性,但以高延时和性能下降为代价。重要的是,POSIX接口 不支持数据和元数据(例如数据属性)的自动更新事务,这个对于保持RADOS的数据一致性很重要, 作为替代方案, 每个Ceph中OSD通过EBOFS管理它的本地对象存储,EBOFS为一个Extent和B-树的对象文件系统,在用户空间实现,并且直接和raw格式 的块设备打交道,并允许我们定义我们自己的底层对象存储接口和更新语义,他把更新序列化(为了同步)从磁盘提交中(为了安全)独立出来。 EBOFS支 持自动事务(例如,在多个对象上写和属性更新) ,支持当内存中缓存被更新时更新返回,同时提供同步提交通知。用户空间的方法,除了提供更好的灵活性和更简单的实现, 同时也能避免与linux VFS和页缓存的笨重交互, linux VFS和页缓存是为不同的接口和工作负载设计的。当多数内核文件系统延时写更新到磁盘,EBOFS积极安排磁盘写,当后面的更新重写它们时,选择而不是取 消等待的I/O操作,这会给我们的底层磁盘调度器带来更长的I/O queue和调度效率的提高,用户空间的调度器也能选择最先优先级的工作负载(例如客户端I/O恢复) 或提供qos质量保证。
EBOFS设计是一个健壮,灵活并且完全集成B-tree服务,它被用于定位磁盘上对象, 管理块分布和收集索引(PG放置组), 通过开始位置与长度对管理块分配,替代了块列表方式, 使得元数据紧凑。磁盘上的空闲块盘区按大小存入,并按照位置排序,可以使EBOFS在写位置或者相关磁盘数据附近快速定位空闲空间, 同时也限制长的碎片。除了对象块分配信息,为了性能和简单,所有的元数据都存在于内存中,即使是很大的数据元数据也是很小的。最后EBFOS采用写时复 制,除了超级大块的更新, 数据总是写入未分配的盘空间中。
我们通过一系列工具来评估性能,可靠性和可扩展性。 测试中,客户端, OSD和MDS都跑在双核的linux集群上, 使用的是SCSI磁盘,通过TCP协议通信,通常情况下, 每个OSD和MDS都跑在自己单独的host上,这样上百个客户端应用都可以共享这个host,这样能更好的测试。
EBOFS能够提供很好的性能和安全,同时通过CRUSH算法形成平衡分布的数据、副本,能够使得总的I/O性能随着OSD集群的大小变化而变化。
下面计算I/O性能是通过一个14节点的OSD集群来测试的, 图5显示了每个OSD在不同写入块大小和副本数下的吞吐, 工作负载由20个节点上的400个客户端应用组成,性能最后被磁盘读写带宽限制(58MB/s)所限制, 副本数为2或3时, 磁盘I/O会增大到二到三倍,当osd数固定,副本的增加会降低客户端的数据吞吐。图6比较了使用EBOFS和使用其他常用文件系统 (ext3,ReiserFS,XFS)的性能。客户端同步写入大文件,以16MB大小条带化, 然后读取。 尽管小的读写性能会有影响,EBOFS还是能最大使用现有的磁盘带宽, 大于32KB时, 读显著的胜过其他文件系统,这些测试都是在一个干净的文件系统上测试的,早期的EBOFS设计经验说明它会比ext3更少的存储碎片,但是现在还是没法评 估这个, 希望将来EBOFS的表现不会变差。
图5 OSD写性能,横线表示物理磁盘的写上限,副本数对osd吞吐的性能影响很小, 当OSD数目固定,Nx复制会降低总的吞吐效率,因为复制数据会写入n个OSD
图6 EBOFS与通用文件系统的使用对比,尽管小块文件的写会存在文件锁的问题, 当块大小大于32KB EBOFS吞吐接近饱和,由于EBOFS在大块写入时会组织数据为大块, 所以他会有很好的读性能。
图7 不同写大小和副本数情况下的同步写延迟,对于小的写,两个以上的副本数会产生很小的额外消耗,因为副本更新会同时发生,对于大的同步写,传输时间会很大,客户端会有停顿,因为大于128KB的写需要排他锁和异步刷新数据。
图8 OSD写性能保持线性,饱和在24个OSD,CRUSH和hash性能会提高,当更大PG时在OSD利用上为小方差
图7描述不同写大小和副本数情况下的同步写延迟。当主OSD同时传输更新到每个副本, 对于副本两个以上时,小块写会有个小的延时增长,对于大块写,传输的消耗会占主导地位,1MB大小的写会花去13ms,当是三个副本时,会增加到2.5 倍,(33ms),因为大于128KB的写需要排他锁和异步刷新数据客户端部分会出现停顿。作为一种选择,共享写应用也可以使用O LAZY标志位,通过放松一致性,客户端可以缓存小的写,提交大的写,异步写入OSD,这样延迟就会取决于客户端,它会写入cache,等待数据刷新到磁 盘。
Ceph的数据性能跟随OSD数量几乎线性变化,CRUSH伪随机分布数据,所以OSD的使用能够通过二次或者正太分布写模型化,是一个完全随 机过程,利用率的方差降低,当组的数量增加:对于每个OSD100个PG,标准的偏差是10%,对于1000个是3%, 图8描述单OSD吞吐当集群通过CRUSH分布,一个简单哈希算法,一个线性的条带化策略分布数据, 在4096或者32768个PG 在现在的OSD上,线性的条带化策略可平衡加载最大的吞吐去提供分布, 像一个简单的随机算法。他不能处理设备故障或其他OSD集群变化,因为数据位置通过CRUSH或者HASH算法都是随机分布的,当PG数少,吞吐会降低, 原因在于OSD利用上的大方差导致请求队列长度与客户端负载不匹配,因为设备可能满载,当然概率小, 性能会降低。CRUSH可以改变这种状况,各种分布到OSD的集群映射都可以通过cluster map标示,不像hash和线性算法,Crush最小化了数据迁移,在集群扩张的同时保持一个平衡的分布, CRUSH算法的复杂度为o(log n),计算只需要几十微秒, 可允许成千上万的OSD组成的集群。
Ceph的MDS集群提供增强的POSIX语义, 具有很好的扩展性, 此处通过没有任何数据I/O的工作负载来衡量它的性能,这次实验中的OSD仅仅用于存储元数据。
首先假定延迟所关联的元数据更新(例如,mknod,mkdir)。 一个单一的客户端建立了一系列的文件和目录, 为了安全,这些MDS都需要同步日志到OSD上。假定为都没有本地磁盘的MDS, 所有的元数据都存储在共享的OSD集群上,同时有一个有本地磁盘有的节点作为主节点OSD来存放日志, 图9a 描述了在无本地磁盘的情况下,随着副本数的增加元数据的更新延迟。0 代表没有元数据日志写入,日志会先写入主OSD, 副本写入其他的osd。有本地磁盘时, 初始的从MDS到OSD的写入会花很少时间。 在无本地磁盘的情况下2x复制的更新延迟和1x差不多,由于更新同步, 这两种情况下,2倍以上的复制会产生一些延迟。
元数据读(例如readdir, stat,open)更复杂了,图9b描述操作累积时间(一个客户端在10000个嵌套的目录,在每个目录上readdir,和在每个文件上一个stat)
图9: 使用本地磁盘,通过减少初始的网络来回减低了写延迟;读受益于缓存,当readdirplus或者一致性要求不高时能消除MDS的交互(当stats在readdir之后时)
图10:Per-MDS吞吐在不同的工作负载和集群大小下, 当集群大小增长到128的节点时,效率与最好表现相比减低了不到50%, 有效的提高了现有系统的效率
MDS缓存减少了readdir时间,之后的stats并不受影响,因为索引节点内容嵌入到目录中,一个OSD访问可以使得目录内容能够被放入 MDS缓存中。通常,累加的stat时间主要由大目录操作占据,后来的MDS交互会被readdirplus消除,它能够把stat和readdir操作 封装为一个操作,或者通过放松POSIX, 允许stats跟在readdir之后时,从客户端的缓存中提供外界服务,这是ceph默认设置。
图11:对与Per-MDS的延迟, 分别为4 , 16, 64个节点的MDS情况下,在mkdirs操作下,大的集群会有不太完美的分布,导致一个低的平均MDS吞吐,和更高的平均延迟
通过在LLNL实验室的alc Linux集 群上使用430个节点分区, 图10描述了随着MDS集群大小变化的per-MDS吞吐,有很优秀的线性扩展性能,在mkdirs的工作负载中,每个客户端创建一个嵌套的四级目录,在 每个目录下带着10个文件和子目录,平均MDS吞吐从2000 ops/s/MDS(在一个小的集群中)到1000 ops/s/MDS,减少50%,这时集群规模为128MDS,总共有大约100000 ops/sec。当操作makefile时,每个客户端在相同的目录中创建上千个文件,当检测到很多的写时,Ceph会哈希这个共享目录,并会放松这个目 录的一致性来分担工作负载到所有的MDS节点。openshared的工作负载中每个客户端重复的打开关闭十个共享文件。在openssh操作中,每个客 户端在一个私有目录下重复捕获文件系统路径,一个例子是用一个共享的/lib 作为共享路径,同时其他共享/usr/include, 这个目录经常被读,openshared and openssh+include拥有很多的读共享, 表现出不太好的扩展性能,相信由于客户端选择很差的副本,openssh+lib扩展优于琐碎的分开的makedirs, 因为它有很少的元数据变更和很少的共享, 尽管我们相信网络上的通信内容和消息层次上的线程进一步的降低了MDS集群的性能, 我们访问大型集群的时间有限,使得没法有更彻底的调研。
尽管有不太完美的线性扩展, 128节点的MDS集群每秒可以做大于25万的元数据操作, 元数据交互是独立的数据I/O, 同时元数据的大小是独立的文件大小,相当于几百个字节的存储甚至更多,这都取决于平均文件大小,举例来说,特定的应用在LLNL实验室的 Bluegene/L建立检查点,这个集群可能包含64000个node,并都有两个处理器, 写文件到相同目录下的不同文件中,就像mkfiles一样, 相同的存储系统能达到6000 元数据操作/s,同时完成每个检查点会要几分钟,128节点的Ceph MDS集群会在2s内完成, 如果每个文件只有10MB(算比较小的), osd的速度会保持50MB/s, 这样的集群能达到1.25TB/s的写速度,支撑至少25000个osd,50000用于副本。如果250GB一个OSD,那OSD集群就是6PB, 更重要的是,Ceph的动态元数据分布允许一个MDS集群(任何大小)可以为现在的操作重定位资源 ,尤其在所有的客户端访问之前分配给MDS元数据,使其更适应任何静态分区。
通过一个分布函数取代文件分布元数据给我们带来了惊喜,也是提供我们简化设计的力量,尽管就函数本身而言需要了更多的需求,但当我们已知这些需 求,CRUSH能够实现必要的扩展性,灵活性和可靠性,它极大的简化了我们的元数据操作,可提供客户端和OSD以完整和独立的数据分布信息。后者使我们可 实现数据复制,迁移和故障检测,恢复。并有效的利用环境中的CPU和内存,RADOS也给未来的OSD模型的增强开启了一扇门,例如位错检测(像 google文件系统)和基于工作负载动态的数据副本,类似于AutoRAID。
尽管ceph试图用现有的文件系统为本地的对象存储,很多其他系统也是这样做的。我们很早就认识到,一个文件系统可为对象的工作负载提供更好的 性能。我们没有预料到的是,现有的文件系统接口之间和我们的需求的差异, 在开发rados复制和可靠性机制时变得很明显, ebofs对于我们user-space开发出奇的快,提供了非常令人满意的性能, 接口也完全适合我们的要求。
在Ceph中一个最大的教训是MDS负载均衡器的重要性, 它负责扩展性,和选择什么元数据迁移到哪和什么时候迁移。尽管本质上我们的设计和目标显得很简单,分发一个演化的工作负载到成百的MDS。MDS有各种不 同的性能限制,包括CPU,内存,(缓存效率), 网络和I/O限制,这些都会在某个点上限制性能,在总的吞吐和失败率下很难做出权衡,某些情况下的元数据分布失衡会提高整体的吞吐。
实现客户端接口遇到了比预期更大的挑战。虽然使用FUSE大大简化实现, 可避开内核, FUSE 有自己的优点。DIRECT_IO绕过内核页面缓存,但没有支持mmap, 迫使我们修改FUSE使得干净页面失效来作为一个解决方案。FUSE执行自己的安全检查会产生很多getattrs(统计), 甚至实现简单的应用程序调用。最后,页面内核和用户空间之间的I / O限制了总体I / O。虽然直接链接到客户端可避免FUSE的一些缺陷, 在用户空间中的系统调用会引入了一组新的问题(其中大部分我们还没有完全检查过), 但内核中客户端模块不可避免。
高性能可扩展的文件系统一直是HPC社区的目标, 提供一个可承担高负载的文件系统。尽管许多文件系统为了满足这种需求,他们不提供相同级别的如Ceph提供的可伸缩性。大规模的系统, 如OceanStore和Farsite是为了提供高度可靠的PB存储, 并能提供成千上万的客户端到成千上万的单独文件的同时访问, 但不能在成千上万的合作客户端对一组文件访问时提供高性能访问,是由于如子系统的名称查找这样的瓶颈。相反,文件存储系统,如 Vesta,Galley,PVFS, SWIFT[5]支持跨多个磁盘条带化数据,实现很高概率的数据转移分布,但缺乏可扩展的元数据访问或健壮的数据分布的强力支持。例如,Vesta允许应 用程序允许没有共享的元数据情形下独立访问每个磁盘文件数据。然而, 像许多其他并行文件系统, Vesta不提供可伸缩的元数据查找支持。因此,这些文件系统通常工作负载表现不佳, 访问许多小文件也需要许多元数据操作。他们通常还受到块分配问题:块分配通过集中式或基于锁机制, 阻碍从成千上万的客户对成千上万的磁盘的写请求的可扩展性。GPFS和StorageTank部分解耦元数据和数据管理,但受限于基于块的磁盘和元数据分 布体系结构的使用。
网格文件系统,如LegionFS,旨在协调广域访问,并不是为本地文件系统提供高性能。同样, Google文件系统是为大的文件访问和包含大量读和文件写附加提供优化。像Sorrento,它是为了非posix语义的很小部分的应用程序使用。
最近,许多文件系统和平台,包括FAB和pNFS,一直围绕网络附加存储来设计。Lustre, Panasas文件系统, zFS,Sorrento,Kybos是基于对象的存储模式,和Ceph最相似。然而,这些系统都没有结合可扩展自适应的元数据管理,没有结合可靠性和容 错性。Lustre和Panasas不能将任务委托给OSD, 对分布式元数据管理,可伸缩性和性能仅提供有限支持。此外,除了Sorrento使用一致性哈希,所有这些系统使用显式的位置图来指定对象存储位置,并有 限支持新存储部署时的再平衡。这可能导致负载不对称和可怜的资源利用率,而Sorrento的散列分布算法缺乏CRUSH所具有的对数据迁移、设备权重计 算和失败检测的支持。
一些核心Ceph元素尚未实现,包括MDS故障恢复和POSIX一些调用。两个安全体系结构和协议正在考虑中, 但也未实现。我们还计划调查关于名空间->inode转换元数据的客户端回调的实用性。对文件系统的静态区域, 可能允许不打开MDS交互。还有计划其他几个MDS增强特性, 包括创建快照的任意子树的目录层次结构。
Ceph可动态复制元数据。我们计划让OSD基于工作负载动态调整单个对象的复制水平和跨多个OSD放置组分发读访问。这将允许对少量数据的可伸缩访问, 并可能使用类似于D-SPTF机制来促进细粒度的OSD负载均衡。
最后,我们正在开发一个服务质量体系结构允许流量优先级控制和OSD-managed基本带宽和延迟保证,除了支持QoS要求的应用,还需要有助于均衡RADOS复制和恢复。一些EBFOS的增强也在计划中,包括改善分布逻辑,数据检索与验证和位错检测机制。
Ceph完成了三个关键挑战, 性能、可扩展性和可靠性。通过剥离现有文件系统的分布表设计, 我们最大限度地分离数据与元数据管理, 允许他们独立使用。这种分离依赖于CRUSH, 允许客户来计算对象的位置, 而不是在分布表中寻找。CRUSH算法可在设备故障、扩张和集群重组为常态的大型存储集群中跨故障域执行数据复制。
RADOS利用智能OSD管理数据复制,故障检测和恢复,低级别磁盘分配,调度和数据迁移,而不是使用任何中央服务器节点来完成这些。尽管对象 可以被认为是文件,并存储在一个通用的文件系统中, EBOFS提供更合适的语义和高性能的满足Ceph中特定的工作负载和接口需求。
最后,Ceph的元数据管理体系结构解决了大型存储系统中最棘手的问题之一, 即如何实现有效提供一个统一的目录层次结构并服从POSIX语义,随着元数据服务器数量增加性能保持良好的机制。Ceph的动态子树分区是一个独特的可伸 缩的,高效的方法, 可在不同工作负载中有保持良好的适应性。
Ceph为LGPL许可,并可在http://ceph.sourceforge.net/查看。