虚拟机总脑裂,写坏磁盘怎么办?

2020-07-10 10:40:07 浏览数 (1)

鹏 飞

专注于OpenStack计算、Python。

热爱大海、雪山。

导 语 在云中使用虚拟机HA,热迁移等功能的时候,可能会出现两个主机上的虚拟机同时对共享存储上同一个磁盘进行读写操作,导致磁盘数据损坏的问题。Libvirt提供了两种方式实现磁盘资源的互斥,分别是sanlock和lockd。 相关的配置可以参考文档: https://libvirt.org/locking.html; 本篇描述libvirt使用sanlock作为磁盘锁的使用方法。

一、安装与配置

1

安装sanlock

sanlock用于实现libvirt磁盘锁的具体配置参考文档 https://libvirt.org/locking-sanlock.html

安装 libvirt sanlock 插件;

代码语言:javascript复制
[root@compute01 ~]# yum install libvirt-lock-sanlock

关闭firewall和selinux;

代码语言:javascript复制
# 关闭防火墙[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服务崩溃或异常终止,将会导致整个主机重新启动;

代码语言:javascript复制
# 启动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。

代码语言:javascript复制
[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 daemon11336  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;

代码语言:javascript复制
[root@compute01 ~]# chown -R sanlock:sanlock /var/lib/libvirt/sanlock

3

配置libvirt使用sanlock

修改配置文件/etc/libvirt/qemu.conf,配置libvirt lock_manager使用sanlock;

代码语言:javascript复制
lock_manager = "sanlock"

编辑配置/etc/libvirt/qemu-sanlock.conf,配置sanlock;

代码语言:javascript复制
# 值为1表示采用自动加锁,即对虚拟机的每个磁盘都进行自动加锁处理# 值为0表示关闭自动加锁,加锁操作需要在虚拟机xml文件中实现auto_disk_leases = 1
# 为当前主机分配一个1~2000的唯一值,该值在所有加入sanlock集群的主机中唯一host_id = 1
# 运行sanlock守护进程的用户和组,从前面得知user和group均为sanlockuser = "sanlock"group = "sanlock"

重启libvirtd服务;

代码语言:javascript复制
[root@compute01 ~]# systemctl restart libvirtd

libvirtd服务重启完成,在共享存储挂载的路径下会自动创建出名为__LIBVIRT__DISKS__的文件,该文件是用于管理主机的文件,如果没有该文件,则说明前面的配置存在问题。

代码语言:javascript复制
[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 lver00000000                   __LIBVIRT__DISKS__      9711d473-6095-46db-a37d-407372bd2612.centos-san 0000002853 0001 0001

二、磁盘位于NFS共享存储

本节验证虚拟机磁盘位于NFS共享存储时,sanlock对磁盘加锁的情况。测试方法为在计算节点1和计算节点2上分别启动一个虚拟机来进行测试,两个主机上的虚拟机系统盘均为NFS共享存储上的同一个磁盘。

准备虚拟机系统盘文件,此处采用cirros镜像,在计算节点1上执行下面的命令下载cirros镜像,并将其置于共享存储上;

代码语言:javascript复制
[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上通过下面的命令创建虚拟机;

代码语言:javascript复制
[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 listId    Name                           State----------------------------------------------------1     cirros-vm                      running

查看共享存储路径下,可以发现自动生成了一个以md5值命名的文件,该文件即为磁盘/var/lib/libvirt/sanlock/cirros.disk的锁文件,文件名的md5值即为磁盘全路径字符串的md5值;

代码语言:javascript复制
[root@compute01 ~]# ll /var/lib/libvirt/sanlocktotal 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的租期;

代码语言:javascript复制
[root@compute01 ~]# sanlock direct dump /var/lib/libvirt/sanlock/0f2d1b2c88ac72e97d4a9c4d13fe4db0  offset                            lockspace                                         resource  timestamp  own  gen lver00000000                   __LIBVIRT__DISKS__                 0f2d1b2c88ac72e97d4a9c4d13fe4db0 0000005856 0001 0001 1

尝试在计算节点2上用相同的命令创建虚拟机,从命令输出中可知获取磁盘的写锁失败了,此结果说明sanlock有效的解决了多个虚拟机同时对一个磁盘进行读写的问题;

代码语言:javascript复制
[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" lockIs 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-vmotherwise, please restart your installation.

在计算节点1上关闭cirros-vm;

代码语言:javascript复制
[root@compute01 ~]# virsh destroy cirros-vmDomain cirros-vm destroyed
# 从磁盘锁文件的内容可知,timestamp被置为0,租期释放[root@compute01 ~]# sanlock direct dump /var/lib/libvirt/sanlock/0f2d1b2c88ac72e97d4a9c4d13fe4db0  offset                            lockspace                                             resource  timestamp  own  gen lver00000000                   __LIBVIRT__DISKS__                     0f2d1b2c88ac72e97d4a9c4d13fe4db0 0000000000 0001 0001 1

在计算节点2上重启创建虚拟机cirros-vm成功;

代码语言:javascript复制
[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 listId    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 lver00000000                   __LIBVIRT__DISKS__                 0f2d1b2c88ac72e97d4a9c4d13fe4db0 0000007694 0002 0001 2

在计算节点1上启动cirros-vm,从输出信息可知启动失败的原因是获取lock失败;

代码语言:javascript复制
[root@compute01 ~]# virsh start cirros-vmerror: Failed to start domain cirros-vmerror: 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" lockIs another process using the image [/var/lib/libvirt/sanlock/cirros.disk]?

三、问题

当虚拟机被删除后,位于/var/lib/libvirt/sanlock目录下自动创建的锁文件将会残留,长时间会占用大量的磁盘空间,因此必须要定期对这些自动创建的锁文件进行清理,可以通过下面的命令完成清理操作,通过定时任务每周执行一次即可;

代码语言:javascript复制
# 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创建的磁盘也是不需要进行加锁操作的,因为该磁始终是只读的;

因此在实际将sanlock应用到OpenStack中实现虚拟机高可用时,不能采用磁盘自动加锁的方式。对残留的资源租期文件清理操作,还需要做到高可用,不能因为单个主机挂掉导致残留资源的清理失效。

另外如果OpenStack环境对接的是Ceph RBD或IPSAN,libvirt目前不支持对ceph rbd的加锁操作,同时虽然支持对通过iscsi挂载到主机上的磁盘进行加锁,但是同一个盘在不同的主机上其挂载路径很可能是不同的,导致磁盘锁无法生效。

参考资料

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鉴权大解密!

听说长得好看的人都点了赞和在看!

0 人点赞