Ceph的官方定义是:ceph是一种为优秀的性能、可靠性和可扩展性而设计的统一的、分布式的存储系统。
统一的:意味着ceph可以使用一套存储系统同时提供对象存储、块存储和文件系统存储3种功能,以便在满足不同应用需求的前提下简化部署和运维的步骤。
分布式:意味着无中心结构,系统规模的扩展可以没有理论上限。
作为一个存储系统,ceph在物理上必然包含一个存储集群,以及访问这个存储集群的应用或客户端。ceph客户端又需要根据一定的协议与ceph集群进行交互。
(1)Ceph存储集群
Ceph基于可靠的、自动化的、分布式的对象存储(Reliable,Autonomous,Distributed Object Storage,RADOS)提供了一个可无限拓展的存储集群。RADOS,这一层本身就是一个对象存储系统,所有存储在ceph系统中的用户数据事实上都是由这一层来存储的。而ceph的高可靠、高可扩展、高性能、高自动化等也都是这一层提供的。
物理上,RADOS是由大量的存储设备节点组成,每个节点拥有自己的硬件资源(CPU、内存、硬盘、网络),并运行着操作系统和文件系统。
(2) 基础库librados
Ceph客户端用一定的协议和存储集群进行交互,Ceph把此功能封装进了librados库,这样基于librados库就能创建自己的定制客户端了。
librados库实际上是对RADOS进行抽象和封装,并向上层提供API,以便可以基于rados(而不是这个ceph)进行应用研发。需要注意的是,RADOS是一个对象存储系统,因此,librados库实现的API也只是针对对象存储功能的。
rados采用C++开发,所提供的原生librados API包含C和C++两种。在物理上,librados和基于其上开发的应用位于同一台机器,因而,也被成为本地API。应用调用本机上的librados API,再向后者通过socket与RADOS集群中的节点通信并完成各种操作。
(3)高层应用接口RADOS GW、 RBD 、Ceph FS
这一层的作用是在librados库的基础上提供抽象层次更高、更便于应用或者客户端使用的上层接口。
ceph对象网关RADOS GW(RADOS Gateway)是一个构建在librados库之上的对象存储接口,为应用访问ceph集群提供了一个与Amazon S3和Swift兼容的RESTful风格的网关。
RBD则提供了一个标准的块设备接口,常用于在虚拟化的场景下为虚拟机创建存储卷。Red Hat已经将RBD驱动集成在KVM/QEMU中,以提高虚拟机的访问性能。
CephFS是一个可移植操作系统接口兼容的分布式存储系统,使用ceph存储集群来存储数据。
(4) 应用层
这一层包含的是在不同场景下对应ceph各个高层应用接口的各种应用方式。例如,基于librados库直接开发的对象存储应用,基于RADOS GW开发的对象存储应用,基于RBD实现的云硬盘,等。
严格意义上讲,ceph只提供对象存储接口,所谓的块存储接口和文件存储接口都算是对象存储接口应用程序。不同于传统文件系统提供的open/read/write/close/lseek,对象存储只提供pu/get/delete,对象存储的逻辑单元就是对象而不是通常概念中的文件。
RADOS GW是一个基于librados库构建的对象存储接口,为应用程序提供Ceph存储集群的RESTful网关,这样ceph就作为Amazon S3和OpenStack Swift的后端对象存储,应用程序可以直接通过librados的C语言或C++语言API实现对象操作了。
RADOS集群主要由两种节点组成:为数众多的OSD,负责完成数据存储和维护;若干个Monitor,负责完成系统状态检测和维护。OSD和monitor之间互相传递节点的状态信息,共同得出系统的总体运行状态,并保存在全局数据结构中,即集群运行图(Cluster Map)中。 集群运行图与RADOS提供的特定算法相配合,实现了Ceph的许多优秀特性。
在使用RADOS系统时,客户端程序向monitor索取最新的集群运行图,然后直接在本地计算,得出对象的存储位置后,便直接与对应的osd进行通信,完成数据的各种操作。
ceph客户端、Monitor和OSD可以直接交互,这意味着OSD可以利用本地节点的CPU和内存执行那些传统集群架构中有可能拖垮中央服务器的任务,充分发挥节点上的计算能力。
OSD是用于实现数据的存储和维护。根据定义OSD可以被抽象为系统和守护进程两个部分.
在实际应用中,通常将多个OSD集中部署到一台大规模的服务器上。在选择系统配置时,应当保证每个OSD占有一定的计算能力、一定数量的内存和一块硬盘(通常情况下一个OSD对应一块硬盘)。同时, 应当保证该服务器具有足够的网络带宽。
每个OSD拥有自己的一个OSD Daemon。这个Daemon负责完成OSD的所有逻辑功能,包括与monitor和其他OSD(实际为其他OSD的Daemon)通信,以维护和更新系统状态,与其他OSD共同完成数据的存储和维护操作,与客户端通信完成各种数据操作,等等。
RADOS集群从ceph客户端接收数据(可能是ceph块设备、ceph对象存储、ceph文件系统或者基于librados的自定义客户端),然后存储为对象。每个对象是文件系统中的一个文件,它们存储在OSD的存储设备上,由OSD Daemon处理存储设备上的读写操作。
OSD的状态直接影响数据的重新分配,所以检测osd的状态是monitor的主要工作之一。
osd的状态由两个维度来表示:up或down(表示osd daemon与monitor是否连接正常);out或in(osd上是否有pg)
因此,osd共有4种状态:
ceph采用心跳机制,检测节点故障和网络故障。
此次映射就是将用户要操作的file映射为RADOS能够处理的Object。本质上就是按照Object的最大尺寸对file进行切分,相当于磁盘阵列中的条带化过程。这种切分的好处有两个:1、让大小不限的file变成具有一致尺寸、能被RADOS高效管理的Object;2、让对单一file实施的串行处理变为对多个Object的并行处理。
上图中:ino表示待操作file的元数据,可理解为file的唯一ID
ono表示该file切分产生的某个Object的序号
oid表示ino后连接ono
在file被映射为1个或多个Object之后,就需要将每个Object独立地映射到1个pg中去。其映射的计算公式如下:
hash(oid) & mask —> pgid
其中:hash表示静态哈希算法。 如果pg的总数为m(m应为2的整数幂),则mask值为 m-1。 & 表示按位与操作。
通过上式计算,能从所有m个pg中近似均匀地随机选择1个pg。
第3次映射就是将作为Object的逻辑组织单元的pg映射到数据的实际存储单元osd上。如上图所示,RADOS采用一个名为crush的算法,将pgid带入其中,就能得到一组共n个osd。生产环境下,n一般为3。这n个osd共同负责存储和维护一个pg中的所有Object。具体到每个osd,则由其上运行的OSD Daemon负责执行映射到本地的Object在本地文件系统中的存储、访问、元数据维护等操作。
crush算法并不是绝对不变的,而会受到其他因素的影响。影响因素主要有2个:
因此,当前系统状态和存储策略都不变时,pg和osd之间的映射关系才是固定不变的。实际应用中,存储策略一经配置通常不会改变。而设备损坏、存储集群规模扩大等都会导致集群运行图发生变化。但是,ceph本身提供了对这种变化的支持。因此,即使pg和osd之间的映射关系发生了变化,也不会对应用产生影响。事实上,ceph正是利用了crush算法的动态特性,可以将一个pg根据需要动态迁移不同的osd组合上,从而自动化地实现高可靠性、数据分布再平衡性等。
至此为止,ceph通过3次映射,完成了从file到Object、Object到pg、pg到osd的整个映射过程。整个过程没有任何的全局性查表操作需求。至于唯一的全局性数据结构:集群运行图,它的维护和操作都是轻量级的,不会对系统的可扩展性、性能等造成影响。
存储池是一个逻辑概念,是对存储对象的逻辑分区。Ceph安装后,会有一个默认的存储池,用户也可以自己创建新的存储池。一个存储池包含若干个pg及其所存储的若干个对象。
Ceph客户端从监视器获取一张集群运行图,并把对象写入存储池。存储池的大小或副本数、crush存储规则、和pg数量决定Ceph如何放置数据。
ceph osd pool create {pool-name} {pg-num} [{pgp-num}] [replicated] [crush-ruleset-name]
ceph osd pool create {pool-name} {pg-num} {pgp-num} erasure [erasure-code-profile] [crush-ruleset-name]
上述命令用于创建存储池。
可以看出存储池支持的内容如下:
Ceph客户端读或写数据之前必须先连接到某个Ceph monitor上,获取最新的集群运行图副本。
一个Ceph集群只需要单个监视器monitor即可运行,但它就成了单一故障点,即如果该monitor宕机,Ceph客户端就不能读或写数据了。为增强其可靠性和容错性,Ceph支持monitor集群。在一个monitor集群内,延时及其他错误会导致一到多个monitor滞后于集群的当前状态。因此,Ceph的各监视器例程必须与集群的当前状态达成一致。
由若干个monitor组成的监视器集群共同负责整个Ceph集群中所有osd状态的发现和记录,并且形成集群运行图的主副本,包括集群成员、状态、变更,以及Ceph存储集群的整体健康状况。随后,这份集群运行图被扩散至全体osd及客户端。osd使用集群运行图进行数据维护,而客户端使用集群运行图进行数据寻址。
在集群中,各个monitor的功能总体上是一样的,其之间的关系可以被简单理解为主从备份关系。monitor并不主动轮询各个osd的当前状态。正相反,osd需要向monitor上报状态信息。常见的两种上报情况:一是新的osd被加入集群,二是某个osd发现自身或其他osd发现异常。在收到这些上报消息后,monitor将更新集群运行图的信息并加以扩散。
集群运行图实际上是多个map的统称,包括monitor map、osdmap、pgmap、Crushmap以及mdsmap等。各运行图维护着各自运行状态的变更。其中Crushmap用于定义如何选择osd,内容包含了存储设备列表、故障域树结构(设备的分组信息,如设备、主机、机架、房间等)和存储数据时如何利用此树状结构的规则。
客户端包括RBD客户端、RADOS客户端、Ceph FS客户端/MDS。根据通信内容分为获取OSDMap和命令行操作。
(命令行操作)
主要包括集群操作命令:OSD、Monitor、MDS的添加和删除、存储池的创建和删除等
集群信息查询命令:集群状态、空间利用率、IOps和带宽等
这些命令都是由Monitor直接执行或者通过Monitor转发到osd上执行的。
(获取OSDMap)
客户端与RADOS的读或写操作不需要Monitor的干预。客户端通过哈希算法得到Object所在的pg信息,然后查询OSDMap就可以知道pg的分布信息,就可以与primary osd进行通信了。因此,客户端与Monitor仅仅是当获取最新OSDMap时才会进行通信。
Ceph的读写操作采用primary-replica模型。客户端只向Object所对应的OSD set的primary发起读或写请求,这保证了数据的强一致性。当primary收到Object的写请求时,它负责把数据发送给其他副本。只有这个数据被保存在所有的osd上时,primary才应答Object的写请求,这保证了副本的一致性。
当客户端需要向Ceph集群写入一个file时,首先需要在本地完成上述的数据寻址。将file变成一个Object,然后找到存储该Object的一组共3个osd。这3个osd具有各自不同的序号,序号最靠前的osd就是primary osd,而后两个则依次是secondary osd和Tertiary osd。
找出3个osd之后,客户端将直接与primary osd进行通信,发起写入操作。primary osd收到请求之后,分别向secondary osd和Tertiary osd发起写入操作。当secondary osd和Tertiary osd各自完成写入操作之后,将分别向primary osd发起确认操作。当primary osd确认其他两个osd都写入完成之后,自己也完成数据写入,并向客户端确认Object写入操作完成。
之所以采用这样的数据写入流程,本质上是为了保证写入过程的可靠性,尽可能避免出现数据丢失的情况。同时由于客户端只向primary osd发送数据,因此,在互联网使用场景下的外网带宽和整体访问延迟又得到了一定程度的优化。
但是,这种可靠性机制必然导致较长的延迟。特别是,如果要等到所有的osd都将数据写入磁盘后再向客户端发送确认信号,则整体延迟将难以忍受。因此,Ceph可以分两次向客户端确认。当各个osd都将数据写入内存缓冲区后,就先向客户端发起一次确认,此时客户端就可以继续向下执行。等所有osd都将数据写入磁盘后,会向客户端发送一个最终确认信号,此时客户端可以根据需要删除本地数据。
在正常情况下,客户端可以独立完成osd寻址操作,不必依赖于其他系统模块。因此,大量的客户端可以同时与大量的osd并行操作。同时,如果一个文件被分为多个Object,这多个Object也可以被并行发送至多个osd上。
从osd的角度来看,同一个osd在不同的pg中角色不同,因此,工作压力也可以尽可能的均匀分担,从而避免单个osd成为性能瓶颈。
如果需要读取数据,客户端只需要完成同样的寻址操作,并直接与primary osd联系。在目前的Ceph设计中,被读取的数据默认由primary osd提供,但也可以设置允许从其他osd中获取,以分散读取压力从而提高性能。
Ceph可以使用一套存储系统同时提供对象存储、块存储、和文件系统存储3种功能。Ceph存储集群RADOS本身就是一个对象操作系统。基础库librados提供一系列的API允许用户操作对象和OSD、MON等进行通信。基于RADOS与librados库,Ceph通过RBD提供了一个标准的块设备接口,提供基于块设备的访问模式。
Ceph中的块设备称为image,是精简配置的,即按需分配,大小可调且将数据条带化存储到集群内的多个osd上。
条带化是指把连续的信息分片存储到多个设备中。当多个进程同时访问一个磁盘时,可能会出现磁盘冲突的问题。大多数磁盘对访问次数(每秒的io操作)和数据传输率(每秒传输的数据量)都有限制,当达到这些限制时,之后需要访问磁盘的进程就需要等待。这时就是所谓的磁盘冲突。避免磁盘冲突是优化io性能的一个重要目标,而优化io性能最有效的手段就是将io请求最大限度的进行平衡。
条带化是能够自动将io负载均衡到多个物理磁盘上的技术。通过将一块连续的数据分成多个相同大小的部分,并把它们分别存储到不同的磁盘上,条带化技术能使多个进程同时访问数据的不同部分而不会造成磁盘冲突,而且能获得最大限度上的io并行能力。
条带化技术能将多个磁盘合成一个卷,这个卷所能提供的速度比单个盘所能提供的速度要快很多。Ceph的块设备就对应于LVM的逻辑卷。块设备在创建时,可以使用如下参数实现条带化。
当处理大尺寸图像、大Swift对象(如视频)的时候,能看到条带化到一个对象集合的多个对象能带来显著的读写性能提升。当客户端把条带单元并行地写入相应对象时,就会有明显的写性能提升。因为对象映射到了不同的pg,进一步映射到不同的osd上,可以并行地以最大速度写入。由于到单一磁盘的写入受制于磁盘移动和存储设备带宽,Ceph把写入分布到多个对象(它们映射到不同的pg和osd中),这样就减少了寻道次数,并利用多个驱动器的吞吐量,以达到更高的读写速度。
使用Ceph的块设备有两种途径:
第二种方式主要是为虚拟机提供块存储设备。在虚拟机场景下,一般会用QEMU/KVM中的RBD驱动部署Ceph块设备。宿主机通过librbd向客户机提供块存储服务。QEMU可以直接通过librbd,像访问虚拟设备一样访问Ceph块设备。