移动云计算Libvirt集成Ceph iSCSI

由于 Arch Linux ARM的软件仓库对 libvirt-storage-ceph 缺失,所以我在部署 移动云计算Libvirt集成Ceph RBD 遇到较大困难暂时放弃。我调整方案,改为采用 Ceph iSCSIKVM Atlas 虚拟化服务器集群提供分布式存储。

Ceph iSCSI 可以解决客户端(libvirt)无法原生支持Ceph的缺憾,通过输出 Ceph RADOS 块设备(RBD)镜像作为SCSI磁盘来提供了一个高可用(Highly Available, HA) iSCSI target:

../../../_images/ceph_iscsi.png

通过Ceph iSCSI网关将RBD镜像映射为iSCSI target

准备工作

  • 为减少iSCSI initiator超时的可能性,调整OSD监控检测的心跳间隔,即修订 /etc/ceph/ceph.conf 配置:

/etc/ceph/ceph.conf 修订OSD心跳参数,降低间隔值减少iSCSI initiator启动超时可能性
[osd]
osd heartbeat grace = 20
osd heartbeat interval = 5
  • 从一个Ceph Monitor (ceph-mon)节点更新运行状态:

选择一个ceph-mon节点发起更新监控OSD心跳运行配置的状态,降低间隔值减少iSCSI initiator启动超时可能性
ceph tell osd.* config set osd_heartbeat_grace 20
ceph tell osd.* config set osd_heartbeat_interval 5
  • 在每个OSD节点 上更新运行状态:

在每个ceph-osd节点发起更新OSD服务进程心跳运行状态,降低间隔值减少iSCSI initiator启动超时可能性
# 在第一个节点上针对 osd.0 更新osd服务的心跳服务状态,其他节点依次类推
ceph daemon osd.0 config set osd_heartbeat_grace 20
ceph daemon osd.0 config set osd_heartbeat_interval 5

部署 Ceph iSCSI

Ceph iSCSI 部署比预想要复杂,虽然客户端可以采用标准的iSCSI initator访问,但是服务端iSCSI和客户端initator需要配置完成以下步骤:

libvirt支持iSCSI方式

libvirt有两种方式使用iSCSI:

  • iSCSI pool: 使用iSCSI target来存储卷,所有卷需要在iSCSI服务器上预先分配

  • iSCSI direct pool: 是iSCSI pool的变体,不使用iscsiadm,而是使用 libscsi ,需要提供 host , target IQN 和 initiator IQN

libvirt配置iSCSI存储池(iSCSI pool)

target path 决定了libvirt如何输出(expose)存储池的的河北路径: /dev/sda /dev/sdb 等路径不是好多选择,因为这些路径在启动时不稳定,或者在集群中的机器上不稳定(名称由内核按照先到先得的原则分配)。强烈建议使用 /dev/disk/by-path ,除非你知道自己在做什么(显然大多数情况你不知道^_^)。这种 /dev/disk/by-path 的路径名可以获得稳定的命名方案。

host name 是iSCSI服务器的FQDN

Source Path 是创建iSCSI target时候获得的IQN,我的案例是 iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw ( 见 Ceph iSCSI initator客户端 扫描 配置Ceph iSCSI网关 输出的iSCSI Node )

  • 创建一个 ceph_iscsi_libvirt.xml 配置:

定义iSCSI pool: ceph_iscsi_libvirt.xml
<pool type='iscsi'>
    <name>images_iscsi</name>
    <source>
        <host name='a-b-data-2.dev.cloud-atlas.io'/>
        <device path='iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw'/>
        <auth type='chap' username='libvirtd'>
            <secret usage='libvirtiscsi'/>
        </auth>
    </source>
    <target>
        <path>/dev/disk/by-path</path>
    </target>
</pool>
  • 使用 virsh 定义iSCSI存储池:

使用virsh命令通过ceph_iscsi_libvirt.xml定义创建存储池
sudo virsh pool-define ceph_iscsi_libvirt.xml

提示信息:

Pool images_iscsi defined from ceph_iscsi_libvirt.xml
  • 检查存储池 images_iscsi 尚未激活:

使用virsh pool-list检查存储池,可以看到images_iscsi尚未激活
sudo virsh pool-list --all

显示输出:

 Name           State      Autostart
--------------------------------------
 ...
 images_iscsi   inactive   no
  • 参考 Secret XML format: Usage type “iscsi”ceph_iscsi_libvirt.xml 中定义了 <secret usage='libvirtiscsi'/> 这个secret,现在就需要创建这个secret,然后再给这个secret插入密码值(你可以认为这是一个分离的密码本):

定义iSCSI pool所使用的secret “libvirtiscsi”
<secret ephemeral='no' private='yes'>
   <description>Passphrase for the ceph iSCSI</description>
   <usage type='iscsi'>
      <target>libvirtiscsi</target>
   </usage>
</secret>
  • 执行以下命令创建secret:

使用virsh secret-define定义名为 “libvirtiscsi” 的secret,注意此时这个secret是空定义(没有密码)
sudo virsh secret-define ceph_iscsi_libvirt_secret.xml

此时会提示这个secret对应的UUID,例如:

ca36b1f9-6299-4b69-a3c9-c44e1349c66d
  • 然后向secret插入密码值:

使用virsh secret-set-value向secret插入密码
virsh secret-set-value --interactive ca36b1f9-6299-4b69-a3c9-c44e1349c66d

在交互模式下输入密钥,则提示secret的值设置成功:

Enter new value for secret:
Secret value set

终于,我们现在完成了认证密钥设置,可以启动存储池了

  • 激活存储池 images_iscsi 并设置为自动启动:

使用virsh pool-start启动images_iscsi存储池
sudo virsh pool-start images_iscsi

这里可能会提示错误:

libvirt启动iSCSI pool报错,显示已经存在iSCSI会话
error: Failed to start pool images_iscsi
error: internal error: Child process (/usr/bin/iscsiadm --mode node --portal a-b-data-2.dev.cloud-atlas.io:3260,1 --targetname iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw --login) unexpected exit status 15: iscsiadm: Could not login to [iface: default, target: iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw, portal: a-b-data-2.dev.cloud-atlas.io,3260].
iscsiadm: initiator reported error (15 - session exists)
iscsiadm: Could not log into all portals

原因是之前的会话没有退出,需要先退出。

但是我发现奇怪,我执行了:

iscsiadm退出target登陆
sudo iscsiadm -m node -T iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw -u

并且重复执行上述logout命令,已经明确看到:

iscsiadm: No matching sessions found

说明没有当前会话

但是再次启动 images_iscsi 存储池还是同样报错,而再次执行 iscsiadm logout,发现确实又出现了会话。这说明执行启动存储确实发起了iscsi登陆会话。

我考虑之前实践 Ceph iSCSI initator客户端 配置了initator,所以执行 从系统删除iSCSI会话 然后再重复上述启动libvirt iSCSI pool操作,就可以看到成功启动:

使用virsh pool-start启动images_iscsi存储池
sudo virsh pool-start images_iscsi

输出显示:

Pool images_iscsi started
  • 设置 iSCSI pool 自动启动

virsh设置iSCSI pool自动启动
virsh pool-autostart images_iscsi

提示信息:

Pool images_iscsi marked as autostarted

查询LUN信息

在完成 libvirt 访问iSCSI target的存储池配置之后,就可以查询存储池中的LUN来获得那些而可用的虚拟磁盘。注意 libvirt iSCSI pool 不支持直接操作远程iSCSI target服务器上的磁盘创建(重大安全隐患),所以必须在 配置Ceph iSCSI网关 为libvirt客户端创建好磁盘并attach。

显然,这个 libvirt iSCSI pool 的功能比较简陋,远比不上 Libvirt集成Ceph RBD 方便灵活。如果作为云计算,需要有辅助工具平台来串联整个流程

  • 查询远程iSCSI target提供的LUN磁盘:

virsh查看iSCSI pool提供的LUN
virsh vol-list images_iscsi

可以看到 配置Ceph iSCSI网关 配置的5块磁盘:

virsh查看iSCSI pool提供的LUN信息输出
 Name         Path
---------------------------------------------------------------------------------------------------------------------------------
 unit:0:0:0   /dev/disk/by-path/ip-a-b-data-2.dev.cloud-atlas.io:3260-iscsi-iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw-lun-0
 unit:0:0:1   /dev/disk/by-path/ip-a-b-data-2.dev.cloud-atlas.io:3260-iscsi-iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw-lun-1
 unit:0:0:2   /dev/disk/by-path/ip-a-b-data-2.dev.cloud-atlas.io:3260-iscsi-iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw-lun-2
 unit:0:0:3   /dev/disk/by-path/ip-a-b-data-2.dev.cloud-atlas.io:3260-iscsi-iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw-lun-3
 unit:0:0:4   /dev/disk/by-path/ip-a-b-data-2.dev.cloud-atlas.io:3260-iscsi-iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw-lun-4
  • 上述远程iSCSI target LUNs已经被映射到本地作为本地块设备,可以通过 virsh vol-info 命令检查,例如检查第一块磁盘:

virsh vol-info查看iSCSI Target的LUN信息
# virsh vol-info可以检查LUN信息,将iSCSI target LUN作为本地磁盘
virsh vol-info /dev/disk/by-path/ip-a-b-data-2.dev.cloud-atlas.io:3260-iscsi-iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw-lun-0

输出信息可以看到这个磁盘就如 配置Ceph iSCSI网关 所配置的服务器端RBD设备情况:

virsh vol-info查看iSCSI Target的LUN信息输出
Name:           unit:0:0:0
Type:           block
Capacity:       10.00 GiB
Allocation:     10.00 GiB
  • 通过 virsh vol-dumpxml 命令可以获得LUN信息:

virsh vol-dumpxml查看iSCSI Target的LUN信息
virsh vol-dumpxml /dev/disk/by-path/ip-a-b-data-2.dev.cloud-atlas.io:3260-iscsi-iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw-lun-0

输出信息如下:

virsh vol-dumpxml查看iSCSI Target的LUN信息输出
<volume type='block'>
  <name>unit:0:0:0</name>
  <key>360014057d19ff6d9865427e94cba1e76</key>
  <capacity unit='bytes'>10737418240</capacity>
  <allocation unit='bytes'>10737418240</allocation>
  <physical unit='bytes'>10737418240</physical>
  <target>
    <path>/dev/disk/by-path/ip-a-b-data-2.dev.cloud-atlas.io:3260-iscsi-iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw-lun-0</path>
    <permissions>
      <mode>0660</mode>
      <owner>0</owner>
      <group>995</group>
    </permissions>
    <timestamps>
      <atime>1671959189.366283420</atime>
      <mtime>1671959189.366283420</mtime>
      <ctime>1671959189.366283420</ctime>
      <btime>0</btime>
    </timestamps>
  </target>
</volume>

使用iSCSI LUN磁盘创建虚拟机

既然iSCSI target KUN磁盘已经就绪,由于我的整个环境是在 ARM硬件环境KVM虚拟化 ( Apple ARM架构芯片M1 Pro Macbook Pro上 ),所以构建虚拟机方法也采用 arch linux ARM KVM虚拟化 方式相同(虚拟机OS选择 Fedora ):

采用iSCSI target LUN磁盘作为存储创建Fedora虚拟机(ovmf)
host="a-k8s-m-1"

virt-install \
  --network bridge:virbr0 \
  --name ${host} \
  --ram=3072 \
  --vcpus=2 \
  --arch aarch64 \
  --boot uefi \
  --disk path=/dev/disk/by-path/ip-a-b-data-2.dev.cloud-atlas.io:3260-iscsi-iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw-lun-0,format=raw,bus=virtio,cache=writeback,io=threads \
  --cdrom=/var/lib/libvirt/images/Fedora-Server-dvd-aarch64-37-1.7.iso

详细安装步骤同 arch linux ARM KVM虚拟化Fedora 37 Server安装

重复上述步骤,完成5个虚拟机的部署,完成后虚拟机列表:

完成部署后虚拟机列表,其中有5台虚拟机是基于iSCSI存储(底层是Ceph分布式存储)
# virsh list
 Id   Name         State
----------------------------
 1    a-b-data-2   running
 2    a-b-data-1   running
 3    a-b-data-3   running
 5    a-k8s-m-1    running
 8    a-k8s-m-3    running
 11   a-k8s-m-2    running
 12   a-k8s-n-2    running
 13   a-k8s-n-1    running

以下段落仅记录参考,暂未实践

libvirt配置iSCSI直接存储池(iSCSI direct pool)

警告

iSCSI direct pool 我没有实践(暂时没有时间精力)

libvirt通过 libiscsi 来构建iSCSI direct pool 访问iSCSI target,配置需要:

  • host

  • target IQN

  • initator IQN

iSCSI direct pool的支持是libvirt v4.7.0开始支持的( iSCSI pool 则是早期 v0.4.1 开始支持 )

  • 创建一个 ceph_direct_iscsi_libvirt.xml 配置:

定义iSCSI direct pool
<pool type="iscsi-direct">
  <name>images_iscsi_direct</name>
  <source>
    <host name="a-b-data-2.dev.cloud-atlas.io"/>
    <device path="iqn.2022-12.io.cloud-atlas.iscsi-gw:iscsi-igw"/>
    <auth type='chap' username='libvirtd'>
        <secret usage='mypassword12'/>
    </auth>
    <initiator>
      <iqn name="iqn.1989-06.io.cloud-atlas:libvirt-client"/>
    </initiator>
  </source>
</pool>

multipath

警告

multipath 我没有实践(暂时没有时间精力)

备注

openstack/cinder-specs/specs/kilo/iscsi-multipath-enhancment.rst 介绍了 nova-compute 支持iSCSI卷数据路径的multipath。但我还没有找到 libvirt 采用 multipath 的参考资料,后续再实践

libvirt storage: Multipath pool 来看,仅提供一个本地挂载的Multipath pool,主要实现应该是依赖主机底层的multipath来实现的。

参考