本文之前在腾讯云tstack上已经发布过了https://cloud.tencent.com/developer/article/1658665,此处自己记录保留一份。
在云中使用虚拟机HA,热迁移等功能的时候,可能会出现两个主机上的虚拟机同时对共享存储上同一个磁盘进行读写操作,导致磁盘数据损坏的问题。Libvirt提供了两种方式实现磁盘资源的互斥,分别是sanlock
和lockd
。相关的配置可以参考文档: https://libvirt.org/locking.html,本篇描述libvirt使用sanlock作为磁盘锁的使用方法。
关于sanlock的基本用法可以参考我之前的一篇文章: https://cloud.tencent.com/developer/article/1651000
sanlock用于实现libvirt磁盘锁的具体配置参考文档 https://libvirt.org/locking-sanlock.html
安装 libvirt sanlock 插件
[root@compute01 ~]# yum install libvirt-lock-sanlock
关闭firewall和selinux
# 关闭防火墙
[root@compute01 ~]# systemctl stop firewalld
[root@compute01 ~]# systemctl disable firewalld
# 关闭selinux
[root@compute01 ~]# setenforce 0
[root@compute01 ~]# sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
启动sanlock与wdmd服务,sanlock通过wdmd(watchdog multiplexing daemon)与watchdog建立连接,如果sanlock服务崩溃或异常终止,将会导致整个主机重新启动
# 启动sanlock
[root@compute01 ~]# systemctl enable sanlock
[root@compute01 ~]# systemctl start sanlock
# 启动wdmd
[root@compute01 ~]# systemctl enable wdmd
[root@compute01 ~]# systemctl start wdmd
查看启动sanlock守护进程使用的用户和组,后续会使用到该用户和组信息,这里只看父进程号为1的sanlock进程的信息,即进程2930所属的用户和组均为sanlock
[root@compute01 ~]# ps xao pid,ppid,group,user,command | grep sanlock
2930 1 sanlock sanlock /usr/sbin/sanlock daemon
2932 2930 root root /usr/sbin/sanlock daemon
11336 3648 root root grep --color=auto sanlock
默认libvirt使用sanlock时,要求共享存储路径为/var/lib/libvirt/sanlock,该路径可以通过修改配置文件/etc/libvirt/qemu-sanlock.conf
中的disk_lease_dir
选项进行修改。共享存储可以采用NFS或者cephfs等,此处不提供共享存储的搭建步骤。
sanlock守护进程需要访问共享存储,从前面得知sanlock守护进程所属的用户和组均为sanlock,所以此处需要修改nfs共享存储挂载路径所属的用户和组为sanlock
[root@compute01 ~]# chown -R sanlock:sanlock /var/lib/libvirt/sanlock
修改配置文件/etc/libvirt/qemu.conf
,配置libvirt lock_manager使用sanlock
lock_manager = "sanlock"
编辑配置/etc/libvirt/qemu-sanlock.conf
,配置sanlock
# 值为1表示采用自动加锁,即对虚拟机的每个磁盘都进行自动加锁处理
# 值为0表示关闭自动加锁,加锁操作需要在虚拟机xml文件中实现
auto_disk_leases = 1
# 为当前主机分配一个1~2000的唯一值,该值在所有加入sanlock集群的主机中唯一
host_id = 1
# 运行sanlock守护进程的用户和组,从前面得知user和group均为sanlock
user = "sanlock"
group = "sanlock"
重启libvirtd服务
[root@compute01 ~]# systemctl restart libvirtd
libvirtd服务重启完成,在共享存储挂载的路径下会自动创建出名为__LIBVIRT__DISKS__
的文件,该文件是用于管理主机的文件,如果没有该文件,则说明前面的配置存在问题
[root@compute01 ~]# ll /var/lib/libvirt/sanlock/
total 1024
-rw-r----- 1 sanlock sanlock 1048576 Dec 9 03:48 __LIBVIRT__DISKS__
[root@compute01 ~]# sanlock direct dump /var/lib/libvirt/sanlock/__LIBVIRT__DISKS__
offset lockspace resource timestamp own gen lver
00000000 __LIBVIRT__DISKS__ 9711d473-6095-46db-a37d-407372bd2612.centos-san 0000002853 0001 0001
本节验证虚拟机磁盘位于NFS共享存储时,sanlock对磁盘加锁的情况。测试方法为在计算节点1和计算节点2上分别启动一个虚拟机来进行测试,两个主机上的虚拟机系统盘均为NFS共享存储上的同一个磁盘。
准备虚拟机系统盘文件,此处采用cirros镜像,在计算节点1上执行下面的命令下载cirros镜像,并将其置于共享存储上
[root@compute01 ~]# wget http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img
# 将下载的虚拟机磁盘文件移动到共享存储上
[root@compute01 ~]# cp cirros-0.3.4-x86_64-disk.img /var/lib/libvirt/sanlock/cirros.disk
在计算节点1上通过下面的命令创建虚拟机
[root@compute01 ~]# virt-install --import \
--name cirros-vm --vcpus 1 --memory 64 \
--disk /var/lib/libvirt/sanlock/cirros.disk,format=qcow2,bus=virtio \
--network bridge=virbr0,model=virtio \
--graphics vnc,listen=0.0.0.0 \
--console pty,target_type=virtio
# 虚拟机创建并运行成功
[root@compute01 ~]# virsh list
Id Name State
----------------------------------------------------
1 cirros-vm running
查看共享存储路径下,可以发现自动生成了一个以md5值命名的文件,该文件即为磁盘/var/lib/libvirt/sanlock/cirros.disk的锁文件,文件名的md5值即为磁盘全路径字符串的md5值
[root@compute01 ~]# ll /var/lib/libvirt/sanlock
total 15028
-rw------- 1 sanlock sanlock 1048576 Dec 9 04:39 0f2d1b2c88ac72e97d4a9c4d13fe4db0
-rw-r--r-- 1 qemu qemu 13287936 Dec 9 04:38 cirros.disk
-rw-rw---- 1 sanlock sanlock 1048576 Dec 9 04:39 __LIBVIRT__DISKS__
# 计算磁盘全路径的md5值,可以看到文件名即为磁盘全路径的md5值
[root@compute01 ~]# python -c "import hashlib; print hashlib.md5('/var/lib/libvirt/sanlock/cirros.disk').hexdigest()"
0f2d1b2c88ac72e97d4a9c4d13fe4db0
通过sanlock direct dump命令查看磁盘锁文件中的内容,own为0001,即编号为1的主机,timestamp不为0,说明此时主机1获得了对磁盘/var/lib/libvirt/sanlock/cirros.disk的租期
[root@compute01 ~]# sanlock direct dump /var/lib/libvirt/sanlock/0f2d1b2c88ac72e97d4a9c4d13fe4db0
offset lockspace resource timestamp own gen lver
00000000 __LIBVIRT__DISKS__ 0f2d1b2c88ac72e97d4a9c4d13fe4db0 0000005856 0001 0001 1
尝试在计算节点2上用相同的命令创建虚拟机,从命令输出中可知获取磁盘的写锁失败了,此结果说明sanlock有效的解决了多个虚拟机同时对一个磁盘进行读写的问题
[root@compute02 ~]# virt-install --import \
--name cirros-vm --vcpus 1 --memory 64 \
--disk /var/lib/libvirt/sanlock/cirros.disk,format=qcow2,bus=virtio \
--network bridge=virbr0,model=virtio \
--graphics vnc,listen=0.0.0.0 \
--console pty,target_type=virtio
Starting install...
ERROR internal error: process exited while connecting to monitor: 2019-12-09T11:07:18.947726Z qemu-kvm: -drive file=/var/lib/libvirt/sanlock/cirros.disk,format=qcow2,if=none,id=drive-virtio-disk0: Failed to get "write" lock
Is another process using the image [/var/lib/libvirt/sanlock/cirros.disk]?
Domain installation does not appear to have been successful.
If it was, you can restart your domain by running:
virsh --connect qemu:///system start cirros-vm
otherwise, please restart your installation.
在计算节点1上关闭cirros-vm
[root@compute01 ~]# virsh destroy cirros-vm
Domain cirros-vm destroyed
# 从磁盘锁文件的内容可知,timestamp被置为0,租期释放
[root@compute01 ~]# sanlock direct dump /var/lib/libvirt/sanlock/0f2d1b2c88ac72e97d4a9c4d13fe4db0
offset lockspace resource timestamp own gen lver
00000000 __LIBVIRT__DISKS__ 0f2d1b2c88ac72e97d4a9c4d13fe4db0 0000000000 0001 0001 1
在计算节点2上重启创建虚拟机cirros-vm成功
[root@compute02 ~]# virt-install --import \
--name cirros-vm --vcpus 1 --memory 64 \
--disk /var/lib/libvirt/sanlock/cirros.disk,format=qcow2,bus=virtio \
--network bridge=virbr0,model=virtio \
--graphics vnc,listen=0.0.0.0 \
--console pty,target_type=virtio
[root@compute02 ~]# virsh list
Id Name State
----------------------------------------------------
2 cirros-vm running
# 查看租期文件,own为0002,timestamp不为0,即主机2获得了磁盘租期
[root@compute02 ~]# sanlock direct dump /var/lib/libvirt/sanlock/0f2d1b2c88ac72e97d4a9c4d13fe4db0
offset lockspace resource timestamp own gen lver
00000000 __LIBVIRT__DISKS__ 0f2d1b2c88ac72e97d4a9c4d13fe4db0 0000007694 0002 0001 2
在计算节点1上启动cirros-vm,从输出信息可知启动失败的原因是获取lock失败
[root@compute01 ~]# virsh start cirros-vm
error: Failed to start domain cirros-vm
error: internal error: process exited while connecting to monitor: 2019-12-09T11:18:18.811215Z qemu-kvm: -drive file=/var/lib/libvirt/sanlock/cirros.disk,format=qcow2,if=none,id=drive-virtio-disk0: Failed to get "write" lock
Is another process using the image [/var/lib/libvirt/sanlock/cirros.disk]?
当虚拟机被删除后,位于/var/lib/libvirt/sanlock目录下自动创建的锁文件将会残留,长时间会占用大量的磁盘空间,因此必须要定期对这些自动创建的锁文件进行清理,可以通过下面的命令完成清理操作,通过定时任务每周执行一次即可
# virt-sanlock-cleanup对资源租期文件的清理原理利用了资源租期,即尝试获取目标资源的租期
# 如果获取成功,则对目标资源执行rm -f操作,如果获取失败,则资源租期文件不会被删除,命令如下:
# sanlock client command -r $RESOURCE -c /bin/rm -f "$LOCKDIR/$MD5"
# 在前面的例子中,$RESOURCE的值为__LIBVIRT__DISKS__:0f2d1b2c88ac72e97d4a9c4d13fe4db0
# :/var/lib/libvirt/sanlock/0f2d1b2c88ac72e97d4a9c4d13fe4db0:0,$LOCKDIR/$MD5的值
# 为/var/lib/libvirt/sanlock/0f2d1b2c88ac72e97d4a9c4d13fe4db0
[root@compute01 ~]# virt-sanlock-cleanup
到目前为止,初步的学习了sanlock用于libvirt磁盘锁的使用方法,但要将其应用到openstack中管理虚拟机和磁盘,还面临比较多的问题。
前面一直采用的libvirt自动对磁盘进行加锁(即auto_disk_leases=1)操作,在openstack中如果直接使用磁盘自动加锁是有问题的,如:
因此在实际将sanlock应用到openstack中实现虚拟机高可用时,不能采用磁盘自动加锁的方式。对残留的资源租期文件清理操作,还需要做到高可用,不能因为单个主机挂掉导致残留资源的清理失效。另外如果openstack环境对接的是Ceph RBD或IPSAN,libvirt目前不支持对ceph rbd的加锁操作,同时虽然支持对通过iscsi挂载到主机上的磁盘进行加锁,但是同一个盘在不同的主机上其挂载路径很可能是不同的,导致磁盘锁无法生效。
参考资料:
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。