runsisi's

technical notes

通过 qemu 创建虚机

2019-03-02 runsisi#openstack#qemu

从 qemu 到 libvirt 再到 OpenStack 他们的关系是封装越来越多,从而实现下一层次的多后端支持,但这也意味着复杂度的急剧增加,对于 Ceph 而言,通常作为 qemu 的一种硬盘驱动器后端,纯粹从数据 IO 的角度(即 Nova)来看,我们只需要理解 qemu 对 Ceph 的 IO 即可,如果对 qemu 与 Ceph 之间 IO 的调试还需要通过 OpenStack 来进行的话,显然过于麻烦。

当然如果涉及到 OpenStack 的镜像和云盘,则还需要理解 Glance、Cinder 两个组件与 Ceph 的交互。

一些基础知识

qemu-kvm 是上游 qemu 项目的一个 fork,但现在已合入上游:

~$ qemu-system-x86_64 -h | grep kvm

查询 qemu 支持模拟的设备:

~$ qemu-system-x86_64 -device help

qemu-img, qemu-io, qemu-nbd 包含在如下安装包中(通过 apt install qemu 或 yum install qemu 将附带安装):

  • Ubuntu: qemu-utils
  • CentOS: qemu-img

qemu 创建虚机

  1. 准备根文件系统
~$ qemu-img create -f raw root.raw 4G
  1. 给根文件系统安装系统
~$ qemu-system-x86_64 -enable-kvm -m 1G -cdrom ubuntu-16.04.4-server-amd64.iso -boot menu=on -drive file=root.raw,format=raw
  1. 从本地根文件系统启动虚机
~$ qemu-system-x86_64 -enable-kvm -m 1G -boot menu=on -drive file=root.raw,format=raw

如果是通过 ssh 远程进行启动,可以加上 vnc 选项,以便通过 vnc 远程访问虚机:

~$ qemu-system-x86_64 -enable-kvm -m 1G -boot menu=on -drive file=root.raw,format=raw -vnc :20

此时 vnc 客户端需要访问的端口号为 5900 + 20 即 5920

  1. 上传本地根文件系统到 Ceph
~$ qemu-img convert -f raw -O raw root.raw rbd:test_pool/root

对于需要指定 ceph 参数的情况,参考如下,每个 ceph 选项使用冒号分隔

~$ qemu-img convert -f raw -O raw ~/root.raw rbd:rbd/root:id=admin:conf=./ceph.conf
  1. 从 Ceph 集群上的根文件系统启动虚机
~$ qemu-system-x86_64 -enable-kvm -m 1G -boot menu=on -drive file=rbd:test_pool/root,format=raw -vnc :20

同样,对于需要指定 ceph 参数的情况,参考如下,每个 ceph 选项使用冒号分隔

~$ qemu-system-x86_64 -enable-kvm -m 1G -boot menu=on -drive file=rbd:rbd/root:id=admin:keyring=./client.admin:conf=./ceph.conf,format=raw -drive file=rbd:rbd/i1:id=x:keyring=./client.x:conf=./ceph.conf,format=raw -vnc :21

实际测试过程中发现,rbd 指定的 rbd_cache = false 并不会生效,因此 qemu 总是会启用 rbd cache,必须要在 qemu 的命令行中加上 cache=none 的配置(应该是 qemu 2.5 版本的 bug),如下所示:

~$ qemu-system-x86_64 -enable-kvm -m 1G -boot menu=on -drive file=rbd:rbd/root:id=admin:keyring=./client.admin:conf=./ceph.conf,format=raw,cache=none -drive file=rbd:rbd/i1:id=x:keyring=./client.x:conf=./ceph.conf,format=raw,cache=none -vnc :21
  1. 在 Ceph 集群上创建一个 image,并作为虚机的云盘
~$ rbd create test_pool/i1 -s 100G
~$ qemu-system-x86_64 -enable-kvm -m 1G -boot menu=on -drive file=rbd:test_pool/root,format=raw -drive file=rbd:test_pool/i1,format=raw -vnc :20

如果虚机启动过程中出现如下错误:

ioctl(KVM_CREATE_VM) failed: 16 Device or resource busy
failed to initialize KVM: Device or resource busy

可能是因为系统已经启动了 VirtualBox 或者 VMware 的虚机,最简单的解决方法是停掉这些虚机。

默认创建的虚机启用了网络,实际上是可以通过 host 机器的网络来访问外部网络的,但是虚机内部对外的 ping 并不支持,这个需要特别注意。

监控 qemu 虚机

通过前面的命令创建的虚机我们只能通过 ctrl-c 停止虚机,基本上没有其它操作的可能,实际上 qemu 提供了完善的管理接口(即 qmp 接口),只需要在虚机启动时加上监控的参数(-monitor-qmp-qmp-pretty)即可。

需要注意的是,一旦加上监控选项,qemu 在启动后会停留在等待客户端连接的状态,需要使用 nc,qmp-shell 等工具连接一下 qemu 才会继续上电。

-monitor

~$ qemu-system-x86_64 -enable-kvm -m 1G -boot menu=on -drive file=rbd:test_pool/root,format=raw -vnc :20 -monitor unix:./qxxx,server
~$ nc -U ./qxxx

使用 help 命令可以查询所有支持的命令。

-qmp

~$ qemu-system-x86_64 -enable-kvm -m 1G -boot menu=on -drive file=rbd:test_pool/root,format=raw -vnc :20 -qmp unix:./qxxx,server
~$ qmp-shell ./qxxx

命令中的 qmp-shell 存在于 qemu 源代码目录下,路径为 qemu/scripts/qmp/qmp-shell,由于代码对输出的处理 qmp-shell 不支持 -qmp-pretty 的监控选项。

使用 tab 键即可查询所有支持的命令,由于 qmp-shell 支持 tab 键补全,因此相对来说最易用。

如果每次使用 qmp-shell 都要去 qemu 的源代码下执行,会比较麻烦,可以将其放到 /usr/bin 目录下:

~$ sudo cp qemu/scripts/qmp/qmp-shell /usr/bin
~$ sudo cp qemu/scripts/qmp/qmp.py /usr/lib/python2.7/dist-packages/

注意:如果是 CentOS 系统,上面的路径 dist-packages 要换成 site-packages

-qmp-pretty

~$ qemu-system-x86_64 -enable-kvm -m 1G -boot menu=on -drive file=rbd:test_pool/root,format=raw -vnc :20 -qmp-pretty unix:./qxxx,server
~$ nc -U ./qxxx
{"execute": "qmp_capabilities"}
{"execute": "query-commands"}
{"execute": "query-block"}

其中 qmp_capabilities 必须最先执行,否则执行其他命令会报错,而 query-commands 用于查询所有支持的命令。

如果是 OpenStack / libvirt 创建的虚机,可能并没有这些选项,此时可以使用 virsh 的 qemu-monitor-command 命令,如:

~$ virsh qemu-monitor-command 3 --pretty '{"execute": "query-iothreads"}'

其中的数字 3 是虚机的 Id,通过 virsh list 命令可以得到该 ID。

iothread

使用上面的命令启动的虚机,默认不会创建单独的 iothread,如果要创建独立的 iothread 可以在启动时添加相关的参数,如下所示:

~$ qemu-system-x86_64 -enable-kvm -m 1G ... -object iothread,id=iothreadxxx1

也可以在运行时通过 qmp 管理接口创建,如下所示(此处我使用的是 qmp-shell 连接的 qemu,因此虚机是使用的 -qmp 监控参数启动的):

(QEMU) query-iothreads
{"return": []}
(QEMU) object-add qom-type=iothread id=threadxxx
{"return": {}}
(QEMU) query-iothreads
{"return": [{"id": "threadxxx", "thread-id": 9120}]}

iothread 实际上要与 virtio 关联起来才有意义,此时在指定硬盘是需要将 -drive-device 分开指定:

~$ qemu-system-x86_64 -enable-kvm -m 1G -boot menu=on -vnc :20 -qmp unix:./qxxx,server -object iothread,id=iothreadxxx1 -drive file=rbd:rbd/root,format=raw,if=none,id=vd1 -device virtio-blk-pci,drive=vd1,bootindex=0 -drive file=rbd:rbd/i2,format=raw,if=none,id=vd2 -device virtio-scsi-pci -device scsi-hd,drive=vd2

上面的命令之所以要指定 bootindex 是因为同时上面分别创建了一个 IDE 控制器和一个 SCSI 控制器,且分别挂载了一块硬盘设备,因此需要指定通过哪个控制器上的硬盘进行启动。

而真正要让 iothread 与控制器关联起来,需要在控制器属性中加上 iothread 的属性:

~$ qemu-system-x86_64 -enable-kvm -m 1G -boot menu=on -vnc :20 -object iothread,id=iothreadxxx1 -drive file=rbd:rbd/root,format=raw,if=none,id=vd1 -device virtio-blk-pci,drive=vd1,bootindex=0,iothread=iothreadxxx1 -drive file=rbd:rbd/i2,format=raw,if=none,id=vd2 -device virtio-scsi-pci,iothread=iothreadxxx1 -device scsi-
hd,drive=vd2

参考资料

Multiple ways to access QEMU Machine Protocol (QMP)

https://kashyapc.wordpress.com/2013/03/31/multiple-ways-to-access-qemu-machine-protocol-qmp/

QMP ( qemu monitor protocol ) and Different ways of accessing it

https://www.humblec.com/qmp-qemu-monitor-protocol-and-different-ways-of-accessing-it/

qdev-device-use

https://github.com/qemu/qemu/blob/master/docs/qdev-device-use.txt