博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在libvirt中使用QCOW2多级快照导致虚拟机无法启动的原因分析及解决
阅读量:6884 次
发布时间:2019-06-27

本文共 4186 字,大约阅读时间需要 13 分钟。

hot3.png

问题重现

环境

  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 的内容如下:
test
262144
1
hvm
destroy
restart
destroy
/opt/vm/qemu/build-v2.5.0/x86_64-softmmu/qemu-system-x86_64
  1. 启动虚拟机,进入Libvirt的编译目录,依次执行如下命令:
$ ./src/virtlockd -d$ ./daemon/libvirtd -d$ ./tools/virsh define test.xml$ ./tools/virsh start testerror: Failed to start domain testerror: 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种方法。

转载于:https://my.oschina.net/LastRitter/blog/1542552

你可能感兴趣的文章
编译OpenWRT过程问题解决
查看>>
整合大量开源库温习基础项目(三)登陆注册主页面大致完成,分析下怎么处理用户信息...
查看>>
详细解释强力的图片加载框架 Glide的配置(顺便补充下CollapsingToolbarLayout的一些功能)...
查看>>
[经典面试题][暴风影音]暴风影音2014校招笔试题
查看>>
jsp重定向forward和sendRedirect的比较
查看>>
MDT部署时,客户端得不到IP地址问题
查看>>
并发控制CountDownLatch、CyclicBarrier和Semaphore
查看>>
MongoDB学习笔记(五) MongoDB文件存取操作
查看>>
Apache模块谈
查看>>
数据系列专题视频课程-处理重复数据发布啦
查看>>
ELK5.4 修改分片数及分片分配方式
查看>>
搭建openstack-icehouse本地yum源
查看>>
sybase笔记
查看>>
Android 状态栏通知Notification、NotificationManager详解
查看>>
如何从网络获取图片显示并保存到SD卡里
查看>>
Active Directory 账号迁移配置介绍
查看>>
java泛型之带有两个类型参数的泛型示例
查看>>
LAN与WAN
查看>>
我的友情链接
查看>>
MySQL中sql语句的 join 用法
查看>>