1. librbd? &? librados介紹
Librbd模塊實現(xiàn)了RBD接口,其基于Librados實現(xiàn)了對RBD的基本操作。
1.1 架構(gòu)概述

????上層應(yīng)用訪問RBD塊設(shè)備有兩種途徑:librbd和krbd。其中l(wèi)ibrbd是一個基于librados的用戶態(tài)接口庫,而krbd是繼承在GNU/Linux內(nèi)核中的一個內(nèi)核模塊,通過用戶態(tài)的rbd命令行工具,可以將RBD塊設(shè)備映射為本地的一個塊設(shè)備文件。
??? 從RBD的架構(gòu)圖中可以看到,RBD塊設(shè)備由于元數(shù)據(jù)信息少且訪問不頻繁,故RBD在Ceph集群中不需要daemon守護(hù)進(jìn)程將元數(shù)據(jù)加載到內(nèi)存進(jìn)行元數(shù)據(jù)訪問加速,所有的元數(shù)據(jù)和數(shù)據(jù)操作直接與集群中的MON服務(wù)和OSD服務(wù)交互。其架構(gòu)圖如下:

????最上層是librbd層,模塊cls_rbd是一個Cls擴(kuò)展模塊,實現(xiàn)了RBD的元數(shù)據(jù)相關(guān)的操作。RBD的數(shù)訪問直接通過Librados來訪問。在最底層是OSDC層完成數(shù)據(jù)的發(fā)送。
1.2 源碼結(jié)構(gòu)
1.2.1 librbd
????在src/librbd和路徑下為librbd的源碼,其中分為.cc文件.h文件和子目錄,文件的大致內(nèi)容為:
??? 1..cc & .h 文件:對應(yīng)組件的源代碼,其中.h為頭文件,具體的實現(xiàn)在.cc中;
??? 2.子目錄:子目錄中文件為針對每個模塊的請求,如針對librbd/operation.cc組件,存在路徑operation,路徑中文件為

? ??????可見相應(yīng)目錄中為對應(yīng)組件的請求的實現(xiàn),這些請求發(fā)送到rados,進(jìn)而實現(xiàn)對osd的操作。
1.2.2 librados
????src/librados中為librados的源碼,內(nèi)容如下

??? 與librbd目錄下源碼相比,librados中的源碼內(nèi)容相對較少,librados.cc中負(fù)責(zé)接口的封裝,具體的實現(xiàn)細(xì)節(jié)在IoCtxImpl.cc中。
2? librados & librbd API介紹
2.1 源碼概覽
????ceph封裝的接口源碼位于src/pybind/路徑下,其中子目錄下的.pyx文件為api接口源碼,文件結(jié)構(gòu)如圖

2.2 主要操作介紹
對于librbd和librados提供的python api接口,本文重點關(guān)注以下操作:
1.? pool的創(chuàng)建和刪除;
2.? image的創(chuàng)建和刪除;
3.??snapshot的創(chuàng)建、利用snapshot進(jìn)行clone操作、snapshot的刪除;
4.??image flatten操作。
? ??下文將按照{(diào)create pool} -> {create image} -> {create snapshot} -> {image clone} -> {image flatten} -> {delete snapshot} -> {delete image} -> {delete pool}的順序來依次介紹上述操作的api接口調(diào)用。
2.2.1 準(zhǔn)備工作:創(chuàng)建鏈接
????在所有的操作之前,首先需要創(chuàng)建對集群的連接,才能進(jìn)行進(jìn)一步的操作:
import rados
import rbd
# connect to cluster
cluster = rados.Rados(conffile='/etc/ceph/ceph.conf')
cluster.connect()
?2.2.2?創(chuàng)建存儲池,并獲取ioctx
????創(chuàng)建了對集群的connection之后,就可以創(chuàng)建存儲池,并獲取ioctx:
# list pool
before_create = cluster.list_pools()
print("before create pools: {0}".format(before_create))
# create pool
cluster.create_pool(POOL)
# list pool
after_create = cluster.list_pools()
print("after create pools: {0}".format(after_create))
# get ioctx
ioctx = cluster.open_ioctx(POOL)
注意:
1. 這里create_pool()接口的輸入不包括pg和pgp,因為pgp和pg的數(shù)量在api中是根據(jù)ceph.conf中參數(shù)設(shè)置
# /etc/ceph/ceph.conf
osd_pool_default_pg_num = {int}
osd_pool_default_pgp_num = {int}
2.2.3 創(chuàng)建image
????創(chuàng)建存儲池并獲取ioctx,就可以開始創(chuàng)建rbd image了
rbd_inst = rbd.RBD()
size = 4 * 1024**3
rbd_inst.create(ioctx, “myimage”, size)
img_list = rbd_inst.list(ioctx)
print("after create images {0}".format(img_list))
注意:
1. 在哪個pool中創(chuàng)建image,ioctx就從哪個pool中獲??;
2. image創(chuàng)建必須顯式指定大小,size計算值為bytes。
2.2.4 創(chuàng)建snapshot
? ??rbd image創(chuàng)建之后,就可以根據(jù)image創(chuàng)建snapshot了。
# create snapshot
print("start to create snapshot")
rbd_img = rbd.Image(ioctx, name=IMG)
rbd_img.create_snap(SNP)
# list snapshot
print("list snapshot")
snap_list = rbd_img.list_snaps()
for item in snap_list:
??? print(item['name'])
注意:
1. 使用的是img.Image()類,與2.2.2區(qū)分;
2. snap_list的類型為rbd.SnapIterator(Image image),使用for循環(huán)迭代的元素為dict,其中有三個key:id(int: numeric identifier of the snapshot), size(int: size of theimage at the time of snapshot (in bytes)), name(str: name of the snapshot).
2.2.5?使用snapshot clone rbd image
? ??如果需要使用snapshot克隆image,需要首先對snapshot進(jìn)行保護(hù),之后才能進(jìn)行clone操作。
# protect snapshot
rbd_img.protect_snap(SNP)
is_protect = rbd_img.is_protected_snap(SNP)
print("{0} protected status:{1}".format(SNP, is_protect))
# image clone
print("clone image")
# parent_ioctx, parent_image_name,parent_snap_name, child_ioctx, child_image_name
rbd_inst.clone(ioctx, “myimage”,“mysnapshot”, ioctx, “clnimg”)
img_list = rbd_inst.list(ioctx)
print("after clone image: {0}".format(img_list))
注意:
????1. rbd.Image()需要與保護(hù)的snpashot一致(即在初始化時需要提供該snapshot隸屬的pool的ioctx和image的name);
????2. clone()函數(shù)必須傳入的五個變量:
(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的依賴。
# image flatten
print("flatten image")
clone_img = rbd.Image(ioctx, name=”clnimg”)
clone_img.flatten()
注意:
1. 因為需要訪問的是clone生成的新image,故需要新初始化一個rbd.Image()訪問。
2.2.7 刪除 snapshot / image / pool
# unprotect image
print("unprotect snapshot")
rbd_img.unprotect_snap(SNP)
is_protect = rbd_img.is_protected_snap(SNP)
print("{0} protected status:{1}".format(SNP, is_protect))
# purge snapshot
print("remove snapshot")
rbd_img.remove_snap(SNP)
# close image connection
rbd_img.clone()
clone_img.close()
# remove rbd image
print("remove rbd image")
rbd_inst.remove(ioctx, IMG)
rbd_inst.remove(ioctx, CLN)
注意:
1. 只有在snapshot下的所有image都進(jìn)行flatten操作之后,才能夠unprotect該snapshot;
2. 需要先關(guān)閉rbd.Image()對image的訪問之后,才能移除rbd image。
2.2.7 關(guān)閉鏈接,終止對集群的訪問
# close ioctx
print("close ioctx")
ioctx.close()
# delete pool
print("delete pool")
cluster.delete_pool(POOL)
after_delete = cluster.list_pools()
print("after delete pools:? {0}".format(after_delete))
# close connection
print("close connection")
cluster.shutdown()
注意:
1. 順序:{關(guān)閉ioctx} -> {刪除pool} -> {關(guān)閉cluster鏈接}.
2.4 參考資料
1. librbd(python)
http://docs.ceph.com/docs/master/rbd/api/librbdpy/
2. librados(python)
http://docs.ceph.com/docs/master/rados/api/python/