本文翻译自:http://docs.ceph.com/docs/hammer/architecture/
一些名词的翻译方式:
scalable :可扩展性
high availability:高可用
map:图
cluster map:集群运行图
monitor:监视器
acting set:运行集
up set:在线集
hyperscale:超大规模
data scrub:数据清洗
peering:互联
rebalance:重平衡
full write:一次完整地写
tiering:层
cache tiering:缓存层
ceph体系结构图:
ceph集群
根据ceph的相关论文,ceph基于RADOS来提供无限扩展的能力,一个ceph存储集群包含两种类型的守护进程:
监视器(Monitor)
OSD守护进程(
OSD Daemon,如下称osd)
ceph集群从ceph客户端接收数据(客户端能够是一个ceph的块设备,ceph的对象存储网关,ceph的文件系统或者是基于librados写的程序),并以对象的形式存储起来。每一个对象就像文件系统中的一个文件,osd负责在存储设备上的读写操做。
osd在一个扁平的命名空间内以对象的形式保存数据。每一个对象都包含标识符(identifier),二进制数据(binary data)和以name/value对组成的元数据(metadata)。元数据的语义彻底由ceph客户端来决定,好比,CephFS就用元数据来存储文件的各类属性。
注意:对象id在整个集群中是惟一的
在传统架构中客户端须要与一个中心组件进行通讯,这使得系统存在单点问题,而且可能成为性能和可扩展性的瓶颈。Ceph则没有这种中心式的网关,客户端直接与osd进行通讯。osd会将对象复制到其余节点上来保证数据的安全和实现高可用,同时,ceph还有一套监控集群来实现高可用。ceph是经过一种叫crush的算法来实现去中心化的。
ceph客户端和osd都使用crush算法来计算对象存储的位置,而不是经过一个中心式的表来查询。crush提供的这种数据管理机制更好,更方便大规模地扩展集群,由于定位对象的工做都交给各个客户端和osd了。crush使用智能数据复制的方式来确保弹性,更加适合超大规模集群。下面几节咱们详细介绍一下crush算法的一些细节:
ceph客户端和osd都知道集群的拓扑结构,该结构由下面5张图组成,统称为“集群运行图”。
- monitor map:包含了集群的fsid,位置,地址名称和端口,也包含了当前的版本,建立时间,最后修改时间。要查看监视器图,可使用命令:ceph osd dump
- osd map:包含了集群的fsid,建立和最后修改时间,pool列表,副本数量,pg数量,osd列表和他们的状态(up,in)。要查看osd图,能够用命令:ceph osd dump
- pg map:包含pg版本,时间戳,最后一次osd图的版本,full radio,每一个pg上的详情如pg id,up set,acting set,pg状态(active + clean),每一个pool的数据使用统计信息。
- crush map:包含了存储设备列表,失败域层级结构(device,host,rack,row,room等),存储数据时遍历这些层级的规则。要查看crush图,可使用命令:ceph osd getcrushmap -o {filename},而后使用命令反编译一下:crushtool -d {comp-crushmap-filename} -o {decomp-rushmap-filename},而后可使用cat命令查看反编译后的图。
- mds map:包含当前的mds运行图版本,建立时间,最后修改时间,同时也包含了保存元数据的pool,元数据服务器列表,哪些元数据服务器是up状态和哪些是in状态。要查看mds图,可使用命令:ceph mds dump
高可用监视器
ceph客户端读写数据前必须从监视器获取一份最新的集群运行图。集群能够只有一台监视器,只不过这样会存在单点问题。
为了使集群高可用,ceph支持使用一套监视器集群,使用Paxos协议来使监视器之间对于当前集群状态达成一致。
高可用认证
为了认证用户身份和防止中间人攻击,ceph经过cephx认证系统来认证用户和守护进程。
注意:cephx协议并不对数据进行加解密。
cephx使用共享密钥进行认证,因此客户端和监视器集群都有一份密钥。
要使用cephx,管理员须要先设置用户(users)。能够经过client.admin用户执行命令:cephauth get-or-create-key来生成用户名和密钥,这时监视器会保有一份密钥,以下图所示:
客户端向监视器发起认证请求,监视器返回一份受权过的数据,有点像Kerberos里的ticket,包含一个会话key。key使用用户的密钥加过密,因此只有该用户才能解密这个key来访问监视器。接着客户端使用会话key请求监视器,监视器这时会返回一个加密过的ticket,以下图所示:
客户端解密这个ticket,对发送到osd和元数据服务器的数据进行签名。osd,元数据服务器和监视器共享一份密钥,因此均可以校验客户端的请求。就像Kerberos,cephx ticket有过时时间。
使用这种方式,只要用户的session key没有泄露就不会被攻击者伪造或修改消息。不过,这种认证只保护ceph客户端到服务器这段路程,若是用户从远程访问客户端,则从用户到客户端这段路程是不受保护的。
智能进程使超大规模成为可能
ceph没有中心节点,osd和客户端都知道集群的全部信息,每一个osd都知道集群中全部其余osd的存在,这样osd就能够直接与其余osd和监视器通讯。另外,客户端也能够直接与osd通讯。
这种方式带来的好处有如下几点:
- osd和客户端直连:网络支持的并发链接数是有限的,若是存在中心节点则在集群规模变大时中心节点的链接数容易超过限制。去中心化则使用ceph客户端能够直接与osd相链接,这样提升了总体的性能和整个系统的并发量。
- osd成员和状态:osd加入集群后会报告它自身的状态,在最底层,用up,down来表示osd是否正常运行,down,in这两种状态表示osd存在问题。若是osd没有在运行(好比:崩掉了),那它就没法告之监视器它处于down状态,而监视器会定时ping osd以确保它们还在运行。不过,ceph还容许osd受权邻近的其它osd来判断其是否处于down状态,并更新集群运行图并报告给监视器,这样监视器能够以轻量级状态运行。
- 数据清洗:做为维护数据一致性和清洁度的一部分,osd能够把pg里面的对象元数据和另外一个osd上面的pg里的副本相比较来清洗pg里面的对象。清洗任务用来捕获osd上的bug或文件系统错误。osd还能够一个比特位一个比特位对比的方式来执行深度清洗,以找出轻度清洗时没有发现的硬盘的坏扇区。
- 复制:osd采用crush算法来计算对象副本应该存储的位置。在写数据的过程当中,客户端使用crush算法来计算对象应该存在哪,将对象映射到一个pool和pg,而后经过crush运行图来定位主osd。客户端将对象写入到主osd上的pg,而后,主osd经过crush运行图来定位第二和第三副本所在osd,将对象复制到这些osd上的pg里面,最后再回应客户端对象保存成功。示意图以下:
动态集群管理
ceph设计的关键点是自主自治,下面就介绍下crush是怎么作到让现代云存储服务实现保存数据,从新平衡集群以及是如何从失败状态中恢复的。
关于Pool
ceph有一个概念叫池(Pool),是对对象的逻辑分区。客户端从监视器获取一份集群运行图,而后往池里面写入对象。池的大小或者说是副本数量,crush规则集和pg的数量共同决定了ceph将怎样存储数据:
池的集合至少有如下几个参数:
- 对象的全部权/访问权
- pg的数量
- 用到的crush规则集
PG和OSD的映射关系
每一个池都有若干个pg,crush动态地映射pg和osd的关系,当客户端要写入对象时,crush算法会把每一个一对象映到到一个pg。这种将对象映射到pg的方式为osd和客户端之间创建了一层间接的关联关系。ceph集群必须支持节点的增长或减小并从新平衡对象的分布,若是客户端知道osd具体保存有哪些对象就会致使它们之间有强耦合的关联。相反,crush算法经过将每一个对象映射到一个pg,每一个pg映射到一个或多个osd,使得当有新osd加入或上线时ceph就能够方便的动态的从新平衡数据的分布。
客户端经过集群运行图,利用crush算法能够计算出某个具体对象应该保存在哪一个osd上:
计算PG的id
当客户端从监视器获取到最新的集群运行图时,它就知道了全部监视器,osd和元数据服务器的信息,可是这时它并不知道对象存储的位置。对象存储的位置须要经过计算得出。
只须要输入对象id和池的信息,客户端使用对象名称,一个哈希值,池中pg的数量和池的名称就能计算出pg,如下是计算pgid的步骤:
- 客户端输入池id和对象id
- ceph计算出对象id的哈希值
- 使用哈希值对pg的数量取模定位到一个pg的id(如58)
- 经过池的名称获得池的id(如“livepool" = 4)
- 将池的id放在pg的id前面(如4.58)
计算对象存储的位置比经过一系列会话去查询要快得多,crush算法容许客户端来计算对象应该存储在哪,这样客户端能够直接链接保存对象的主osd。
互联和子集
osd相互之间除了会有心跳机制,还会进行互联,即全部osd会对每一个pg中的全部对象的状态进行协商达成一致。实际上,osd会向监视器报告互联过程当中出现的失败,互联这个功能一般会本身修复失败,可是也有可能一直不能修复,关于这一点请参见疑难解答部分。
ceph设计成至少保存对象的两个副本,这是对数据安全的最低保证。为了高可用目标,一个ceph集群只有保存两个以上对象的副本时才能以degraed状态运行。
咱们没有对osd进行1,2,3这样的命名,而是使用了primary,secondary之类。按照约定,primary是运行集(Acting Set)中的第一个osd,为上面的每个pg负责与其余osd进行互联协商,而且负责接受客户端写对象的请求。
对于那些负责同一个pg的osd集合咱们称之为运行集(Acting Set)。
运行集中的osd并不老是up状态,当一个运行集是up状态时,它就变成了在线集(Up Set)的一部分。在线集是一个重要的特性,由于ceph能够在一个osd出问题时将pg从新映射到其余osd节点。
重平衡
当ceph集群中新增了一个osd节点的时候,集群运行图会进行更新,这样一来就改变了对象存储的位置,集群会进行重平衡,将一部分pg转移位置。但并非全部pg都会进行迁移,即便会发生重平衡,crush算法仍然是比较稳定的,不少pg仍然会留在原来的位置,每一个osd都将腾出一些空间,也不会对新加入的节点形成负载问题。下图是一次重平衡过程:
数据一致性
经过对比pg和它的副本里的对象元数据,osd能够在pg里清洗对象,这一点在前面有讲过。
纠删码
若是纠删码池使用K+M块数据块来存储对象,那它会将对象分红K块数据块和M块编码块。池的大小配置成K+M的话就可让每块数据保存到运行集中的一个osd上,块的序号保存在对象的属性里。
若是一个纠删码池采用了5个osd(K+M=5),那它最多能够接受丢失2个osd(M=2)
读取和写入校验码数据块
当一个包含ABCDEFGHI的对象NYAN写入到池中时,纠删码函数会把数据内容切分红3个数据块:ABC,DEF,GHI。若是数据内容的长度不是K的整数倍将会进行补齐,函数还会建立两个校验码块:YXY和GQC。每一个块保存在运行集中的一个osd上。数据块以对象的形式保存,而且对象名称相同(NYAN)。数据块的序号则保存在对象的属性中(shard_t)。
若是块2和5丢失了,从纠删码的池中读取对象时,解码函数会读取1,3,4三块数据:BC,GHI,YXY,而后重构出对象的原始内容:ABCDEFGHI。以下图所示:
写过程被中断
在纠删码池中,在线集(up set)中的主osd(primary osd)负责接收全部写请求,负责将数据编码成K+M块而后发送给其余osd,同时也负责维护一份权威版本的pg日志
下图示例了一个以K=2,M=1的方式建立的pg,分布在三个osd上面(osd1,osd2,osd3):有一个对象编码后分块保存在这些osd上面:d1v1,d2v2,c1v1,每一个osd上面的pg日志都是相同的。
osd1是主osd,负责接收整个写操做,这个操做将替换掉整个原有对象而不仅是对象的一部分,即Version2将替换掉Version1。osd1将对象编码成三个块:d1v2保存在osd1,d2v2保存在osd2,c1v2保存在osd3。每一块都会发送到相应的osd,包括主osd。当一个osd接收到指令要求写入数据块时,也会建立一份pg日志来记录本次变动。好比只要osd3保存好了c1v2,它就会在日志中增长一项entry(epoch:1, version:2)。由于osd都是异步工做的,有些块可能还在传输过程当中时另外一些可能已经落盘保存好了。以下图:
若是一切顺利进行,每一个osd都会确认数据块写入成功,而后日志中'last_complete'指针就能够从1,1指向1,2。以下图:
最后,用来保存以前的那个旧的数据块的文件就要以移除掉了,最终以下图所示:
可是也有可能发生意外,发果osd1下线了而d2v2仍然在传输中,那这个对象的第2个版本就只写了一部分:osd3有一块数据但仍然不足以还原完整对象。这时就丢掉了两个数据块:d1v2,d2v2,而K=2,M=1要求至少有两个数据块可用才能重建第三块。假设osd4变成新的主osd,并从日志中查找到last_complete指针指向(1,1),而后将这个指针做为新的权威日志的头部。以下图所示:
osd3就会发现它上面日志的entry(1,2)与osd4上面的权威日志不匹配,因此它会丢弃c1v2数据块。数据清洗任务将会经过解码函数重建出d1v1块并保存到osd4上面。以下图所示:
缓存层
缓存层可让客户端体验更好的io性能,你可使用一组相对快但比较贵的存储设备用来用于缓存层,而使用另外一组相对较慢但更便宜的设备来作为比较经济的存储层。缓存代理负责何时将缓存中的对象刷新到后端存储,因此缓存层和后端存储层对于客户端来讲是透明的。以下图:
扩展ceph
你能够经过建立共享对象类(Ceph Classes)来扩展ceph。ceph从osd class dir动态加载.so文件。若是你想本身实现一个类,你能够设计新的对象方法来调用本地方法,或者其余的类方法。
在进行写操做时,ceph类会调用本地方法或类方法,对输入的数据执行一系列操做而后产生一个写事务的结果。
在进行读操做时,ceph类会调用本地方法或类方法,对输出的数据执行一系列操做而后将数据返回给客户端。
ceph类示例:若是ceph做为内存管理系统的存储,能够对输入的图片进行大小和比例的剪裁,嵌入一个不可见的版权说明或水印来保护版权,而后将修改过的图片保存到ceph。
ceph集群总结
ceph存储集群就像有机体同样是动态的,不少存储系统不能很好地利用cpu和内存,而ceph能够。从心跳,互联,重平衡集群或者从失败中恢复,ceph利用了osd的计算能力来替客户端作了不少工做。当涉及到硬件推荐和网络配置时,能够回顾一下前面的概念来理解ceph是怎么利用计算机资源的。
ceph协议
ceph客户端使用原生协议来与ceph集群通讯。ceph将这些方法打包成librados库,这样你就可使用它来建立本身的客户端,下图是它的基本结构:
原生协议和librados
现代的应用程序须要一个有异步通讯的能力的简单的对象存储接口,而ceph正好能提供这样的接口,这些接口提供了直接的,可并行访问对象的能力:
- 对池的操做
- 快照和写时复制
- 读写对象-包括读写完整对象,字节范围,追加或截断。
- 建立和更新对象属性 XATTRs
- 建立和更新 key/value对
- 混合多种操做和双重确认语义
- 对象分类
对象监视和通知
客户端能够向主osd注册一个对象的监听器并维持一个会话,客户端也能够发送通知消息和载荷给全部watcher,并能够接收watcher的消息。经过这些方式,客户端可使用任何对象做为同步或通讯channel。
数据条带化
存储设备都存在吞吐量限制,这就影响了存储性能和伸缩性。因此通常的存储系统都支持条带化,即将数据分红若干连续块分别存储到不一样的设备,这样来提升吞吐量和性能。经常使用的条带方式有RAID,ceph的条带化像RAID0,或是一个条带卷。ceph的条带化达到了raid0的吞吐量、n路raid镜像的可靠性和快速恢复能力。
ceph提供了三种客户端类型:block device,Filesystem,ObjectStorage,客户端须要本身负责将用户数据转换成多个对象存储到ceph集群里。
最简单的条带化方式是一个对象一个对象地写入条带数据,客户端持续往对象写入条带单元的数据直到达到对象所容许的最大值,而后建立新的对象来继续保存剩下的条带单元数据。这种方式对于小的块存储,s3或Swift对象和cephfs的文件是有效率的,可是尚未最大化的利用ceph的分布式pg的能力,因此也不能提高多大的性能。
若是你须要存储大的块,对象或文件等,采用多对象集合条带化的方式能够获得可观的性能提高。客户端将条带单元大小的数据并发写入相应的条带对象。由于每一个对象对应到不一样的pg从而对应到不一样的osd,因此每次并发写操做都能达到最大的写速度。写入到单个磁盘会受限于磁头的速度,而将写操做分布到多个磁盘能够减小每一个磁盘的总寻址次数,从而能够结合多个磁盘的吞吐量达到较快的写速度。
下图展现了客户端数据在对象集(object set 1)中的条带化过程,对象集中有四个对象,条带单元数据按顺序写入到这四个对象,每写一轮客户端都会判断一下对象集是否已满,没满则继续从第一个对象开始写数据,若是满了则建立另外一个对象集(object set 2),而后按一样的方式写入数据。
有三个重要变量决定了ceph如何条带化数据:
- 对象大小: ceph集群中有对象的最大大小限制,这个大小必须能容纳多个条带单元的数据,而且应该是条带单元的整数倍。
- 条带带宽:条带有一个单元大小的配置,客户端将数据按单元大小进行分块。
- 条带数量:客户端每次按照条带数量来决定将多少个条带单元写入到多少个对象中。这些对象称之为对象集,当数据写入到最后一个对象后,客户端又从集合中第一个对象开始写。
重要:在上生产环境前对你的条带配置进行性能测试,一旦条带化数据写入到对象后就不再能修改这些参数了。
ceph客户端
ceph客户端包含几种接口:
下图是一个高层架构视图:
ceph对象存储
对象存储radosgw是一个网关服务,它提供了一套restful风格的api来存取对象和元数据。它是基于ceph集群,有本身的数据格式,维护本身的用户数据库,认证和访问控制。rgw使用了统一的命名空间,因此你可使用openstack swift兼容api或者亚马逊s3兼容api。好比,你能够经过s3接口写入数据,而后经过swift接口读取数据。
ceph块设备
ceph块设备将一个块设备映象条带化成多个对象,每一个对象都分散到一个pg中,而pg又分散到不一样的osd上。
这种精简的,可快照的ceph块设备对于虚拟化和云计算是颇有吸引力。在虚拟机场景,人们常用Qemu/KVM中的rbd网络存储驱动部署ceph块设备,其中宿主机采用librbd向客户机提供块设备服务。许多云计算堆栈使用libvirt和管理程序集成。你能够采用精简的的ceph块设备搭配Qume和libvirt来支持openstack和cloudstack,构成一套解决方案。
ceph文件系统
ceph文件系统在ceph集群的上面提供了一套posix兼容的接口,cephFS里的文件映射为ceph集群里的一个对象。ceph客户端将cephfs挂载成一个内核对象或做为面向用户的文件系统(FUSE)。以下图:
cephFS服务包括了与ceph集群部署在一块儿的mds(ceph metadata server),mds的目的是在高可用的元数据服务器上保存全部文件系统元数据(目录,文件拥有者,访问模式等),这些元数据在内存里也有一份。mds的意义在于对那些简单的文件系统相关操做(如ls,cd)没必要要去浪费osd的资源。将元数据和数据分开意味着ceph文件系统能够在不浪费集群资源的状况下提供高性能的服务。
cephFS将数据和元数据分开,元数据保存在mds,文件数据保存在一个或多个对象中。cephFS的目标是posix兼容,ceph-mds能够以单进程形式运行,或者它也能够分布到多个物理机上运行,这样也是为了高可用和高伸缩性:
- 高可用:有额外的mds在一旁待命,这样一旦活动的ceph-mds失败就能够取而代之。这是很容易的由于全部数据,包括日志,都是保存在rados上的。这个主备的转换过程由ceph-mon自动完成。
- 伸缩性:能够有多个ceph-mds处于活动状态,它们将文件夹树切分红多个子树,有效地平衡活动服务器的负载。
能够将standy和active组合在一块儿,好比运行3个活动的ceph-mds来提供伸缩性,一个待命例程来提供高可用。