1. 程式人生 > >ceph(2)--Ceph RBD 接口和工具

ceph(2)--Ceph RBD 接口和工具

test length rem handle qemu-kvm 源文件 經典 ise ets

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

(1)安裝和部署

(2)Ceph RBD 接口和工具

(3)Ceph 物理和邏輯結構

(4)Ceph 的基礎數據結構

(5)Ceph 與 OpenStack 集成的實現

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

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

(8)關於Ceph PGs

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() doesnt search LD_LIBRARY_PATH
    # in addition, it doesnt 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 (連接-打開-使用-關閉)。該模塊除了提供主要三個類 Rados, Ioctx, 和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(ioctx, name, size, order=None, old_format=True, features=0, stripe_unit=0, stripe_count=0):創建一個 RBD Image
  • list(ioctx) 返回 RBD Image 的名稱列表
  • clone(p_ioctx, p_name, p_snapname, c_ioctx, c_name, features=0, order=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(ioctx, name) :刪除一個 RBD image。這可能會需要較長的時間,因為它需要等待組成該 image 的每個對象都被刪除。
  • rename(ioctx, src, dest):修改 RBD Image 的名稱

3.3 class rbd.Image

該類的一個實例代表一個 RBD image。它的方法用於對 image 的 I/O 操作和處理其 snapshot。它的主要方法包括:
  • class rbd.Image(ioctx, name, snapshot=None, read_only=False)
  • copy(dest_ioctx, dest_name) Copy 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(name) Mark a snapshot as protected. This means it can’t be deleted until it is unprotected.
  • read(offset, length, fadvise_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(data, offset, fadvise_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 的計算節點為例)

技術分享圖片

參考鏈接:

http://www.wzxue.com/rbdcache/

https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-rbd

http://docs.ceph.com/docs/master/rbd/rbd/

ceph(2)--Ceph RBD 接口和工具