鹏 飞
专注于OpenStack计算、Python。
热爱大海、雪山。
一、安装与配置
1
安装sanlock
sanlock用于实现libvirt磁盘锁的具体配置参考文档 https://libvirt.org/locking-sanlock.html
安装 libvirt sanlock 插件;
二、磁盘位于NFS共享存储
本节验证虚拟机磁盘位于NFS共享存储时,sanlock对磁盘加锁的情况。测试方法为在计算节点1和计算节点2上分别启动一个虚拟机来进行测试,两个主机上的虚拟机系统盘均为NFS共享存储上的同一个磁盘。
准备虚拟机系统盘文件,此处采用cirros镜像,在计算节点1上执行下面的命令下载cirros镜像,并将其置于共享存储上;
当虚拟机被删除后,位于/var/lib/libvirt/sanlock目录下自动创建的锁文件将会残留,长时间会占用大量的磁盘空间,因此必须要定期对这些自动创建的锁文件进行清理,可以通过下面的命令完成清理操作,通过定时任务每周执行一次即可;
![5d70c808c0a1c1f56d4bc974e084ce3a.png](https://i-blog.csdnimg.cn/blog_migrate/d9eac6df5bec34059590348a97e0cf54.png)
导 语在云中使用虚拟机HA,热迁移等功能的时候,可能会出现两个主机上的虚拟机同时对共享存储上同一个磁盘进行读写操作,导致磁盘数据损坏的问题。Libvirt提供了两种方式实现磁盘资源的互斥,分别是sanlock和lockd。相关的配置可以参考文档:https://libvirt.org/locking.html;本篇描述libvirt使用sanlock作为磁盘锁的使用方法。
![145eba340d59a466a8f27241db2f0682.png](https://i-blog.csdnimg.cn/blog_migrate/df6a5787007a90c52963d42e02341944.png)
[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
2
准备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
3
配置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
![145eba340d59a466a8f27241db2f0682.png](https://i-blog.csdnimg.cn/blog_migrate/df6a5787007a90c52963d42e02341944.png)
[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中如果直接使用磁盘自动加锁是有问题的,如:
- 如果共享存储在不同的计算节点上路径不同,则生成的磁盘锁文件是不同的,会导致对同个磁盘的加锁操作失败;
- cinder支持多挂载的情况下,如果采用自动加锁则会导致挂载同一个数据盘的多个虚拟机无法启动的问题;
- 当虚拟机通过config drive进行配置时,实际上config drive创建的磁盘也是不需要进行加锁操作的,因为该磁始终是只读的;
![2a65d638c554cd83c476da549fd15122.png](https://i-blog.csdnimg.cn/blog_migrate/87367808e63eb8339b5a8fa7cdfe30e2.png)
参考资料
http://ssdxiao.github.io/linux/2017/04/12/Libvirt-Sanlock.html
https://blog.csdn.net/weixin_34258782/article/details/92440532
https://libvirt.org/locking.html
https://libvirt.org/locking-sanlock.html
往期精彩内容回顾
1 | S3请求来了,该怎么处理? |
2 | 初探Docker的网络模式 |
3 | OpenStack Policy鉴权大解密! |
![1992ac605688a9c643456ca579df1d9c.gif](https://i-blog.csdnimg.cn/blog_migrate/c27d0ec74b43ef3a0fef8497988a051a.gif)