理解 OpenStack + Ceph (5):OpenStack 與 Ceph 之間的集成 [OpenStack Integration with Ceph]


理解 OpenStack + Ceph 系列文章:

(1)安裝和部署

(2)Ceph RBD 接口和工具

(3)Ceph 物理和邏輯結構

(4)Ceph 的基礎數據結構

(5)Ceph 與 OpenStack 集成的實現

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

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

(8)關於Ceph PGs

1. Glance 與 Ceph RBD 集成

1.1 代碼

Kilo 版本中,glance-store 代碼被從 glance 代碼中分離出來了,地址在 https://github.com/openstack/glance_store

Glance 中與 Ceph 相關的配置項: 

配置項 含義 默認值
rbd_pool 保存rbd 卷的ceph pool 名稱 images
rbd_user rbd user id,僅僅在使用 cephx 認證時使用 none。讓 librados 根據 ceph 配置文件決定
rbd_ceph_conf Ceph 配置文件的完整路徑 /etc/ceph/ceph.conf
rbd_store_chunk_size 卷會被分成對象的大小(單位為 MB) 64
 
Glance_store 使用 rbd 和 rados 模塊:
    import rados
    import rbd
 
Glance_store 中新增了文件 /glance_store/_drivers/rbd.py,其中實現了三個類:
  • class StoreLocation(location.StoreLocation) #表示 Glance image 在 RBD 中的location,格式為 "rbd://<FSID>/<POOL>/<IMAGE>/<SNAP>"
  • class ImageIterator(object) #實現從 RBD image 中讀取數據塊
  • class Store(driver.Store): #實現將 RBD 作為Nova 鏡像后端(backend)

class Store(driver.Store) 實現的主要方法:

(1)獲取 image 數據:根據傳入的Glance image location,獲取 image 的 IO Interator

def get(self, location, offset=0, chunk_size=None, context=None):
    return (ImageIterator(loc.pool, loc.image, loc.snapshot, self), self.get_size(location))
1. 根據 location,定位到 rbd image
2. 調用 image.read 方法,按照 chunk size 讀取 image data
3. 將 image data 返回調用端

(2)獲取 image 的 size

def get_size(self, location, context=None):
1. 找到 store location:loc = location.store_location
2. 使用 loc 中指定的 pool 或者配置的默認pool
3. 建立 connection 和 打開 IO Context:with rbd.Image(ioctx, loc.image, snapshot=loc.snapshot) as image
4. 獲取 image.stat() 並獲取 “size”

(3)添加 image

def add(self, image_id, image_file, image_size, context=None):
1. 將 image_id 作為 rbd image name:image_name = str(image_id)
2. 創建 rbd image:librbd.create(ioctx, image_name, size, order, old_format=False, features=rbd.RBD_FEATURE_LAYERING)。如果 rbd 支持 RBD_FEATURE_LAYERING 的話,創建一個 clonable snapshot:rbd://fsid/pool/image/snapshot;否則,創建一個 rbd image:rbd://image        
3. 調用 image.write 方法將 image_file 的內容按照 chunksize 依次寫入 rbd image
4. 如果要創建 snapshot 的話,調用 image.create_snap(loc.snapshot) 和 image.protect_snap(loc.snapshot) 創建snapshot

(4)刪除 image

def delete(self, location, context=None):
1. 根據 location 計算出 rbd image,snapshot 和 pool
2. 如果它是一個 snapshot (location 是 rbd://fsid/pool/image/snapshot),則 unprotect_snap,再 remove_snap,然后刪除 image;這時候有可能出錯,比如存在基於該 image 的 volume。
3. 如果它不是一個 snapshot (location 是 rbd://image),直接刪除 rbd image。這操作也可能會出錯。

1.2 使用

(1)使用 Ceph RDB 作為后端存儲創建一個 Glance image:
root@controller:~/s1# glance image-show 71dc76da-774c-411f-a958-1b51816ec50f
+------------------+----------------------------------------------------------------------------------+
| Property         | Value                                                                            |
+------------------+----------------------------------------------------------------------------------+
| checksum         | 56730d3091a764d5f8b38feeef0bfcef                                                 |
| container_format | bare                                                                             |
| created_at       | 2015-09-18T10:22:49Z                                                             |
| direct_url         | rbd://4387471a-ae2b-47c4-b67e-9004860d0fd0/images/71dc76da-774c-                 |
|                        | 411f-a958-1b51816ec50f/snap   
 
可見該Glance image 在 Ceph 中其實是個 RBD snapshot,其父 image 為 rbd://4387471a-ae2b-47c4-b67e-9004860d0fd0/images/71dc76da-774c-411f-a958-1b51816ec50f/,snapshot 名稱為 “snap”。
 
查看 rdb:
root@ceph1:~# rbd info images/71dc76da-774c-411f-a958-1b51816ec50f
rbd image '71dc76da-774c-411f-a958-1b51816ec50f':
size 40162 kB in 5 objects
order 23 (8192 kB objects)
block_name_prefix: rbd_data.103d2246be0e
format: 2
features: layering
Glance 自動創建了它的 snapshot:
root@ceph1:~# rbd snap ls images/71dc76da-774c-411f-a958-1b51816ec50f
SNAPID NAME      SIZE
     2 snap  40162 kB 
查看該 snapshot:
root@ceph1:~# rbd info images/71dc76da-774c-411f-a958-1b51816ec50f@snap
rbd image '71dc76da-774c-411f-a958-1b51816ec50f':
size 40162 kB in 5 objects
order 23 (8192 kB objects)
block_name_prefix: rbd_data.103d2246be0e
format: 2
features: layering
protected: True

基於該 Glance image 創建的 Cinder volume 都是該 snapshot 的 clone:

root@ceph1:~# rbd children images/71dc76da-774c-411f-a958-1b51816ec50f@snap
volumes/volume-65dbaf38-0b9d-4654-bba4-53f12cc906e3
volumes/volume-6868a043-1412-4f6c-917f-bbffb1a8d21a

此時如果試着去刪除該 image,則會報錯:

The image cannot be deleted because it is in use through the backend store outside of Glance.
這是因為該 image 的 rbd image 的 snapshot 被使用了。

2. Cinder 與 Ceph RBD 的集成

OpenStack Cinder 組件和 Ceph RBD 集成的目的是將 Cinder 卷(volume)保存在 Ceph RBD 中。當使用 Ceph RBD 作為 Cinder 的后端存儲時,你不需要單獨的一個 Cinder-volume 節點.

2.1 配置項

配置項 含義 默認值
rbd_pool 保存rbd 卷的ceph pool 名稱 rbd
rbd_user 訪問 RBD 的用戶的 ID,僅僅在使用 cephx 認證時使用 none
rbd_ceph_conf Ceph 配置文件的完整路徑 ‘’,表示使用 librados 的默認ceph 配置文件
rbd_secret_uuid rbd secret uuid  
rbd_flatten_volume_from_snapshot RBD Snapshot 在底層會快速復制一個元信息表,但不會產生實際的數據拷貝,因此當從 Snapshot 創建新的卷時,用戶可能會期望不要依賴原來的 Snapshot,這個選項開啟會在創建新卷時對原來的 Snapshot 數據進行拷貝來生成一個不依賴於源 Snapshot 的卷。 false
rbd_max_clone_depth

卷克隆的最大層數,超過的話則使用 fallter。設為 0 的話,則禁止克隆。

與上面這個選項類似的原因,RBD 在支持 Cinder 的部分 API (如從 Snapshot 創建卷和克隆卷)都會使用 rbd clone 操作,但是由於 RBD 目前對於多級卷依賴的 IO 操作不好,多級依賴卷會有比較嚴重的性能問題。因此這里設置了一個最大克隆值來避免這個問題,一旦超出這個閥值,新的卷會自動被 flatten。

5
rbd_store_chunk_size 每個 RBD 卷實際上就是由多個對象組成的,因此用戶可以指定一個對象的大小來決定對象的數量,默認是 4 MB 4
rados_connect_timeout 連接 ceph 集群的超時時間,單位為秒。如果設為負值,則使用默認 librados 中的值 -1

從這里也能看出來,

(1)Cinder 不支持單個 volume 的條帶化參數設置,而只是使用了公共配置項 rbd_store_chunk_size 來指定 order。

(2)Cinder 不支持卷被附加到客戶機時設置緩存模式。

2.2 代碼

Cinder 使用的就是之前介紹過的 rbd phthon 模塊:

import rados
import rbd

它實現了以下主要接口。

2.2.1 與 RBD 的連接

(1)初始化連接到 RBD 的連接
def initialize_connection(self, volume, connector):
hosts, ports = self._get_mon_addrs() #調用 args = ['ceph', 'mon', 'dump', '--format=json'] 獲取 monmap,再獲取 hosts 和 ports data = { 'driver_volume_type': 'rbd', 'data': { 'name': '%s/%s' % (self.configuration.rbd_pool, volume['name']), 'hosts': hosts, 'ports': ports, 'auth_enabled': (self.configuration.rbd_user is not None), 'auth_username': self.configuration.rbd_user, 'secret_type': 'ceph', 'secret_uuid': self.configuration.rbd_secret_uuid, } }

(2)連接到 ceph rados

client = self.rados.Rados(rados_id=self.configuration.rbd_user, conffile=self.configuration.rbd_ceph_conf)
client.connect(timeout= self.configuration.rados_connect_timeout) ioctx = client.open_ioctx(pool)

(3)斷開連接

ioctx.close()
client.shutdown()

2.2.2 創建卷

Cinder API:create_volume
調用的是 RBD 的 create 方法來創建 RBD image:
with RADOSClient(self) as client:
            self.rbd.RBD().create(client.ioctx,
                                  encodeutils.safe_encode(volume['name']),
                                  size,
                                  order,
                                  old_format=old_format,
                                  features=features)

2.2.3 克隆卷

#創建克隆卷
def create_cloned_volume(self, volume, src_vref): 

# 因為 RBD 的 clone 方法是基於 snapshot 的,所有 cinder 會首先創建一個 snapshot,再創建一個 clone。 if CONF.rbd_max_clone_depth <= 0: #如果設置的 rbd_max_clone_depth 為負數,則做一個完整的 rbd image copy vol.copy(vol.ioctx, dest_name) depth = self._get_clone_depth(client, src_name) #判斷 volume 對應的 image 的 clone depth,如果已經達到 CONF.rbd_max_clone_depth,則需要做 flattern src_volume = self.rbd.Image(client.ioctx, src_name) #獲取 source volume 對應的 rbd image #如果需要 flattern, _pool, parent, snap = self._get_clone_info(src_volume, src_name) #獲取 parent 和 snapshot src_volume.flatten() # 將 parent 的data 拷貝到該 clone 中 parent_volume = self.rbd.Image(client.ioctx, parent) #獲取 paraent image parent_volume.unprotect_snap(snap) #將 snap 去保護 parent_volume.remove_snap(snap) #刪除 snapshot src_volume.create_snap(clone_snap) #創建新的 snapshot src_volume.protect_snap(clone_snap) #將 snapshot 加保護 self.rbd.RBD().clone(client.ioctx, src_name, clone_snap, client.ioctx, dest_name, features=client.features) #在 snapshot 上做clone self._resize(volume) #如果 clone 的size 和 src volume 的size 不一樣,則 resiz

3. Nova 與 Ceph 的集成

常規地,Nova 將虛機的鏡像文件放在本地磁盤或者Cinder 卷上。為了與 Ceph 集成,Nova 中添加了新的代碼來將鏡像文件保存在 Ceph 中。

3.1 Nova 與 Ceph 集成的配置項

配置項 含義 默認值
images_type

其值可以設為下面幾個選項中的一個:

  • raw:實現了 class Raw(Image) 類,在 CONF.instances_path 指定的目錄中保存 raw 格式的 鏡像文件
  • qcow2:實現了 class Qcow2(Image) 類,在 CONF.instances_path 指定的目錄中創建和保存 qcow2 格式的鏡像文件 ['qemu-img', 'create', '-f', 'qcow2']
  • lvm:實現了 class Lvm(Image) 類,在 CONF.libvirt.images_volume_group 指定的 LVM Volume Group 中創建 local vm 來存放 vm images
  • rbd:實現了 class Rbd(Image) 類
  • default:使用 use_cow_images 配置項
default
images_rbd_pool 存放 vm 鏡像文件的 RBD pool rbd
images_rbd_ceph_conf Ceph 配置文件的完整路徑 ‘’
hw_disk_discard

設置使用或者不使用discard 模式,使用的話需要 Need Libvirt(1.0.6)、 Qemu1.5 (raw format) 和 Qemu1.6(qcow2 format)') 的支持

"unmap" : Discard requests("trim" or "unmap") are passed to the filesystem.
"ignore": Discard requests("trim" or "unmap") are ignored and aren't passed to the filesystem.

none
rbd_user rbd user ID  
rbd_secret_uuid rbd secret UUID   
 
關於 discard 模式(詳情見 這篇文章):
    Discard,在 SSD 上稱為 trim,是在磁盤上回收不使用的數據塊的機制。默認地 RBD image 是稀疏的(thin provision),這意味着只有在你寫入數據時物理空間才會被占用。在 OpenStack 虛機中使用 discard 機制需要滿足兩個條件:
(1)虛機使用 Virtio-scsi 作為存儲接口,而不是使用傳統的 Virtio-blk。這是因為 Virtio-scsi 中實現了如下的新的功能:
  • 設備直接暴露給客戶機(device pass-through to directly expose physical storage devices to guests)
  • 更好的性能(better performance and support for true SCSI device)
  • 標准的設備命名方法(common and standard device naming identical to the physical world thus virtualising physical applications is made easier)
  • 更好的擴展性(better scalability of the storage where virtual machines can attach more device (more LUNs etc…))

要讓虛機使用該接口,需要設置 Glance image 的屬性:

$ glance image-update --property hw_scsi_model=virtio-scsi --property hw_disk_bus=scsi

(2)在 nova.conf 中配置 hw_disk_discard = unmap

注意目前 cinder 尚不支持 discard。

3.2 Nova 中實現的 RBD image 操作

Nova 在 \nova\virt\libvirt\imagebackend.py 文件中添加了支持 RBD 的新類 class Rbd(Image) 來支持將虛機的image 放在 RBD 中。其主要方法包括:

(1)image create API
def create_image(self, prepare_template, base, size, *args, **kwargs):
# 調用 'rbd', 'import' 命令將 image file 的數據保存到 rbd image 中
rbd import 命令:
import [–image-format format-id] [–order bits] [–stripe-unit size-in-B/K/M –stripe-count num] [–image-feature feature-name]... [–image-shared] src-path[image-spec]
Creates a new image and imports its data from path (use - for stdin). The import operation will try to create sparse rbd images if possible. For import from stdin, the sparsification unit is the data block size of the destination image (1 << order).
The –stripe-unit and –stripe-count arguments are optional, but must be used together.

虛機創建成功后,在 RBD 中查看創建出來的 image:

root@ceph1:~# rbd ls vms
74cbdb41-3789-4eae-b22e-5085de8caba8_disk.local

root@ceph1:~# rbd info vms/74cbdb41-3789-4eae-b22e-5085de8caba8_disk.local
rbd image '74cbdb41-3789-4eae-b22e-5085de8caba8_disk.local':
size 1024 MB in 256 objects
order 22 (4096 kB objects)
block_name_prefix: rbd_data.11552ae8944a
format: 2
features: layering

查看虛機的 xml 定義文件,能看到虛機的系統盤、臨時盤和交換盤的鏡像文件都在 RBD 中,而且可以使用特定的 cache 模式(由 CONF.libvirt.disk_cachemodes 配置項指定)和 discard 模式(由 CONF.libvirt.hw_disk_discard 配置項設置):

<devices>
    <disk type="network" device="disk">
      <driver type="raw" cache="writeback" discard="unmap"/>
      <source protocol="rbd" name="vms/74cbdb41-3789-4eae-b22e-5085de8caba8_disk.local">
        <host name="9.115.251.194" port="6789"/>
        <host name="9.115.251.195" port="6789"/>
        <host name="9.115.251.218" port="6789"/>
      </source>
      <auth username="cinder">
        <secret type="ceph" uuid="e21a123a-31f8-425a-86db-7204c33a6161"/>
      </auth>
      <target bus="virtio" dev="vdb"/>
    </disk>
    <disk type="network" device="disk">
      <driver name="qemu" type="raw" cache="writeback"/>
      <source protocol="rbd" name="volumes/volume-6868a043-1412-4f6c-917f-bbffb1a8d21a">
        <host name="9.115.251.194" port="6789"/>
        <host name="9.115.251.195" port="6789"/>
        <host name="9.115.251.218" port="6789"/>
      </source>
      <auth username="cinder">
        <secret type="ceph" uuid="e21a123a-31f8-425a-86db-7204c33a6161"/>
      </auth>
      <target bus="virtio" dev="vda"/>
      <serial>6868a043-1412-4f6c-917f-bbffb1a8d21a</serial>
    </disk>

    關於 libvirt.disk_cachemodes 配置項,可以指定鏡像文件的緩存模式,其值的格式為 ”A=B",其中:

  • A 可以為 'file','block','network','mount'。其中,file 是針對 file-backend 的disk(比如使用 qcow2 格式的鏡像文件),block 是針對塊設備的disk(比如使用 cinder volume 或者 lvm),network 是針對通過網絡連接的設備的disk(比如 Ceph)。
  • B 可以為 none,writethrough,writeback,directsync,unsafe。writethrough 和 writeback 可以參考 理解 OpenStack + Ceph (2):Ceph 的物理和邏輯結構,其它的請自行google。
  • 比如,disk_cachemodes="network=writeback",disk_cachemodes="block=writeback"。

   上面的虛機 XML 定義文件是在Nova 配置為 disk_cachemodes="network=writeback" 和 hw_disk_discard = unmap 的情形下生成的。

(2)image clone API

def clone(self, context, image_id_or_uri):
1. 通過 Glance API 獲取 image 的 rbd location
2. 檢查 rbd image 是否可以被克隆(檢查它是不是在本 ceph cluster 內、是不是 raw 格式、是不是可以訪問等)
3. 調用 rbd 的 clone 方法來創建 clone

3.3 鏡像 clone 而非下載

    使用傳統存儲作為 image 的后端存儲時,在創建虛機的過程中的創建鏡像文件時,都是調用  _try_fetch_image_cache 方法來從 Glance 中將鏡像文件下載到本地(第一次會緩存,以后就直接讀緩存而不用下載),然后再創建鏡像文件的方法。而在使用 RBD 作為鏡像的后端存儲時,如果 Glance 鏡像文件被保存在 RBD 中,那么該過程將是重復的(先通過 Glance 從 RDB 中倒出鏡像,然后再由 Nova 放到RBD中),而且是非常耗時的。針對這種情況,Nova 實現了一種新的辦法,具體見下面的藍色字體部分: 

在 Spawn 虛機過程中,一個步驟是 create image,如下的方法會被調用:
def _create_image(self, context, instance,
                      disk_mapping, suffix='',
                      disk_images=None, network_info=None,
                      block_device_info=None, files=None,
                      admin_pass=None, inject_files=True,
                      fallback_from_host=None):
 
      if not booted_from_volume: #如果不是從 volume 啟動虛機
            root_fname = imagecache.get_cache_fname(disk_images, 'image_id')
            size = instance.root_gb * units.Gi
            backend = image('disk')
            if backend.SUPPORTS_CLONE: #如果 image backend 支持 clone 的話(目前的各種 image backend,只有 RBD 支持 clone)
                def clone_fallback_to_fetch(*args, **kwargs):
                        backend.clone(context, disk_images['image_id']) #直接調用 backend 的 clone 函數做 image clone
                fetch_func = clone_fallback_to_fetch
            else:
                fetch_func = libvirt_utils.fetch_image #否則走常規的 image 下載-導入過程
            self._try_fetch_image_cache(backend, fetch_func, context, root_fname, disk_images['image_id'], instance, size, fallback_from_host)

    可見,當 nova 后端使用 ceph 時,nova driver 調用 RBD imagebackend 命令,直接在 ceph 存儲層完成鏡像拷貝動作(無需消耗太多的nova性能,也無需將鏡像下載到hypervisor本地,再上傳鏡像到ceph),如此創建虛擬機時間將會大大提升。當然,這個的前提是 image 也是保存在 ceph 中,而且 image 的格式為 raw,否則 clone 過程會報錯。 具體過程如下:

(1)這是 Glance image 對應的 rbd image:

root@ceph1:~# rbd info images/0a64fa67-3e34-42e7-b7b0-423c11850e18
rbd image '0a64fa67-3e34-42e7-b7b0-423c11850e18':
        size 564 MB in 71 objects
        order 23 (8192 kB objects)
        block_name_prefix: rbd_data.16d21e1d755b
        format: 2
        features: layering

(2)使用該 image 創建第一個虛機

root@ceph1:~# rbd info vms/982b8eac-6bcc-4a21-bd04-b67e26188be0_disk
rbd image '982b8eac-6bcc-4a21-bd04-b67e26188be0_disk':
        size 3072 MB in 384 objects
        order 23 (8192 kB objects)
        block_name_prefix: rbd_data.130a36a6b435
        format: 2
        features: layering
        parent: images/0a64fa67-3e34-42e7-b7b0-423c11850e18@snap
        overlap: 564 MB

root@ceph1:~# rbd info vms/982b8eac-6bcc-4a21-bd04-b67e26188be0_disk.local
rbd image '982b8eac-6bcc-4a21-bd04-b67e26188be0_disk.local':
        size 2048 MB in 512 objects
        order 22 (4096 kB objects)
        block_name_prefix: rbd_data.a69b2ae8944a
        format: 2
        features: layering

root@ceph1:~# rbd info vms/982b8eac-6bcc-4a21-bd04-b67e26188be0_disk.swap
rbd image '982b8eac-6bcc-4a21-bd04-b67e26188be0_disk.swap':
        size 102400 kB in 25 objects
        order 22 (4096 kB objects)
        block_name_prefix: rbd_data.a69e74b0dc51
        format: 2
        features: layering

(3)創建第二個虛機

root@ceph1:~# rbd info vms/a9670d9a-8aa7-49ba-baf5-9d7a450172f3_disk
rbd image 'a9670d9a-8aa7-49ba-baf5-9d7a450172f3_disk':
        size 3072 MB in 384 objects
        order 23 (8192 kB objects)
        block_name_prefix: rbd_data.13611f6abac6
        format: 2
        features: layering
        parent: images/0a64fa67-3e34-42e7-b7b0-423c11850e18@snap
        overlap: 564 MB

(4)會看到 Glance image 對應的 RBD image 有兩個克隆,分別是上面虛機的系統盤

root@ceph1:~# rbd children  images/0a64fa67-3e34-42e7-b7b0-423c11850e18@snap
vms/982b8eac-6bcc-4a21-bd04-b67e26188be0_disk
vms/a9670d9a-8aa7-49ba-baf5-9d7a450172f3_disk

 4. 其它集成

除了上面所描述的 Cinder、Nova 和 Glance 與 Ceph RBD 的集成外,OpenStack 和 Ceph 之間還有其它的集成點:

(1)使用 Ceph 替代 Swift 作為對象存儲 (網絡上有很多比較 Ceph 和 Swift 的文章,比如 1,2,3,)

(2)CephFS 作為 Manila 的后端(backend)

(3)Keystone 和  Ceph Object Gateway 的集成,具體可以參考文章 (1)(2)

 參考文檔:

http://www.sebastien-han.fr/blog/2015/02/02/openstack-and-ceph-rbd-discard/

http://www.sebastien-han.fr/blog/2013/11/26/back-from-icehouse-openstack-summit-ceph-slash-openstack-integration/

http://www.wzxue.com/openstack-cinder-advance/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM