理解 OpenStack + Ceph (3):Ceph RBD 接口和工具 [Ceph RBD API and Tools]

本系列文章會深刻研究 Ceph 以及 Ceph 和 OpenStack 的集成:html

(1)安裝和部署python

(2)Ceph RBD 接口和工具linux

(3)Ceph 物理和邏輯結構git

(4)Ceph 的基礎數據結構github

(5)Ceph 與 OpenStack 集成的實現編程

(6)QEMU-KVM 和 Ceph RBD 的 緩存機制總結ubuntu

(7)Ceph 的基本操做和常見故障排除方法centos

(8)關於Ceph PGsapi

 

  Ceph 做爲一個統一的分佈式存儲,其一大特點是提供了豐富的編程接口。咱們來看看下面這張經典的圖:緩存

其中,librados 是 Ceph 的基礎接口,其它的接口好比 RADOSGW, RBD 和 CephFS 都是基於 librados 實現的。本文試着分析下 Ceph 的各類接口庫和經常使用的工具。

1 librados

1.1 librados 概述

  Ceph 提供一個消息層協議(messaging layer protocol)使得 ceph 客戶端能夠和 Ceph Monitor 以及 Ceph OSD Daemon 交互。librados 就是一個該協議的編碼庫形式的實現。全部的 Ceph clients 要麼使用 librados 要麼使用其封裝的更高層 API 與對象存儲進行交互。好比,librbd 使用 librados 提供 Ceph 客戶端與 RBD 交互 API。

librados 是使用 C++ 實現的。它實現了 Ceph 的一個私有協議,使得客戶端能夠直接、同步或者異步、並行地和 MON  和 OSD 服務通訊,來執行以下操做:

  • Pool Operations
  • Snapshots
  • Read/Write Objects
  • Create or Remove
  • Entire Object or Byte Range
  • Append or Truncate
  • Create/Set/Get/Remove XATTRs
  • Create/Set/Get/Remove Key/Value Pairs
  • Compound operations and dual-ack semantics

librados 於 OSD 交互的示例:

同時也提供 C, python,Java 和 PHD 語言的綁定。使用它以前,你須要安裝它:

  • 安裝 C/C++ 版本的 librados,好比在 Ubuntu 環境中,運行 sudo apt-get install librados-dev,而後你就會在 /usr/include/rados 目錄中找到C/C++的頭文件。
  • 使用 Python 版本的 librados,好比在 Ubuntu 環境中,運行 sudo apt-get install python-rados。具體請見 1.1.2 部分。

一個 Ceph client 經過 librados 存放或者讀取數據塊到 Ceph 中,須要通過如下步驟:

  1. Client 調用 librados 鏈接到 Ceph monitor,獲取 Cluster map。
  2. 當 client 要讀或者寫數據時,它建立一個與 pool 的 I/O Context。該 pool 關聯有 ruleset,它定義了數據在 Ceph 存儲集羣中是怎麼存放的。
  3. client 經過 I/O Context 提供 object name 給 librados,它使用該 object name 和 cluster map 計算出 PG 和 OSD 來定位到數據的位置。
  4. client 直接和 OSD Deamon 交互來讀或者寫數據。

可見,第一步中,ceph client 須要知道 Ceph Minotor 的訪問方法,以及用戶的身份信息,而這些信息每每是放在 ceph 配置文件中,好比:

[global]
mon host = 192.168.1.1
keyring = /etc/ceph/ceph.client.admin.keyring

1.2 librados for phthon

    Python rados 模塊是 librados 的 Python 的很薄的封裝,它在 github 上的源文件在 /src/pybind/rados.py, 安裝到 Ubunt 後的源文件在 /usr/lib/python2.7/dist-packages/rados.py

(1)要使用 rados.py 模塊,在你的代碼中 import rados

(2)建立一個 cluster handler,你須要提供 ceph.conf 文件和 keystring

import rados, sys

#Create Handle Examples. 你有多種方式來指定 ceph.conf
(1)cluster = rados.Rados(conffile='ceph.conf') #默認路徑的 ceph.conf
(2)cluster = rados.Rados(conffile=sys.argv[1]) #指定路徑的 ceph.conf
(3)cluster = rados.Rados(conffile = 'ceph.conf', conf = dict (keyring = '/path/to/keyring')) #默認路徑的 ceph.conf 和指定的 keystring

(3)鏈接到 ceph cluster

cluster.connect()

(4)獲取 ceph cluster 的信息,以及操做 pool

cluster_stats = cluster.get_cluster_stats()
pools = cluster.list_pools()
cluster.create_pool('test')
cluster.delete_pool('test')

(5)向 ceph 集羣讀寫數據,須要有一個 I/O Context (ioctx)

ioctx = cluster.open_ioctx('data')

(6)而後就能夠讀寫數據了

ioctx.write_full("hw", "Hello World!") #向 「hw」 object 寫入 "Hello World!"
print ioctx.read("hw") #讀取 」hw「 object 的數據
ioctx.remove_object("hw") #刪除 「hw」 object

更多的信息,能夠參考 LIBRADOS (PYTHON)

2. librbd:Go 語言實現的訪問 Ceph RBD 的接口

RBD 是  Rados Block Device  的縮寫,而 librbd 是訪問 RBD 的庫。它調用 librados C 語言綁定,以及與Linux 內核中的 rbd 模塊通訊,來提供 RMD image 的建立、刪除、映射和刪除映射等操做。其中,建立和刪除等操做是調用 librados,而將 RDB Image 映射到主機上則是調用 Linux rbd 內核模塊:

// MapDevice maps an image to a device on the host. Returns the device path and
// any errors. On error, the device path will be blank.
func (img *Image) MapDevice() (string, error)

  librbd 的源代碼在 https://github.com/contiv/librbd,是使用 Go 語言實現的,調用 librados 的 C 庫以及linux 內核模塊,在 Ubuntu 14 上它的文件在 /usr/lib/x86_64-linux-gnu/librbd.so.1。它支持:

  1. Open a connection to the rbd pool
  2. Creates an image called test (Removing it before if necessary)
  3. Maps it to a block device on the local host
  4. Creates a snapshot of the image, called test-snap.
  5. Lists and prints the snapshots available.

3. rbd.py:librbd 的 python 封裝

   rbd python 模塊 rbd.py 的源代碼在 https://github.com/ceph/ceph/blob/master/src/pybind/rbd.py,它是 librbd 的一個 python 封裝。

def load_librbd():
    """
    Load the librbd shared library.
    """
    librbd_path = find_library('rbd')
    if librbd_path:
        return CDLL(librbd_path)

    # try harder, find_library() doesn't search LD_LIBRARY_PATH
    # in addition, it doesn't seem work on centos 6.4 (see e46d2ca067b5)
    try:
        return CDLL('librbd.so.1')
    except OSError as e:
        raise EnvironmentError("Unable to load librbd: %s" % e)

   它只封裝了 librbd 中的同步操做。它主要提供了 RBD、Image 和 SnapIterator 三個類。在 Ubuntu 上,安裝 python-rbd 包就能夠在你的機器上找到該文件:/usr/lib/python2.7/dist-packages/rbd.py。 提供相似文件訪問的方式去訪問 ceph RBD image (鏈接-打開-使用-關閉)。該模塊除了提供主要三個類 RadosIoctx, 和Image (Rados 對應於 ceph 集羣;Ioctx 對應於 pool;Image 對應於 RBD Image)之外,還封裝了 librdb 的返回值做爲 Error 類。rbd 模塊還提供 Error 類的具體的子類,好比 PermissionError 和 IOError。

3.1 基本流程

#使用 rbd 以前,鏈接到 RADOS,並打開一個 IO context (和特定 pool 相關)
cluster = rados.Rados(conffile='my_ceph.conf')
cluster.connect()
ioctx = cluster.open_ioctx('mypool')

#初始化一個 RBD 對象
rbd_inst = rbd.RBD()
size = 4 * 1024**3  # 4 GiB
#建立 image
rbd_inst.create(ioctx, 'myimage', size)

#初始化 image 對象
image = rbd.Image(ioctx, 'myimage')
#準備 600 個字符的數據
data = 'foo' * 200
#寫入數據
image.write(data, 0)

#關閉 image 對象
image.close()
#關閉 IO Context
ioctx.close()
#關閉鏈接
cluster.shutdown()

更優化的編碼方式:

with rados.Rados(conffile='my_ceph.conf') as cluster:
    with cluster.open_ioctx('mypool') as ioctx:
        rbd_inst = rbd.RBD()
        size = 4 * 1024**3  # 4 GiB
        rbd_inst.create(ioctx, 'myimage', size)
        with rbd.Image(ioctx, 'myimage') as image:
            data = 'foo' * 200
            image.write(data, 0)

3.2 class rbd.RBD 類

class  rbd. RBD 該類封裝了 librbd 的 CURD 操做,包括:
  • create(ioctxnamesizeorder=Noneold_format=Truefeatures=0stripe_unit=0stripe_count=0):建立一個 RBD Image
  • list(ioctx)  返回 RBD Image 的名稱列表
  • clone(p_ioctxp_namep_snapnamec_ioctxc_namefeatures=0order=None):克隆 Image 的snapshot 到一個 COW 克隆
    • features (int) – bitmask of features to enable; if set, must include layering
    • order (int) – the image is split into (2**order) byte objects
  • remove(ioctxname) :刪除一個 RBD image。這可能會須要較長的時間,由於它須要等待組成該 image 的每一個對象都被刪除。
  • rename(ioctxsrcdest):修改 RBD Image 的名稱

3.3  class rbd.Image

該類的一個實例表明一個 RBD image。它的方法用於對 image 的 I/O 操做和處理其 snapshot。它的主要方法包括:
  • class rbd.Image(ioctxnamesnapshot=Noneread_only=False)
  • copy(dest_ioctxdest_nameCopy the image to another location.
  • create_snap(name)  Create a snapshot of the image
  • flatten()  Flatten clone image (copy all blocks from parent to child)
  • list_snaps()  Iterate over the snapshots of an image
  • protect_snap(nameMark a snapshot as protected. This means it can’t be deleted until it is unprotected.
  • read(offsetlengthfadvise_flags=0)  Read data from the image. 
  • remove_snap(name Delete a snapshot of the image
  • resize(size Change the size of the image
  • rollback_to_snap(name Revert the image to its contents at a snapshot.
  • set_snap(name)   Set the snapshot to read from. Writes will raise ReadOnlyImage while a snapshot is set. Pass None to unset the snapshot (reads come from the current image) , and allow writing again.
  • size( Get the size of the image  in bytes
  • stat( Get information about the image.
  • unprotect_snap(name)   Mark a snapshot unprotected. This allows it to be deleted if it was protected.
  • write(dataoffsetfadvise_flags=0 Write data to the image.
  • parent_info  Get information about a cloned image’s parent (if any)

4. Ceph RBD 對 Linux 主機和虛機的支持,以及 Linux RBD 內核模塊

  使用 Ceph 的塊存儲有兩種路徑:

  • 一種是利用QEMU走librbd路徑,主要爲虛擬機提供塊存儲設備
  • 另外一種是使用 kernel module,走 kernel 的路徑,主要爲Host提供塊設備支持。

  兩種途徑的接口實現不徹底相同。就目前來講,前者是目前更穩定的途徑,也是Ceph全部應用場景中最普遍使用的。網上多篇文章認爲目前內核模塊尚不穩定,建議儘可能不要使用。

4.1 qemu-kvm 使用librbd 訪問 RBD

QEMU 經過 librbd 庫訪問 RBD,而 librbd 是調用 librados。

QEMU 對 librbd 的調用關係可使用 ldd 命令查看:

root@compute1:~# ldd /usr/bin/qemu-system-x86_64 | grep rbd
        librbd.so.1 => /usr/lib/x86_64-linux-gnu/librbd.so.1 (0x00007f76ef9e8000)

qemu-img 命令支持直接建立 rbd 鏡像,好比:

qemu-img create -f rbd rbd:vmimages/ubuntu-newdrive 2G

而後可使用 virsh 將它定義爲一個 device,下面是 device.xml 文件:

<disk type='network' device='disk'>
  <driver name='qemu' type='raw'/>
  <auth username='vmimages'>
    <secret type='ceph' uuid='76e3a541-b997-58ac-f7bd-77dd7d4347cb'/>
  </auth>
  <source protocol='rbd' name='vmimages/ubuntu-newdrive'>
    <host name='192.168.0.100' port='6789'/>
  </source>
  <target dev='vdc' bus='virtio'/>
</disk>

而後該 device 添加到虛機中便可。虛機訪問 RBD 設備是經過:

   咱們知道,Linux Kernel 中的塊設備驅動是 virtio_blk,它會對虛機的塊設備各類請求封裝成一個消息經過 virtio 框架提供的隊列發送到 QEMU 的 IO 線程,QEMU 收到請求後會轉給相應的 QEMU Block Driver 來完成請求,當使用 RDB 時 QEMU block driver 會調用 librbd 來訪問 Ceph RDB 設備。

4.2 使用內核模塊,將 RBD 設備映射給主機

    rbd 工具也能夠調用 Linux RBD 內核模塊來將 RBD Image 掛載到Linux 主機上做爲一個新的設備。一開始,該內核模塊並無被加載,可是在執行了 rbd map 操做後,libceph 模塊自動被加載:

root@compute2:~# lsmod | grep rbd                      #此時,rbd 內核模塊尚未被加載

root@compute2:~# rbd -n client.cinder map vms/smb01_d1 #作 rbd map 操做
/dev/rbd1
root@compute2:~# lsmod | grep rbd                      #此時, rbd 內核模塊已經被自動加載
rbd 63787 1
libceph 225461 1 rbd
root@compute2:~# rmmod rbd                             #這時候,rbd 內核模塊也沒法被卸載
rmmod: ERROR: Module rbd is in use

在看 rbd 的幫助,咱們要能夠看到 map,unmap,showmapped 命令確實調用的是 rbd 內核模塊:

map <image-name>                            map image to a block device using the kernel
unmap <device>                              unmap a rbd device that was mapped by the kernel
showmapped                                  show the rbd images mapped by the kernel

過程:

#建立一個 RDB Image
root@compute1:~# rbd -n client.cinder create vms/smb01_d2 --size 1000 --image-format 2
#將 Image map 給主機  
root@compute1:
~# rbd -n client.cinder map vms/smb01_d2 /dev/rbd2

#列表已經被映射的 RDB Image
root@compute1:~# rbd showmapped
id pool image snap device
1 vms smb01_d1 - /dev/rbd1
2 vms smb01_d2 - /dev/rbd2
3 vms smb01_d3 - /dev/rbd3
#unmap
root@compute1:~# rbd unmap /dev/rbd1
root@compute1:~# rbd unmap /dev/rbd2
root@compute1:~# rbd unmap /dev/rbd3

其實,rbd 是和 /sys/bus/rbd 交互來完成map:

access("/sys/bus/rbd", F_OK)            = 0
socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, 15) = 3
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\r\0\0\0\0\0\0\0\3000b\261\375\177\0\0", 16) = 0
bind(3, {sa_family=AF_NETLINK, pid=0, groups=00000002}, 12) = 0
getsockname(3, {sa_family=AF_NETLINK, pid=6958, groups=00000002}, [12]) = 0
setsockopt(3, SOL_SOCKET, SO_PASSCRED, [1], 4) = 0
open("/sys/bus/rbd/add_single_major", O_WRONLY) = -1 ENOENT (No such file or directory)
open("/sys/bus/rbd/add", O_WRONLY)      = 4
write(4, "9.115.251.194:6789,9.115.251.195:6789,9.115.251.218:6789 nam"..., 101) = 101
close(4)    

你甚至能夠直接對 /sys/bus/rbd 操做來 map,showmapped 和 unmap RBD Image:

root@compute1:~#echo "10.20.30.40  name=admin rbd foo" | sudo tee /sys/bus/rbd/add #map

root@compute1:~# ls /sys/bus/rbd/devices                                           #showmapped 1
root@compute1:~# echo 1 | sudo tee /sys/bus/rbd/remove                             #unmap 1
root@compute1:~# ls /sys/bus/rbd/devices               

相信能夠參考 RBD KERNEL OBJECT OPERATIONS 和 這篇文章

5. rbd 命令行

  Ceph 提供 rbd 做爲一個操做 ceph rados 塊設備(RBD)image 的 工具,根據調用的底層模塊,其功能可分爲兩部分:

  • 常規的 create, list, introspect 和 remove block device images,以及 clone images, create snapshots, rollback an image to a snapshot, view a snapshot 等操做,是經過調用 librados 來實現的。其調用層次爲 rbd -> librbd -> librados:
root@compute1:~# which rbd
/usr/bin/rbd
root@compute1:~# ldd /usr/bin/rbd | grep rbd
        librbd.so.1 => /usr/lib/x86_64-linux-gnu/librbd.so.1 (0x00007fcf7ae62000)
root@compute1:~# ldd /usr/lib/x86_64-linux-gnu/librbd.so.1 | grep rados
        librados.so.2 => /usr/lib/x86_64-linux-gnu/librados.so.2 (0x00007f29b543f000)
  • 將 RBD image map 給 Linux 主機,以及從 Linux 主機 unmap等操做,是經過和 Linux rbd 內核模塊通訊來實現的。詳情請參考 1.4.2 部分。

  在 Ubuntu 上,你安裝了 ceph-common 就能夠獲得該工具,其命令行格式爲:

rbd [ -c  ceph.conf ] [ -m  monaddr ] [ -p | –pool  pool ] [ –size  size ] [ –order  bits ] [  command ... ]
 
  部分使用示例:
  • 在 pool ‘mypool’ 中建立 100 GB 的 rbd image ‘myimage’:  rbd create mypool/myimage --size 102400
  • 在默認的 ‘rbd’ pool 中建立 image:rbd create myimage --size 102400
  • 建立對象大小爲 8MB 的 image:rbd create mypool/myimage --size 102400 --order 23
這裏的 order 參數比較特別,它和 ceph 的對象條帶化有關。ceph 客戶端的一個數據塊,會被條帶化成多個小的對象,被保存在 Ceph 分佈式對象存儲(RADOS) 中。這樣的話,對 image 的讀和寫操做能夠被分散到集羣的多個節點上,一般來說這樣能夠防止某個 image 很是大或者很是忙時單個節點稱爲性能瓶頸。
Ceph 的條帶化行爲受三個參數控制:
  • order:被條帶化的對象的大小爲 2^[order] bytes。默認的 oder 爲 22,這時候對象大小爲4MB。
  • stripe_unit:每一個 [stripe_unit]的連續字節會被相連的保存到同一個對象中,直到去寫下一個對象
  • stripe_count:在寫入了 [stripe_unit] 字節到 [stripe_unit]各對象後,ceph 又從新從第一個對象開始寫下一個條帶,直到該對象達到了它的最大size。這時候,ceph 轉移到下 [stripe_unit] 個對象。
默認的時候,[stripe_unit] 等於 object size;stripe_count 爲1. 要設置其餘的 [ stripe_unit] 值,須要Ceph v0.53 版本及之後版本對 STRIPINGV2 的支持以及使用 format 2 image 格式。  
  • 刪除 image:rbd rm mypool/myimage
  • 建立 snapshot:rbd snap create mypool/myimage@mysnap
  • 建立 clone:rbd clone mypool/myimage@mysnap otherpool/cloneimage
  • 查看snapshot 的全部 clone:rbd children mypool/myimage@mysnap
  • 刪除 snapshot:rbd snap rm mypool/myimage@mysnap
  • 將 image map 到 linux 主機:rbd map mypool/myimage 
  • 將 image 從主機上刪除 (unmap):rbd unmap /dev/rbd0

6. 總結一下各模塊的關係(以 OpenStack 的計算節點爲例)

參考連接:

相關文章
相關標籤/搜索