Libvirt集成Ceph RBD

Libvirt虚拟机管理器 在hypervisor接口和应用程序之间提供虚拟机抽象层方便我们管理和使用虚拟化技术。通过 libvirt 开发者和系统管理员可以专注于一个通用管理框架,通用API以及通用shell接口( virsh )来完成很多hypervisor工作:

  • QEMU/KVM

  • XEN

  • LXC

  • VirtualBox

  • 等等

Ceph块设备支持QEMU/KVM,通过 libvirt 可以使用Ceph块设备:

../../_images/ceph_rbd_libvirt.png

libvirt 通用架构也提供了云计算解决方案的Ceph块设备,例如 OpenStack Atlas 或 CloudStack都通过libvirt使用Ceph块设备。这个云计算解决方案使用 libvirt 和 QEMU/KVM 交互,然后 QEMU/KVM 再通过 librbd 和Ceph块设备交互。

也可以使用 Libvirt虚拟机管理器 , virsh管理虚拟机 以及 libvirt API 来使用Ceph块设备。

配置Ceph

  • 创建存储池:

    sudo ceph osd pool create libvirt-pool
    

备注

这里创建存储池命名为 libvirt-pool ,实际可根据需要命令

这里我的 安装 ceph-mon 最初配置 /etc/ceph/ceph.conf

osd pool default size = 3
osd pool default min size = 2
osd pool default pg num = 333
osd pool default pgp num = 333

则会提示错误:

Error ERANGE:  pg_num 333 size 3 would mean 1002 total pgs, which exceeds max 750 (mon_max_pg_per_osd 250 * num_in_osds 3)

也就是说,默认参数 mon_max_pg_per_osd = 250 ,对于3副本,则每个 ceph-mon 只能监控 250x3=750pg ,所以需要缩减 osd pool default pg num ,改为 128

osd pool default size = 3
osd pool default min size = 2
osd pool default pg num = 128
osd pool default pgp num = 128

备注

这里参考 ceph PG数量调整/PG的状态说明 计算PG数量:

Total PGs = (Total_number_of_OSD * 100) / max_replication_count

结算的结果往上取靠近2的N次方的值。我这里有3个osd,所以计算结果是 100 ,接近的 2的7 次方取 128

创建成功则提示:

pool 'libvirt-pool' created
  • 验证资源池:

    sudo ceph osd lspools
    

输出显示:

1 device_health_metrics
2 libvirt-pool
  • 检查这个创建资源池的pg数量:

    sudo ceph osd pool get libvirt-pool pg_num
    

显示:

pg_num: 81
  • 使用 rbd 工具初始化资源池用于RBD:

    sudo rbd pool init libvirt-pool
    
  • 创建一个Ceph用户:

    sudo ceph auth get-or-create client.libvirt mon 'profile rbd' osd 'profile rbd pool=libvirt-pool'
    

此时终端会显示keyring内容,可以存储为客户端用户的 rbd 访问使用的key /etc/ceph/ceph.client.<username>.keyring

sudo ceph auth get client.libvirt -o client.libvirt.keyring

上述命令会创建一个 client.libvirt.keyring ,内容类似如下:

[client.libvirt]
        key = XXXXXXXXXX
        caps mon = "profile rbd"
        caps osd = "profile rbd pool=libvirt-pool"
  • 然后验证用户存在:

    sudo ceph auth ls
    

备注

libvirt 访问Ceph是使用ID libvirt 而不是 Ceph名字 client.libvirt

备注

请注意,对于 admin 用户来说,采用了 sudo 会自动读取 /etc/ceph/ceph.client.<username>.keyring ( 实际就是 /etc/ceph/ceph.client.admin.keyring )来访问默认名为 ceph 的Ceph集群

客户端(使用Ceph)

备注

目前我对ceph认证和权限管理的了解有限,所以我暂时采用了所有服务器共享 /etc/ceph/ceph.client.admin.keyring 的方式,这在测试环境部署比较方便,但不适合生产环境。后续需要学习和实践权限管理。

对于使用Ceph的客户端(运行 KVM AtlasLibvirt虚拟机管理器 的虚拟化服务器) ,我采用复制 /etc/ceph/ceph.client.admin.keyring ,所以后续步骤具备了全面的权限可以直接操作:

sudo rbd -p libvirt-pool ls
  • 将Ceph服务器(前文创建)的 /etc/ceph/client.libvirt.keyring 复制到本地( KVM AtlasLibvirt虚拟机管理器 的虚拟化服务器) ,然后指定 keyringid 就可以检查和处理rbd:

    rbd --keyring /etc/ceph/client.libvirt.keyring --id libvirt -p libvirt-pool ls
    

可以看到,这里只使用了限定使用 RBD 的 keyring,没有使用超级 keyring ,也可以同样完成维护操作

CEPH_ARGS 环境变量

备注

Ceph提供了一个避免每次输入参数的方法:

用户环境变量设置 CEPH_ARGS 可以方便执行 rbd 命令
export CEPH_ARGS="--keyring /etc/ceph/client.libvirt.keyring --id libvirt -p libvirt-pool"

设置了上述环境变量之后,就可以直接使用简化的操作:

rbd ls

WOW!!! 我突然发现,这个方法可能是解决 Ceph 手工部署zdata集群(暂未成功) 解决自定义Ceph集群名的方法,传递 --ceph zdata 参数

RBD镜像

  • 创建RBD镜像:

    sudo qemu-img create -f rbd rbd:libvirt-pool/new-libvirt-image 2G
    

提示信息:

Formatting 'rbd:libvirt-pool/new-libvirt-image', fmt=rbd size=2147483648

然后就可以验证:

export CEPH_ARGS="--keyring /etc/ceph/client.libvirt.keyring --id libvirt -p libvirt-pool"
rbd ls

可以看到输出:

new-libvirt-image

也可以查看详细信息:

rbd ls -l

输出会显示:

NAME               SIZE   PARENT  FMT  PROT  LOCK
new-libvirt-image  2 GiB            2

配置libvirt RBD存储池

  • libvirt 需要定义RBD存储池,需要首先配置访问Ceph存储的secret:

    SECRET_UUID=$(uuidgen)
    cat >secret.xml <<__XML__
    <secret ephemeral='no' private='no'>
      <uuid>$SECRET_UUID</uuid>
      <usage type='ceph'>
        <name>client.libvirt secret</name>
      </usage>
    </secret>
    __XML__
    
    virsh secret-define --file secret.xml
    virsh secret-set-value --secret "$SECRET_UUID" --base64 "$(sudo ceph auth get-key client.libvirt)"
    

备注

我这里执行 virsh 命令的账号是已经配置过 libvirt 用户组的账号 huatai ,所以能够直接执行 virsh

  • 设置 libvirt-pool 存储池:

    cat >pool.xml <<__XML__
    <pool type="rbd">
      <name>images_rbd</name>
      <source>
        <name>libvirt-pool</name>
        <host name='192.168.6.204'/> # ceph monitor 1
        <host name='192.168.6.205'/> # ceph monitor 2
        <host name='192.168.6.206'/> # ceph monitor 3
        <auth username='libvirt' type='ceph'>
          <secret uuid='$SECRET_UUID'/>
        </auth>
      </source>
    </pool>
    __XML__
    
    virsh pool-define pool.xml
    virsh pool-start images_rbd
    virsh pool-autostart images_rbd
    

备注

由于Debian/Ubuntu将libvirt的存储后端支持独立打包,可能会遇到 libvirt的RBD存储池报错”missing backend for pool type 9 (rbd)” 会出现 image_rbd 存储池状态始终是 unknown (或无法创建),此时需要:

Ubuntu 安装 libvirt-daemon-driver-storage-rbd
sudo apt install libvirt-daemon-driver-storage-rbd -y
  • 然后验证检查是否能够看到之前创建的RBD磁盘文件:

    virsh vol-list images_rbd
    

如果一切正常,可以看到:

Name                Path
-----------------------------------------------------
new-libvirt-image   libvirt-pool/new-libvirt-image

virsh存储池激活问题

为了在物理服务器重启自动启动3个提供ceph存储的虚拟机,通过 virsh管理虚拟机 配置了存储虚拟机 autostart 。同时也配置了自动启动存储池 autostart 。但是看起来底层ceph启动速度比较慢,尚未就就绪的时候libvirt存储池 images_rbd 就会激活失败。此时:

virsh pool-list --all

显示该libvirt存储池没有激活:

 Name           State      Autostart
--------------------------------------
 boot-scratch   active     yes
 images         active     yes
 images_lvm     active     yes
 images_rbd     inactive   yes
 nvram          active     yes

不过,不影响使用这个存储池的虚拟机使用存储,所有使用 images_rbd 的虚拟机都能正常运行。只是,无法使用 virsh vol-list images_rbd ,会提示错误:

error: Failed to list volumes
error: Requested operation is not valid: storage pool 'images_rbd' is not active

解决方法可以通过再次激活存储卷(这个命令可以添加到启动脚本中自动激活一次):

virsh pool-start images_rbd

创建虚拟机

CentOS 9 Stream

  • 创建RBD磁盘:

    # virsh vol-create-as "${LIBVIRT_POOL}" "${VM_VOLUME}" --capacity "${VM_SZ}" --format raw
    virsh vol-create-as images_rbd z-centos9 --capacity 6GB --format raw
    

提示信息:

Vol z-centos9 created
  • 检查磁盘:

    virsh vol-list images_rbd
    

信息如下:

Name                Path
-----------------------------------------------------
new-libvirt-image   libvirt-pool/new-libvirt-image
z-centos9           libvirt-pool/z-centos9
  • 使用 virsh vol-create-as 创建的 RBD 镜像不能直接用 rbd 命令删除(即使已经 virsh undefine z-centos9 --nvram 删除了虚拟机定义),会提示错误:

    $ sudo rbd -p libvirt-pool rm z-centos9
    2021-12-09T17:15:40.727+0800 7fe7c1ffb700 -1 librbd::image::PreRemoveRequest: 0x55caaa6e6ea0 check_image_watchers: image has watchers - not removing
    Removing image: 0% complete...failed.
    rbd: error: image still has watchers
    This means the image is still open or the client using it crashed. Try again after closing/unmapping it or waiting 30s for the crashed client to timeout.
    

尝试执行删除:

virsh vol-delete z-centos9 --pool images_rbd

不过还是报错:

error: Failed to delete vol z-centos9
error: failed to remove volume 'libvirt-pool/z-centos9': No such file or directory

参考 ceph can not remove image - watchers ,检查watcher:

sudo rbd status -p libvirt-pool z-centos9

可以看到:

Watchers:
    watcher=192.168.6.200:0/1289276469 client.164826 cookie=140044924882800

检查磁盘信息:

sudo rbd info -p libvirt-pool z-centos9

输出信息:

rbd image 'z-centos9':
     size 5.6 GiB in 1431 objects
     order 22 (4 MiB objects)
     snapshot_count: 0
     id: 281f7561e072c
     block_name_prefix: rbd_data.281f7561e072c
     format: 2
     features: layering, exclusive-lock, object-map, fast-diff, deep-flatten
     op_features:
     flags:
     create_timestamp: Tue Dec  7 21:10:56 2021
     access_timestamp: Tue Dec  7 23:20:30 2021
     modify_timestamp: Tue Dec  7 21:10:56 2021

检查watcher:

sudo rados -p libvirt-pool listwatchers rbd_header.281f7561e072c

可以看到:

watcher=192.168.6.200:0/1289276469 client.164826 cookie=140044924882800

但是 没有 mapped

sudo rbd showmapped

输出是空的。

我错了,我突然发现我忘记停止 z-centos9 虚拟机,所以导致上述清理失败,通过以下方式清理完成:

sudo virsh destroy z-centos9
sudo rbd -p libvirt-pool rm z-centos9
  • 安装虚拟机:

    virt-install \
      --network bridge:br0 \
      --name z-centos9 \
      --ram=2048 \
      --vcpus=1 \
      --os-type=Linux --os-variant=rhl9 \
      --boot uefi --cpu host-passthrough \
      --disk vol=images_rbd/z-centos9,sparse=false,format=raw,bus=virtio,cache=none,io=native \
      --graphics none \
      --location=http://mirror.stream.centos.org/9-stream/BaseOS/x86_64/os/ \
      --extra-args="console=tty0 console=ttyS0,115200"
    

备注

不过我安装CentOS 9 Stream尚未成功,具体实践见 创建KVM虚拟机

Ubuntu 20

  • 创建RBD磁盘:

    # virsh vol-create-as "${LIBVIRT_POOL}" "${VM_VOLUME}" --capacity "${VM_SZ}" --format raw
    virsh vol-create-as --pool images_rbd --name z-ubuntu20-rbd --capacity 7GB --allocation 7GB --format raw
    

备注

virsh vol-create-as 使用方法参考 Red Hat Enterprise Linux7 > Virtualization Deployment and Administration Guide > 20.30. Storage Volume Commands

不过,似乎对于RBD --capacity 7GB --allocation 7GB 也不是立即完全分配存储空间?

  • 安装虚拟机:

    virt-install \
      --network bridge:br0 \
      --name z-ubuntu20-rbd \
      --ram=2048 \
      --vcpus=1 \
      --os-type=Linux --os-variant=ubuntu20.04 \
      --boot uefi --cpu host-passthrough \
      --disk vol=images_rbd/z-ubuntu20-rbd,sparse=false,format=raw,bus=virtio,cache=none,io=native \
      --graphics none \
      --location=http://mirrors.163.com/ubuntu/dists/focal/main/installer-amd64/ \
      --extra-args="console=tty0 console=ttyS0,115200"
    

安装完成后按照 私有云KVM环境 中订正

  • 修订NTP:

    echo "NTP=192.168.6.200" >> /etc/systemd/timesyncd.conf
    
  • 修订控制台输出

默认安装 /etc/default/grub 内容:

GRUB_TERMINAL=serial
GRUB_SERIAL_COMMAND="serial --unit=0 --speed=115200 --stop=1"

修改成:

GRUB_CMDLINE_LINUX="console=ttyS0,115200"
GRUB_TERMINAL="serial console"
GRUB_SERIAL_COMMAND="serial --speed=115200"

然后执行:

sudo update-grub
  • 修订虚拟机 /etc/sudoers 并添加主机登陆ssh key

磁盘转换

备注

以下实践是将 libvirt LVM卷管理存储池 上VM转换成基于 Ceph RBD的VM,无需重新安装虚拟机

  • qemu-img 转换:

    virsh shutdown z-ubuntu20  #关闭虚拟机之后再做转换
    time sudo qemu-img convert /dev/vg-libvirt/z-ubuntu20 -O raw rbd:libvirt-pool/z-ubuntu20 -p #计算一下转换时间
    

耗时约1分半钟转换了6G数据:

real1m25.619s
user0m4.428s
sys0m11.003s

注意,此时通过 qemu-img 或者 rbd 命令创建的RBD磁盘,在 images_rbd 存储池查看不到:

virsh vol-list images_rbd

查看不到刚才转换的磁盘

而:

sudo rbd ls --pool libvirt-pool

则可以看到:

new-libvirt-image
z-centos9
z-ubuntu20

解决的方法是刷新libvirt存储池:

virsh pool-refresh images_rbd

提示:

Pool images_rbd refreshed

完成刷新后就可以看到新生成的镜像文件:

virsh vol-list images_rbd

此时可以看到:

Name                Path
-----------------------------------------------------
new-libvirt-image   libvirt-pool/new-libvirt-image
z-centos9           libvirt-pool/z-centos9
z-ubuntu20          libvirt-pool/z-ubuntu20

接下来我们就可以参考现有的 z-ubuntu20 虚拟机的 XML 配置来构建基于RBD的虚拟机

修订磁盘使用RBD

  • 可以直接 virsh edit z-ubuntu20 将原先使用 libvirt LVM卷管理存储池 的虚拟机 z-ubuntu20 的虚拟磁盘修订成刚才创建的 RBD 磁盘。不过,我这里为了对比两种环境 (运行在SSD本地磁盘上 libvirt LVM卷管理存储池 和运行在 基于NVMe硬件的Ceph分布式存储上 性能差异),所以将 z-ubuntu20 配置dump出来修订创建 z-ubuntu20-rbd 虚拟机:

    virsh dumpxml z-ubuntu20 > z-ubuntu20-rbd.xml
    
  • 修改 z-ubuntu20-rbd.xml

修订 nameuuid ,并调整 vcpumemory

...
<name>z-ubuntu20-rbd</name>
<uuid>89df14f3-a51c-4c62-a91d-584e4058961c</uuid>
...
<memory unit='KiB'>8388608</memory>
<currentMemory unit='KiB'>8388608</currentMemory>
<vcpu placement='static'>4</vcpu>
...

修订磁盘部分,将:

<devices>
  <emulator>/usr/bin/qemu-system-x86_64</emulator>
  <disk type='block' device='disk'>
    <driver name='qemu' type='raw' cache='none' io='native'/>
    <source dev='/dev/vg-libvirt/z-ubuntu20'/>
    <target dev='vda' bus='virtio'/>
    <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
  </disk>

修订成:

<devices>
  <emulator>/usr/bin/qemu-system-x86_64</emulator>
  <disk type='network' device='disk'>
    <driver name='qemu' type='raw' cache='none' io='native'/>
    <auth username='libvirt'>
      <secret type='ceph' uuid='3f203352-fcfc-4329-b870-34783e13493a'/>
    </auth>
    <source protocol='rbd' name='libvirt-pool/z-ubuntu20'>
      <host name='192.168.6.204'/>
    </source>
    <target dev='vda' bus='virtio'/>
    <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
  </disk>

修订虚拟网卡MAC地址:

<interface type='bridge'>
  <mac address='52:54:00:85:7c:09'/>

备注

这里RBD配置参考前述 virt-install 安装 z-centos9 生成配置,注意其中提供了了 <secret type='ceph' uuid='3f203352-fcfc-4329-b870-34783e13493a'/> 是引用libvirt认证,否则无法读取Ceph存储

  • 创建 z-ubuntu20-rbd

    virsh define z-ubuntu20-rbd.xml
    
  • 修改 z-ubuntu20 调整 vcpu 和 memory ,虚拟机硬件和 z-ubuntu20-rbd 一致:

    <memory unit='KiB'>8388608</memory>
    <currentMemory unit='KiB'>8388608</currentMemory>
    <vcpu placement='static'>4</vcpu>
    
  • 启动 z-ubuntu20-rbd

    virsh start z-ubuntu20-rbd
    

然后执行 virsh console z-ubuntu20-rbd 登陆系统,然后订正主机名和IP地址( 私有云KVM环境 ):

hostnamectl set-hostname z-ubuntu-rbd
sed -i 's/192.168.6.246/192.168.6.247/g' /etc/netplan/01-netcfg.yaml
netplan generate
netplan apply
sed -i '/192.168.6.246/d' /etc/hosts
echo "192.168.6.247    z-ubuntu-rbd" >> /etc/hosts

性能测试

我非常好奇我部署的分布式Ceph存储性能能够达到本地SSD磁盘的多少比例,所以进行 比较KVM虚拟机本地SSD和Ceph RBD存储性能

性能优化

要进一步提高Ceph RBD性能,可以采用 Ceph SPDK 实现。我将在后续完成这个性能优化实践。

参考