Ceph 客户端所见的是一个完整的连续的二进制数据块,其大小为创建 RBD image 是设置的大小或者 resize 的大小,客户端可以从头或者从某个位置开始写入二进制数据。
librados 负责在 RADOS 中创建对象(object),其大小为 pool 的 order 决定,默认情况下 order = 22 此时 object 大小为 4MB;以及负责将客户端传入的二进制块条带化为若干个条带(stripe)。
librados 控制哪个条带由哪个 OSD 写入(条带 ---写入哪个----> object ----位于哪个 ----> OSD)
OSD 负责创建在文件系统中创建文件,并将 librados 传入的数据写入数据。
Ceph client 向一个 RBD image 写入二进制数据(假设 pool 的拷贝份数为 3):
(1)Ceph client 调用 librados 创建一个 RBD image,这时候不会做存储空间分配,而是创建若干元数据对象来保存元数据信息。
(2)Ceph client 调用 librados 开始写数据。librados 计算条带、object 等,然后开始写第一个 stripe 到特定的目标 object。
(3)librados 根据 CRUSH 算法,计算出 object 所对应的主 OSD ID,并将二进制数据发给它。
(4)主 OSD 负责调用文件系统接口将二进制数据写入磁盘上的文件(每个 object 对应一个 file,file 的内容是一个或者多个 stripe)。
(5)主 ODS 完成数据写入后,它使用 CRUSH 算啊计算出第二个OSD(secondary OSD)和第三个OSD(tertiary OSD)的位置,然后向这两个 OSD 拷贝对象。都完成后,它向 ceph client 反馈该 object 保存完毕。
(6)然后写第二个条带,直到全部写入完成。全部完成后,librados 还应该会做元数据更新,比如写入新的 size 等。
完整的过程(来源):
该过程具有强一致性的特点:
Ceph 的读写操作采用 Primary-Replica 模型,Client 只向 Object 所对应 OSD set 的 Primary 发起读写请求,这保证了数据的强一致性。
由于每个 Object 都只有一个 Primary OSD,因此对 Object 的更新都是顺序的,不存在同步问题。
当 Primary 收到 Object 的写请求时,它负责把数据发送给其他 Replicas,只要这个数据被保存在所有的OSD上时,Primary 才应答Object的写请求,这保证了副本的一致性。这也带来一些副作用。相比那些只实现了最终一致性的存储系统比如 Swift,Ceph 只有三份拷贝都写入完成后才算写入完成,这在出现磁盘损坏时会出现写延迟增加。
在 OSD 上,在收到数据存放指令后,它会产生2~3个磁盘seek操作:
把写操作记录到 OSD 的 Journal 文件上(Journal是为了保证写操作的原子性)。
把写操作更新到 Object 对应的文件上。
把写操作记录到 PG Log 文件上。
三、客户端请求流程(转的一只小江的博文,写的挺好的)
RADOS读对象流程
RADOS写对象操作流程
例子:
#!/usr/bin/env python
import sys,rados,rbd
def connectceph():
cluster = rados.Rados(conffile = '/root/xuyanjiangtest/ceph-0.94.3/src/ceph.conf')
cluster.connect()
ioctx = cluster.open_ioctx('mypool')
rbd_inst = rbd.RBD()
size = 4*1024**3 #4 GiB
rbd_inst.create(ioctx,'myimage',size)
image = rbd.Image(ioctx,'myimage')
data = 'foo'* 200
image.write(data,0)
image.close()
ioctx.close()
cluster.shutdown()
if __name__ == "__main__":
connectceph()
1. 首先cluster = rados.Rados(conffile = 'ceph.conf'),用当前的这个ceph的配置文件去创建一个rados,这里主要是解析ceph.conf中中的集群配置参数。然后将这些参数的值保存在rados中。
2. cluster.connect() ,这里将会创建一个radosclient的结构,这里会把这个结构主要包含了几个功能模块:消息管理模块Messager,数据处理模块Objector,finisher线程模块。
3. ioctx = cluster.open_ioctx('mypool'),为一个名字叫做mypool的存储池创建一个ioctx ,ioctx中会指明radosclient与Objector模块,同时也会记录mypool的信息,包括pool的参数等。
4. rbd_inst.create(ioctx,'myimage',size) ,创建一个名字为myimage的rbd设备,之后就是将数据写入这个设备。