从 qemu 到 libvirt 再到 OpenStack 他们的关系是封装越来越多,从而实现下一层次的多后端支持,但这也意味着复杂度的急剧增加,对于 Ceph 而言,通常作为 qemu 的一种硬盘驱动器后端,纯粹从数据 IO 的角度(即 Nova)来看,我们只需要理解 qemu 对 Ceph 的 IO 即可,如果对 qemu 与 Ceph 之间 IO 的调试还需要通过 OpenStack 来进行的话,显然过于麻烦。
当然如果涉及到 OpenStack 的镜像和云盘,则还需要理解 Glance、Cinder 两个组件与 Ceph 的交互。
一些基础知识
qemu-kvm 是上游 qemu 项目的一个 fork,但现在已合入上游:
1 | ~$ qemu-system-x86_64 -h | grep kvm |
查询 qemu 支持模拟的设备:
1 | ~$ 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 远程访问虚机:
1 | ~$ qemu-system-x86_64 -enable-kvm -m 1G -boot menu=on -drive file=root.raw,format=raw -vnc :20 |
此时 vnc 客户端需要访问的端口号为 5900 + 20 即 5920
- 上传本地根文件系统到 Ceph
1 | ~$ qemu-img convert -f raw -O raw root.raw rbd:test_pool/root |
对于需要指定 ceph 参数的情况,参考如下,每个 ceph 选项使用冒号分隔
1 | ~$ qemu-img convert -f raw -O raw ~/root.raw rbd:rbd/root:id=admin:conf=./ceph.conf |
- 从 Ceph 集群上的根文件系统启动虚机
1 | ~$ qemu-system-x86_64 -enable-kvm -m 1G -boot menu=on -drive file=rbd:test_pool/root,format=raw -vnc :20 |
同样,对于需要指定 ceph 参数的情况,参考如下,每个 ceph 选项使用冒号分隔
1 | ~$ 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),如下所示:
1 | ~$ 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 |
- 在 Ceph 集群上创建一个 image,并作为虚机的云盘
1 | ~$ rbd create test_pool/i1 -s 100G |
如果虚机启动过程中出现如下错误:
1 | ioctl(KVM_CREATE_VM) failed: 16 Device or resource busy |
可能是因为系统已经启动了 VirtualBox 或者 VMware 的虚机,最简单的解决方法是停掉这些虚机。
默认创建的虚机启用了网络,实际上是可以通过 host 机器的网络来访问外部网络的,但是虚机内部对外的 ping 并不支持,这个需要特别注意。
监控 qemu 虚机
通过前面的命令创建的虚机我们只能通过 ctrl-c 停止虚机,基本上没有其它操作的可能,实际上 qemu 提供了完善的管理接口(即 qmp 接口),只需要在虚机启动时加上监控的参数(-monitor
或 -qmp
或 -qmp-pretty
)即可。
需要注意的是,一旦加上监控选项,qemu 在启动后会停留在等待客户端连接的状态,需要使用 nc,qmp-shell 等工具连接一下 qemu 才会继续上电。
-monitor
1 | ~$ 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 |
使用 help 命令可以查询所有支持的命令。
-qmp
1 | ~$ 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 存在于 qemu 源代码目录下,路径为 qemu/scripts/qmp/qmp-shell,由于代码对输出的处理 qmp-shell 不支持 -qmp-pretty
的监控选项。
使用 tab 键即可查询所有支持的命令,由于 qmp-shell 支持 tab 键补全,因此相对来说最易用。
如果每次使用 qmp-shell 都要去 qemu 的源代码下执行,会比较麻烦,可以将其放到 /usr/bin 目录下:
1 | ~$ sudo cp qemu/scripts/qmp/qmp-shell /usr/bin |
注意:如果是 CentOS 系统,上面的路径 dist-packages 要换成 site-packages
-qmp-pretty
1 | ~$ 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 |
其中 qmp_capabilities
必须最先执行,否则执行其他命令会报错,而 query-commands 用于查询所有支持的命令。
如果是 OpenStack / libvirt 创建的虚机,可能并没有这些选项,此时可以使用 virsh 的 qemu-monitor-command 命令,如:
1 | ~$ virsh qemu-monitor-command 3 --pretty '{"execute": "query-iothreads"}' |
其中的数字 3 是虚机的 Id,通过 virsh list 命令可以得到该 ID。
iothread
使用上面的命令启动的虚机,默认不会创建单独的 iothread,如果要创建独立的 iothread 可以在启动时添加相关的参数,如下所示:
1 | ~$ qemu-system-x86_64 -enable-kvm -m 1G ... -object iothread,id=iothreadxxx1 |
也可以在运行时通过 qmp 管理接口创建,如下所示(此处我使用的是 qmp-shell 连接的 qemu,因此虚机是使用的 -qmp
监控参数启动的):
1 | (QEMU) query-iothreads |
iothread 实际上要与 virtio 关联起来才有意义,此时在指定硬盘是需要将 -drive
和 -device
分开指定:
1 | ~$ 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 的属性:
1 | ~$ 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- |
参考资料
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