在libvirt中使用QCOW2多级快照导致虚拟机无法启动的原因分析及解决


声明:本文转载自https://my.oschina.net/LastRitter/blog/1542552,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

问题重现

环境

  1. 在支持Intel VT-x虚拟化的PC上安装Windows7 SP1 64位;

  2. 在VMware Workstation 12.1.0中安装CentOS 7 x64,同时开启Intel VT-x支持;

  3. 在虚拟机内部编译安装Qemu 2.5.0(编译参数:--target-list=x86_64-softmmu --enable-debug-tcg --enable-debug-info --enable-sdl --enable-curses --enable-vnc --enable-kvm)和Libvirt 1.3.4(编译参数:--disable-nls --enable-debug)。

步骤

  1. 在LVM分区上直接创建qcow2的外部镜像:
  • 创建测试磁盘
$ mkdir -pv /opt/vm/ $ dd if=/dev/zero of=/opt/vm/disk.img bs=1024 count=4M $ losetup /dev/loop0 /opt/vm/disk.img 
  • 创建LVM卷组和分区
$ vgcreate qemu /dev/loop0 $ lvcreate -L 256M -n image01 qemu $ lvcreate -L 256M -n image02 qemu $ lvcreate -L 256M -n image03 qemu 
  • 格式化
$ qemu-img create -f qcow2 /dev/qemu/image01 128M $ qemu-img create -f qcow2 -b /dev/qemu/image01 /dev/qemu/image02 128M $ qemu-img create -f qcow2 -b /dev/qemu/image02 /dev/qemu/image03 128M 
  1. 配置虚拟机,配置文件test.xml 的内容如下:
<domain type='kvm'> 	<name>test</name> 	<memory>262144</memory> 	<vcpu>1</vcpu> 	<os> 		<type arch='x86_64' machine='pc'>hvm</type> 		<boot dev='hd'/> 	</os> 	<features> 		<acpi/> 		<apic/> 		<pae/> 	</features> 	<clock offset='localtime'/> 	<on_poweroff>destroy</on_poweroff> 	<on_reboot>restart</on_reboot> 	<on_crash>destroy</on_crash> 	<devices> 		<emulator>/opt/vm/qemu/build-v2.5.0/x86_64-softmmu/qemu-system-x86_64</emulator> 		<disk type='file' device='disk'> 			<driver name='qemu' type='qcow2'/> 			<source file='/dev/qemu/image03'/> 			<target dev='hda' bus='ide'/> 		</disk> 		<input type='tablet' bus='usb'/> 		<input type='mouse' bus='ps2'/> 		<graphics type='vnc' port='-1' listen = '0.0.0.0' autoport='yes' keymap='en-us'/> 	</devices> </domain> 
  1. 启动虚拟机,进入Libvirt的编译目录,依次执行如下命令:
$ ./src/virtlockd -d $ ./daemon/libvirtd -d $ ./tools/virsh define test.xml $ ./tools/virsh start test error: Failed to start domain test error: internal error: process exited while connecting to monitor: 2016-05-16T02:43:42.812996Z qemu-system-x86_64: -drive file=/dev/qemu/image03,format=qcow2,if=none,id=drive-scsi0-0-0: Could not open backing file: Could not open backing file: Could not open '/dev/qemu/image01': Operation not permitted 

原因分析

  • 这个问题仅仅在使用LVM分区直接创建qcow2多级快照,且使用Libvirt进行启动时出现权限问题。修改文件的GID、UID和权限,libvirtd的执行用户组和用户,关闭或配置AppArmor均无法解决此问题。

  • 使用相同的命令行参数直接使用Qemu命令行运行正常,但使用Libvirt启动时就会出现上述问题。通过对Qemu的源码分析和调试,发现最终问题出在打开镜像文件的open函数上,均会以errno为1的方式返回错误。且在两种运行方式中,open时的Linux权限相关设置完全相同。

  • 而这两种启动方式的主要差别在于,libvirtd启动时使用了QMP与Qemu进行通信,以及fork时设置了不同的进程运行环境。通过测试和源码分析发现open函数失败在Qemu和libvirtd建立QMP通信之前,所以可以排除是QMP导致的问题。

因此问题可能就出现在Libvirt启动Qemu时对其进程运行环境进行的设置,最终通过测试和源码分析发现问题就在这里。

过程分析

  1. libvirtd在启动Qemu时,会分析Qemu的磁盘配置,根据其中设置的路径和类型分析它的Backing File,并生成一个链表。上述配置<source file='/dev/qemu/image03'/>则会生成类似这两的结构“image03-> image02-> image01”。

  2. 然而在这个分析过程中,如果imageXX中并未包含Backing File的格式定义,比如image03中并未说明image02的格式,image02中并未说明image01的格式;同时在libvirtd的qemu.conf中并未设置“allow_disk_format_probing = 1”来探测image02和image01的格式。则libvirtd只会生成“image03-> image02”这一级的链表,而不会进行更深度的遍历。

  3. 在libvirtd创建(fork)出Qemu进程,在执行(exec)之前会对这个子进程进行一系列的设置,其中包括对子进程cgroup的设置。在对进程的设备访问权限进行设置时使用的是白名单,只会允许Qemu子进程对之前分析出来的磁盘链表(如image03和 image02,却没有image01的访问权限,所以最终在打开image01时,会因为权限不足而打开失败。

相关源码

  1. src/util/virstoragefile.c中virStorageFileGetMetadataInternal()、qcow2GetBackingStore()和qcowXGetBackingStore()函数,启动Qemu进程前分析配置文件中设置的磁盘镜像的Backing File和Format。

  2. src/storage/storage_driver.c中virStorageFileGetMetadataRecurse()函数,迭代调用virStorageFileGetMetadataInternal()遍历所有磁盘镜像和他的Backing File。

  3. src/qemu/qemu_process.c中qemuProcessLaunch()函数,fork新进程,设置新进程,exec新Qemu进程。

  4. src/qemu/qemu_cgroup.c中qemuSetupDevicesCgroup()函数,设置进程的设备cgroup配额。

解决办法

  1. 修改/etc/libvirt/qemu.conf中的cgroup设置,默认不禁止对设备文件(devices)的访问,或者将要访问的设备文件加入允许列表。

cgroup_controllers = [ "cpu", "devices", "memory", "blkio", "cpuset", "cpuacct" ] 

改为

cgroup_controllers = [ "cpu", "memory", "blkio", "cpuset", "cpuacct" ] 

或者把image01加入默认允许访问列表

cgroup_device_acl = [     "/dev/null", "/dev/full", "/dev/zero",     "/dev/random", "/dev/urandom",     "/dev/ptmx", "/dev/kvm", "/dev/kqemu",     "/dev/rtc","/dev/hpet", "/dev/vfio/vfio", "/dev/qemu/image01" ] 
  1. 修改/etc/libvirt/qemu.conf,允许镜像文件的格式探测(有安全风险,不建议使用)。
allow_disk_format_probing = 1 
  1. 在创建新的qcow2镜像时指定Backing File的格式。
$ qemu-img create -f qcow2 /dev/qemu/image01 128M $ qemu-img create -f qcow2 -b /dev/qemu/image01 -o backing_fmt=qcow2 /dev/qemu/image02 128M $ qemu-img create -f qcow2 -b /dev/qemu/image02 -o backing_fmt=qcow2 /dev/qemu/image03 128M 
  1. 使用rebase命令修改已存在的镜像的Backing File格式。
$ qemu-img rebase -f qcow2 -b /dev/qemu/image02 -F qcow2 /dev/qemu/image03 $ qemu-img rebase -f qcow2 -b /dev/qemu/image01 -F qcow2 /dev/qemu/image02 

优先推荐第3种或者第4种方法。

本文发表于2017年09月24日 22:36
(c)注:本文转载自https://my.oschina.net/LastRitter/blog/1542552,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2457 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1