移动云计算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

  • 创建存储池:

Ceph OSD存储池创建
sudo ceph osd pool create libvirt-pool

备注

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

这里我的 移动云计算Ceph部署ceph-mon 配置 /etc/ceph/ceph.conf

osd pool default size = 3
osd pool default min size = 2
osd pool default pg num = 128
osd pool default pgp num = 128
osd crush chooseleaf type = 1

备注

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

Total PGs = (Total_number_of_OSD * 100) / max_replication_count

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

经验见 Libvirt集成Ceph RBD

但是这次还是报错:

Error ERANGE: 'pgp_num' must be greater than 0 and lower or equal than 'pg_num', which in this case is 1

奇怪,看起来符合规则。

改为在管理界面创建:

../../../_images/ceph_rbd_create_libvirt_pool.png

Ceph管理节点创建RBD存储池

备注

提供 Quotas 限制磁盘使用

提供 QoS 限制网盘的网络速度

创建成功则提示:

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

    sudo ceph osd lspools
    

输出显示:

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

    sudo ceph osd pool get libvirt-pool pg_num
    

显示:

pg_num: 32

备注

ceph-mgr 管理平台提供了自动计算出的 pg_num = 32 ,所以我调整 /etc/ceph/ceph.conf 配置:

osd pool default pg num = 32
osd pool default pgp num = 32
  • 使用 rbd 工具初始化资源池用于RBD:

使用rbd工具对Ceph RBD存储池初始化
sudo rbd pool init libvirt-pool
  • 创建一个Ceph用户:

使用ceph auth创建libvirt访问用户账号
sudo ceph auth get-or-create client.libvirt mon 'profile rbd' osd 'profile rbd pool=libvirt-pool'

此时终端会显示keyring内容,可以存储为客户端用户的 rbd 访问使用的key /etc/ceph/${CLUSTER}.client.<username>.keyring 。不过,标准方法是执行以下命令生成 keyring :

使用ceph auth获取libvirt访问用户账号密钥保存为文件
sudo ceph auth get client.libvirt -o /etc/ceph/${CLUSTER}.client.libvirt.keyring

提示:

exported keyring for client.libvirt

上述命令会创建一个 /etc/ceph/ceph.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

客户端(使用Ceph)

Ubuntu Linux 安装:

sudo apt install ceph-common

Arch Linux 需要使用 Arch Linux AUR 方式安装 ceph

yay -S ceph

另外一个 ceph-bin 软件包版本虽然更新(17.2.5-4),但是只提供X86版本无法使用

备注

我发现 Arch Linux 安装ceph依赖安装大量的软件包,有JDK runtime和python库,让我很是诧异。不过实在没有时间和精力 编译Ceph ,所以我还是采用 Arch Linux AUR 安装,安装版本比 Fedora 提供的 17.2.5 低一些,为 15.2.17 (arch linux ARM似乎跟进较慢,X86版本已经同步到 v17.2.5-4 了)

这里有一个安装依赖问题,执行 rbd 命令会提示缺少依赖库文件:

rbd: error while loading shared libraries: libboost_program_options.so.1.79.0: cannot open shared object file: No such file or directory

这是因为系统已经升级到 libboost_program_options.so.1.80.0 ( bootst-libs ) ,且arch Linux ARM网站没有提供旧版安装包,所以 下载源代码编译 :

编译boost
tar xfz boost_1_79_0.tar.gz
cd boost_1_79_0
./bootstrap.sh
./b2

# 编译成功后提示:

The following directory should be added to compiler include paths:

    /data/docs/github.com/boost/boost_1_79_0

The following directory should be added to linker library paths:

    /data/docs/github.com/boost/boost_1_79_0/stage/lib

不过,实际验证 boost-1.80boost-1.79 兼容,可以通过以下命令简单处理链接后就能正常运行 rbd :

软连接boost解决rbd运行依赖
sudo ln -s /usr/lib/libboost_program_options.so.1.80.0 /usr/lib/libboost_program_options.so.1.79.0
sudo ln -s /usr/lib/libboost_thread.so.1.80.0 /usr/lib/libboost_thread.so.1.79.0
sudo ln -s /usr/lib/libboost_iostreams.so.1.80.0 /usr/lib/libboost_iostreams.so.1.79.0

备注

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

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

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

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

CEPH_ARGS 环境变量

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

export CEPH_ARGS="--keyring /etc/ceph/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:

配置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)"

备注

注意,需要在本地复制好服务器上的 /etc/ceph/ceph.client.admin.keyring 才有权限向Ceph服务器发起 ceph auth get-key client.libvirt

  • 设置 libvirt-pool 存储池:

定义libvirt的Ceph存储池
cat >pool.xml <<__XML__
<pool type="rbd">
  <name>images_rbd</name>
  <source>
    <name>libvirt-pool</name>
    <host name='192.168.8.204'/> # ceph monitor 1
    <host name='192.168.8.205'/> # ceph monitor 2
    <host name='192.168.8.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

警告

这里在执行 virsh pool-define pool.xml 出现报错,待解决:

error: Failed to define pool from pool.xml
error: internal error: missing backend for pool type 9 (rbd)

参考 FS#58375 - [libvirt] Creating pool for disk sd* fails 其中提到需要安装 qemu-block-rbd 软件包,但是我使用 pacman -Ss qemu 没有找到这个包

参考 Re: libvirt-storage-rbd removed from repos 提到 libvirt-storage-rbd 软件包已经被移除出仓库,原因是 ceph-libs 已经被迁移到 Arch Linux AUR

参考 Liibvirtd service won’t start libvirtd 的存储后端驱动都保存在 /usr/lib/libvirt/storage-backend 目录下,可以看到该目录下有:

libvirt_storage_backend_disk.so
libvirt_storage_backend_fs.so
libvirt_storage_backend_iscsi.so
libvirt_storage_backend_logical.so
libvirt_storage_backend_mpath.so
libvirt_storage_backend_scsi.so
libvirt_storage_backend_zfs.so

果然没有 rbd 驱动。这里提到:

libvirt_storage_backend_rbd.so links to both librbd.so.1 and librados.so.2 which both link to libceph-common.so.2 which links to libfmt.so.7 fmt is only listed in ceph-libs makedepends. Add fmt to depends of ceph-libs?

I installed fmt and it seem to start working right away and after a reboot too.

这里的 libvirt_storage_backend_rbd.so 链接是怎么搞?

尝试软连接:

ln -s /usr/lib/librbd.so /usr/lib/libvirt/storage-backend/libvirt_storage_backend_rbd.so

然后再次执行:

virsh pool-define pool.xml

问题未解决,看来还要寻找方法: 能否找到 libvirt-storage-rbd 呢? 难道要自己编译 libvirt

备注

我忽然想到,既然 libvirt 内置支持了 iscsiNFS ,实际上我可以将 Ceph 转换成 iSCSI (注意, Ceph从2022年11月不再活跃开发iSCSI gateway了,所以不建议采用iSCSI) 或者而直接输出NFS来实现(比较倾向,并且可以用来构建NAS存储),虽然性能会差些,但是至少能够运行起来。毕竟我花费了大量的时间来构建分布式存储,再回头沉没成本实在太高了。

需要考虑 虚拟机存储方案: NFS vs. iSCSI 性能(待调研)

当然,如果放弃ARM架构,转为X86架构,这些问题应该都不复存在。谁让我挑战Apple Silicon的 移动云架构 呢?

备注

我发现 systemctl status libvirtd 显示有错误:

libvirtd[46965]: internal error: Child process (dmidecode -q -t 0,1,2,3,4,11,17) unexpected exit status 1: /sys/firmware/efi/systab: SMBIOS entry point missing
libvirtd[46965]: operation failed: Cannot find CPU model with PVR 0x025

这个错误应该和ARM对 dmidecode 支持有关,先忽略

警告

关于在 Apple ARM架构芯片M1 Pro ARM Atlas 架构的 Libvirt集成Ceph RBD 实践我 暂时做到这里 :

  • 由于 Arch Linux ARM软件仓库无法提供 libvirt-storage-ceph ,所以无法直接部署 ( 或许采用 Fedora ARM版本可以比较容易实现,毕竟Fedora已经集成了 Asahi Linux 工具,也已经可以在ARM架构的苹果Macbook上运行 )

  • 我在X86架构的 MacBook Pro 2013 later笔记本上将重新构建Ceph虚拟化集群,预计可以比较方便实现 Libvirt集成Ceph RBD

以下文档部分请暂时忽略,还未作。目前我将调整方案,改为:

备注

以下部分未执行,仅保留参考,方案已转为 移动云计算Libvirt集成Ceph iSCSI

  • 然后验证检查是否能够看到之前创建的RBD磁盘文件:

    virsh vol-list images_rbd
    

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

Name                Path
-----------------------------------------------------
libvirt-image   libvirt-pool/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 实现。我将在后续完成这个性能优化实践。

参考