Ceph的客户端是系统对外提供的功能接口,上层应用通过它访问ceph存储系统。
RADOS的客户端API
上述接口几乎包括了对Ceph集群和其中数据的所有访问功能。其中所谓集群的整体访问包括连接集群、创建存储池、删除存储池和获取集群状态等等。所谓对象访问是之对存储池中对象的访问,包括创建删除对象、向对象写数据或者追加数据和读对象数据等接口。上述功能通过Rados和IoCtx两个类实现,两个类的主要函数如图2所示(这里仅是示例,实际接口数量要多很多,具体参考源代码)。
图2 访问接口类图

1. librbd & librados介绍
Librbd模块实现了RBD接口,其基于Librados实现了对RBD的基本操作。
1.1 架构概述
上层应用层访问RBD块设备有两种途径:librbd和krbd。其中librbd是一个属于librados的用户态接口库,而krbd是继承在GNU/Linux内核中的一个内核模块,通过用户态的rbd命令行工具,可以将RBD块设备映射为本地的一个块设备文件。
从RBD的架构图中可以看到,RBD块设备由于元数据信息少且访问不频繁,故RBD在ceph集群中不需要daemon守护进程将元数据加载到内存进行元数据访问加速,所有的元数据和数据操作直接与集群中的MON服务和OSD服务交互。其架构图如下:
最上层是librbd层,模块cls_rbd是一个Cls扩展模块,实现了RBD的元数据相关的操作。RBD的数访问直接通过librados来访问。在最底层是OSDC层完成数据的发送。
1.2 源码结构
1.2.1 librbd
在src/librbd下为librbd的源码,其中分为.cc文件和.h文件和子目录,文件的大致内容为:
1. .cc和.h 文件:对应组件的源码;
2.子目录: 子目录中文件为针对每个模块的请求,如针对librbd/组件,存在路径为operation,路径中文件为:
可见相应目录中为对应组件的请求的实现,这些请求发送到rados,进而实现对osd的操作。
1.2.2 librados
src/librados中为librados的源码,内容如下:
与librbd目录下源码相比,librados中的源码内容相对较少,中负责接口的封装,具体的实现细节在中。
2 librados & librbd API介绍
2.1 源码概览
ceph封装的接口源码位于src/pybind路径下,其中子目录下的.pyx文件为api接口源码,文件结构如图:
2.2 主要操作介绍
对于librbd和librados提供的python api接口,本文重点关注以下操作:
1 1. pool的创建和删除;
2 2. image的创建和删除;
3 3. snapshot的创建、利用snapshot进行clone操作、snapshot的删除;
4 4. image flatten操作。
下文将按照{create pool} -> {create image} -> {create snapshot} -> {image clone} -> {image flatten} -> {delete snapshot} -> {delete image} -> {delete pool}的顺序来依次介绍上述操作的api接口调用。
2.2.1 准备工作:创建链接
在所有操作之前,首先需要创建对集群的连接,才能进行进一步的操作:
1 import rados
2 import rbd
3 # connect to cluster
4 cluster = rados.Rados(conffile='/etc/ceph/ceph.conf')
5 cluster.connect()
2.2.2 创建存储池,并获取ioctx
创建了对集群的connection之后,就可以创建存储池,并获取ioctx。
1 #list pool
2 before_create = cluster.list_pools()
3 print("before create pools:{0}.format(before_create))
4
5 #create pool
6 cluster.create_pool(POOL)
7
8 #list pool
9 after_create = cluster.list_pools()
10 print("after create pools:{0}.format(after_create))
11
12 #get_ioctx
13 ioctx = cluster.open_ioctx(POOL)
注意:
这里create_pool()接口的输入不包括pg和pgp,因为pgp和pg的数量在api中是根据ceph.conf中参数设置。
1 # /etc/ceph/ceph.conf
2 osd_pool_default_pg_num = {int}
3 osd_pool_default_pgp_num = {int}
2.2.3 创建image
创建存储池并获取ioctx,就可以开始创建rbd image了
1 rbd_inst = rbd.RBD()
2 size = 4* 1024 * 3
3
4 rbd_inst.create(ioctx, "myimage", size)
5 img_list = rbd_inst.list( ioctx)
6 print("after create images {0}".format(img_list))
注意:
1.在哪个pool中创建image,ioctx就从哪个pool中获取;
2.image创建必须显示指定大小,size计算值为bytes。
2.2.4 创建snapshot
rbd image创建之后,就可以根据image创建snapshot了。
1 # create snapshot
2 print("start to create snapshot")
3 rbd_img = rbd.Image(ioctx, name=IMG)
4 rbd_img.create_snap(SNP)
5 # list snapshot
6 print("list snapshot")
7 snap_list = rbd_img.list_snaps()
8 for item in snap_list:
9 print(item['name'])
注意:
1.使用的是img.Image()类,与2.2.2区分;
2.snap_list的类型为rbd.SnapIterator(Image image),使用for循环迭代的元素为dict,其中有三个key: id(int:numeric identifier of the snapshot), size(int, size of the image at the time of snapshot (in bytes)); name ( str: name of the snapshot).
2.2.5 使用snapshot clone rbd image
如果需要使用snapshot克隆image,需要首先对snapshot进行保护,之后才能进行clone操作。
1 # protect snapshot
2 rbd_img.protect_snap(SNP)
3 is_protect = rbd_img.is_protected_snap(SNP)
4 print("{0} protected status:{1}".format(SNP, is_protect))
5 # image clone
6 print("clone image")
7 # parent_ioctx, parent_image_name,parent_snap_name, child_ioctx, child_image_name
8 rbd_inst.clone(ioctx, “myimage”,“mysnapshot”, ioctx, “clnimg”)
9 img_list = rbd_inst.list(ioctx)
注意:
1.rbd_image() 需要与保护的snapshot一致(即在初始化时需要提供该snapshot隶属的pool的ioctx和image的name):
2.clone() 函数必须传入的五个变量
(1) parent_ioctx: snapshot 父类的ioctx;
(2) parent_image_name: 父image的名称;
(3) parent_snap_name: 父snapshot的名称;
(4) child_ioctx: 子image的ioctx;
(5) child_image_name: 子image名称;
如果clone之后的image和原来的image位于同一个pool,则两者的ioctx相同,反之需要从子image所属的pool获取子ioctx。
2.2.6 image flatten
image flatten操作使clone生成的rbd image摆脱对snapshot的依赖
1 # image flatten
2 print("flatten image")
3 clone_img = rbd.Image(ioctx, name=”clnimg”)
4 clone_img.flatten()
注意:
因为需要访问的是clone生成的薪image,故需要新初始化一个rbd.Image()访问。
2.2.7 删除 snapshot / image / pool
1 # unprotect image
2 print("unprotect snapshot")
3 rbd_img.unprotect_snap(SNP)
4 is_protect = rbd_img.is_protected_snap(SNP)
5 print("{0} protected status:{1}".format(SNP, is_protect))
6
7 # purge snapshot
8 print("remove snapshot")
9 rbd_img.remove_snap(SNP)
10
11 # close image connection
12 rbd_img.clone()
13 clone_img.close()
14
15 # remove rbd image
16 print("remove rbd image")
17 rbd_inst.remove(ioctx, IMG)
18 rbd_inst.remove(ioctx, CLN)
注意:
1.只有在snapshot下的所有image都进行flatten操作之后,才能够unprotect该snapshot;
2.需要先关闭rbd.Image()对image的访问之后,才能移除rbd image。
2.2.8 关闭链接,终止对集群的访问
1 # close ioctx
2 print("close ioctx")
3 ioctx.close()
4
5 # delete pool
6 print("delete pool")
7 cluster.delete_pool(POOL)
8 after_delete = cluster.list_pools()
9 print("after delete pools: {0}".format(after_delete))
10
11 # close connection
12 print("close connection")
13 cluster.shutdown()
注意:
顺序:
{关闭ioctx} -> {删除pool}-> {关闭cluster链接}