QEMU Monitor
qemu命令行使用
sudo qemu-system-x86_64 -enable-kvm -cpu host -m 2048 -smp 4 -drive file=cirros-0.5.2-x86_64-disk.img,format=qcow2 -boot c -nographic -monitor telnet:127.0.0.1:1234,server,nowait
telnet:127.0.0.1:1234
:这部分指定了监控控制台使用 telnet 协议进行连接。其中,127.0.0.1
是监控控制台监听的主机地址,表示监听在本地主机上;1234
是监控控制台监听的端口号,表示监控控制台将在 1234 端口上进行监听。server
:这个选项指示 QEMU 将作为 telnet 服务器启动,即等待客户端连接。nowait
:这个选项指示 QEMU 在启动时不等待监控控制台连接,即在启动后立即开始监听 telnet 连接。这使得 QEMU 可以在后台运行并立即开始启动虚拟机,而不需要等待监控控制台连接。
telnet 127.0.0.1 1234 #连接qemu monitor
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
QEMU 7.2.0 monitor - type 'help' for more information
(qemu) info migrate
globals:
store-global-state: on
only-migratable: off
send-configuration: on
send-section-footer: on
decompress-error-check: on
clear-bitmap-shift: 18
#迁移参数
(qemu) info migrate_parameters
announce-initial: 50 ms
announce-max: 550 ms
announce-rounds: 5
announce-step: 100 ms
compress-level: 1 #压缩级别
compress-threads: 8 #压缩线程数
compress-wait-thread: on #压缩等待线程
decompress-threads: 2 # 解压线程数
throttle-trigger-threshold: 50 #节流触发阈值。指定了在触发节流之前允许的最大脏页数
cpu-throttle-initial: 20
cpu-throttle-increment: 10
cpu-throttle-tailslow: off
max-cpu-throttle: 99
tls-creds: ''
tls-hostname: ''
max-bandwidth: 134217728 bytes/second
downtime-limit: 300 ms #宕机时间限制
x-checkpoint-delay: 20000 ms
block-incremental: off
multifd-channels: 2
multifd-compression: none
xbzrle-cache-size: 67108864 bytes
max-postcopy-bandwidth: 0
tls-authz: ''
QEMU热迁移的全局配置选项:
- store-global-state:这个选项表示是否在迁移过程中存储全局状态。当它被设置为 “on” 时,QEMU 会在迁移过程中存储全局状态,以便在目标主机上恢复虚拟机时使用。
- only-migratable:这个选项表示是否仅迁移可迁移的资源。当它被设置为 “on” 时,QEMU 会仅迁移可迁移的资源,而忽略不可迁移的资源。
- send-configuration:这个选项表示是否在迁移过程中发送虚拟机配置信息。当它被设置为 “on” 时,QEMU 会在迁移过程中发送虚拟机的配置信息,以便在目标主机上正确配置虚拟机。
- send-section-footer:这个选项表示是否在迁移过程中发送节(section)尾部信息。当它被设置为 “on” 时,QEMU 会在迁移过程中发送节的尾部信息,以便在目标主机上正确重建虚拟机的内存布局。
- decompress-error-check:这个选项表示是否在解压过程中检查错误。当它被设置为 “on” 时,QEMU 会在解压过程中进行错误检查,以确保解压操作的正确性。
- clear-bitmap-shift:这个选项表示清除位图的偏移量。在迁移过程中,QEMU 会使用一个位图来跟踪脏页的变化,这个选项指定了清除位图的偏移量。
使用libvirt访问qemu monitor
QMP的全称是QEMU Machine Protocol,是一种以json格式为基础的协议,允许用户通过该接口查询、配置QEMU实例。virsh qemu-monitor-command
子命令支持 –hmp参数,是对QMP命令的简化,大大降低了使用QMP命令的复杂度,并且部分HMP就是对QMP命令进行了封装,底层实际只用的还是QMP命令。但是通过QEMU的官方说明看,HMP是QEMU上的简单交互式监视器,主要为调试和简单的人类使用而设计,更高级别的工具应该连接到QMP , QMP才能提供一个稳定的JSON接口,以便于进行可靠的解析。
QMP
#查询QMP支持的命令
sudo virsh qemu-monitor-command 1 '{ "execute": "query-commands" }' --pretty
#查询热迁移相关的命令
sudo virsh qemu-monitor-command 1 '{ "execute": "query-commands" }' --pretty | grep migrate
"name": "migrate-pause"
"name": "migrate-recover"
"name": "migrate-incoming"
"name": "migrate"
"name": "migrate-continue"
"name": "migrate_cancel"
"name": "migrate-start-postcopy"
"name": "client_migrate_info"
"name": "query-migrate-parameters"
"name": "migrate-set-parameters"
"name": "query-migrate-capabilities"
"name": "migrate-set-capabilities"
"name": "query-migrate"
sudo virsh qemu-monitor-command <domain> '{"execute": "command", "arguments":
{"": ,"": ,"": }}' --pretty
#迁移配置
sudo virsh qemu-monitor-command cirros0.5.2 '{ "execute": "query-migrate-parameters"}' --pretty #pretty参数为格式化输出结果
{
"return": {
"cpu-throttle-tailslow": false, #是否启用了对尾部慢操作的 CPU 节流。
"xbzrle-cache-size": 67108864, #XBZRLE 压缩算法的缓存大小
"cpu-throttle-initial": 20, #初始 CPU 节流百分比
"announce-max": 550, #迁移期间的最大公告数
"decompress-threads": 2, #解压缩的线程数
"compress-threads": 8, #压缩的线程数
"compress-level": 1, #迁移的压缩级别
"multifd-channels": 2, #multifd 迁移的通道数
"multifd-zstd-level": 1, #multifd 迁移的 Zstd 压缩级别
"announce-initial": 50, #迁移期间的初始公告数
"block-incremental": false, #是否使用增量块迁移
"compress-wait-thread": true, #是否使用专用线程进行压缩
"downtime-limit": 300, #迁移期间的最大停机时间
"tls-authz": "", #TLS 授权信息
"multifd-compression": "none", # multifd 迁移的压缩算法
"announce-rounds": 5, #迁移期间的公告轮数
"announce-step": 100, #公告之间的迁移步数
"tls-creds": "", #TLS 凭据
"multifd-zlib-level": 1, #multifd 迁移的 Zlib 压缩级别
"max-cpu-throttle": 99, #最大 CPU 节流百分比
"max-postcopy-bandwidth": 0, # postcopy 迁移的最大带宽
"tls-hostname": "", #TLS 主机名
"throttle-trigger-threshold": 50, #触发 CPU 节流的阈值
"max-bandwidth": 134217728, #迁移的最大带宽
"x-checkpoint-delay": 20000, #迁移的检查点延迟
"cpu-throttle-increment": 10 #CPU节流增量
},
"id": "libvirt-425"
}
#修改热迁移中的参数
sudo virsh qemu-monitor-command <domain> '{"execute": "migrate-set-parameters", "arguments": {"<options>": }}'
#修改压缩级别
sudo virsh qemu-monitor-command 1 '{"execute": "migrate-set-parameters", "arguments": {"compress-level": 9}}'
{"return":{},"id":"libvirt-479"}
HMP
sudo virsh qemu-monitor-command --hmp <domain> "<command>"
#迁移配置
sudo virsh qemu-monitor-command 1 --hmp "info migrate_capabilities"
xbzrle: off #XBZRLE(XOR-Based Zero Run-Length Encoding)压缩算法
rdma-pin-all: off #在迁移期间锁定所有内存页以便使用 RDMA
auto-converge: off #在迁移过程中自动调整源虚拟机的性能
zero-blocks: off #传输零块以提高迁移效率
compress: off #在迁移过程中对数据进行压缩
events: on #启用事件通知机制,用于在迁移过程中通知相关事件。
postcopy-ram: off #启用后迁移(postcopy)方式迁移内存
x-colo: off #跨数据中心协作(COLO)模式
release-ram: off #迁移完成后释放源虚拟机的内存
block: off #块复制的方式进行迁移
return-path: off #迁移的返回路径功能
pause-before-switchover: off #切换前暂停迁移
multifd: off #多文件描述符传输方式
dirty-bitmaps: off #使用脏位图来跟踪内存页面的变化
postcopy-blocktime: off #后迁移模式下块传输的超时时间
late-block-activate: off #迁移结束时激活块复制
x-ignore-shared: off #忽略共享内存的迁移
validate-uuid: off #验证迁移中的 UUID
background-snapshot: off #迁移期间使用后台快照
zero-copy-send: off #零拷贝方式发送数据
postcopy-preempt: off #后迁移模式下允许抢占
修改上述中的capabilities,以开启postcopy-ram为例:
#启用postcopy后迁移方式
sudo virsh qemu-monitor-command 8 '{ "execute": "migrate-set-capabilities", "arguments": { "capabilities": [ { "capability": "postcopy-ram", "state": true } ] } }' --pretty
{
"return": {
},
"id": "libvirt-1755"
}
#块复制的方式迁移
sudo virsh qemu-monitor-command 8 '{ "execute": "migrate-set-capabilities", "arguments": { "capabilities": [ { "capability": "block", "state": true } ] } }' --pretty
{
"return": {
},
"id": "libvirt-1757"
}
查看迁移信息
sudo virsh qemu-monitor-command ubuntu22.04 '{ "execute": "query-migrate"}' --pretty
{
"return": {
"expected-downtime": 300, # 预期的停机时间,单位为秒
"status": "active", # 迁移状态,此处为活动状态
"setup-time": 17, # 设置时间,单位为毫秒
"total-time": 9274, # 总迁移时间,单位为毫秒
"ram": {
"total": 2282831872, # 内存总量,单位为字节
"postcopy-requests": 0, # 后拷贝请求的次数
"dirty-sync-count": 1, # 脏页同步计数
"multifd-bytes": 0, # 多文件描述符的字节数
"pages-per-second": 562349, # 每秒的页数
"downtime-bytes": 0, # 停机期间的字节数
"page-size": 4096, # 页面大小,单位为字节
"remaining": 738705408, # 剩余的迁移量,单位为字节
"postcopy-bytes": 0, # 后拷贝字节数
"mbps": 393.85467961165051, # 迁移速率,单位为兆字节/秒
"transferred": 747652138, # 已传输的字节数
"dirty-sync-missed-zero-copy": 0, # 脏页同步丢失的零拷贝次数
"precopy-bytes": 747652138, # 预拷贝字节数
"duplicate": 195235, # 重复的字节数
"dirty-pages-rate": 0, # 脏页率
"skipped": 0, # 跳过的字节数
"normal-bytes": 744439808, # 正常字节数
"normal": 181748 # 正常数量
}
},
"id": "libvirt-656"
}
sudo virsh qemu-monitor-command ubuntu22.04 '{ "execute": "query-migrate"}' --pretty
{
"return": {
"expected-downtime": 300, # 预期的停机时间,单位为秒
"status": "device", # 迁移状态,此处为设备迁移
"setup-time": 17, # 设置时间,单位为毫秒
"total-time": 10827, # 总迁移时间,单位为毫秒
"ram": {
"total": 2282831872, # 内存总量,单位为字节
"postcopy-requests": 0, # 后拷贝请求的次数
"dirty-sync-count": 3, # 脏页同步计数
"multifd-bytes": 0, # 多文件描述符的字节数
"pages-per-second": 26228, # 每秒的页数
"downtime-bytes": 7547757, # 停机期间的字节数
"page-size": 4096, # 页面大小,单位为字节
"remaining": 0, # 剩余的迁移量,单位为字节
"postcopy-bytes": 0, # 后拷贝字节数
"mbps": 717.61919999999998, # 迁移速率,单位为兆字节/秒
"transferred": 885381867, # 已传输的字节数
"dirty-sync-missed-zero-copy": 0, # 脏页同步丢失的零拷贝次数
"precopy-bytes": 877834110, # 预拷贝字节数
"duplicate": 344240, # 重复的字节数
"dirty-pages-rate": 104, # 脏页率
"skipped": 0, # 跳过的字节数
"normal-bytes": 880562176, # 正常字节数
"normal": 214981 # 正常数量
}
},
"id": "libvirt-662"
}
脏页速率计算
QEMU 提供了几种方法来测量客户机的脏页率。这可以用于虚拟机监控目的,并且可以提供有关使用在线迁移迁移此虚拟机的难度的线索。
目前有三种模式支持脏速率计算:
- Page sampling 页面采样
- Dirty bitmap 脏位图
- Dirty ring 脏环
页面采样模式可以随时使用,而脏位图或脏环模式将取决于为特定虚拟机启用了什么样的脏页跟踪机制。
对于QMP,可以使用命令“calc-dirty-rate”来触发具有特定参数的示例过程。然后,可以使用“query-dirty-rate”来检查结果。对应的HMP命令是“calc_dirty_rate”和“info dirty-rate”。
Page sampling 页面采样模式
当跟踪被触发时,虚拟机管理程序将选择几个页面(在sample-pages=参数中指定,默认值为每GB512个页面),计算这些页面的哈希值并记住它们。然后,虚拟机管理程序等待特定的时间长度(由call-time=指定)并重新进行哈希计算。如果任何页面在其存储的数据上获得了不同的哈希值,则意味着此页面在此期间已更改。
示例:
sudo virsh qemu-monitor-command 1 '{"execute": "calc-dirty-rate", "arguments":
{"calc-time": 10, "mode": "page-sampling", "sample-pages": 1024}}' --pretty
{
"return": {
},
"id": "libvirt-491"
}
#在calc-time结束之前,status显示measuring测量
sudo virsh qemu-monitor-command 1 '{"execute": "query-dirty-rate"}' --pretty
{
"return": {
"status": "measuring",
"sample-pages": 1024,
"mode": "page-sampling",
"start-time": 2707018,
"calc-time": 10
},
"id": "libvirt-492"
}
#在calc-time结束之后,status显示measuring测量
sudo virsh qemu-monitor-command 1 '{"execute": "query-dirty-rate"}' --pretty
{
"return": {
"status": "measured",
"sample-pages": 1024,
"dirty-rate": 0,
"mode": "page-sampling",
"start-time": 2707018,
"calc-time": 10
},
"id": "libvirt-493"
}
脏页位图模式
我们可以在客户端启用脏位图模式的脏率测量,当基于脏位图的脏追踪在客户端启用时(在 QEMU 命令行中未指定“-accel kvm,dirty-ring-size=N”)。基于脏位图的脏追踪使用位图来表示客户端内存。当写入一页时,一个位(位图的一部分)将被设置,这意味着此位表示的页面是脏的。这个位图是针对每个虚拟机的,这意味着我们不会区别对待每个虚拟 CPU - 任何写入一页的虚拟 CPU 都会设置该位。一旦设置了该位,它将在整个脏率计算过程中始终设置。
sudo virsh qemu-monitor-command 5 '{"execute": "calc-dirty-rate", "arguments":
{"calc-time": 10, "mode": "dirty-bitmap"}}' --pretty
{
"return": {
},
"id": "libvirt-1141"
}
sudo virsh qemu-monitor-command 5 '{"execute": "query-dirty-rate"}' --pretty
{
"return": {
"status": "measured",
"sample-pages": 0,
"dirty-rate": 0,
"mode": "dirty-bitmap",
"start-time": 2530,
"calc-time": 10
},
"id": "libvirt-1154"
}
脏环模式
Dirty ring模式可以在每个虚拟CPU(vCPU)基础上提供更精细的脏页率测量。只有当为特定的客户端启用了脏页环(通过在QEMU命令行中指定"-enable-kvm,dirty-ring-size=N")时,才能使用该模式。
脏页环是一个针对每个虚拟CPU的结构,其中包含一个包含客户端内存页面的页框号(PFN)数组。这意味着首先每个虚拟CPU都有自己的环结构来保持脏页,同时一个脏页可能存在于多个环中。例如,如果两个虚拟CPU写入相同的页面,而该页面以前是干净的,那么这两个虚拟CPU将分别将该页面的一个PFN推送到自己的脏环中,该PFN将作为脏页报告给用户空间(QEMU)。在计数时,同一个页面可能会被计数多次。但这并不总是发生,例如,如果第二个虚拟CPU在第一个虚拟CPU写入后再写入并解析页面故障,那么只会记录一个脏的PFN,并且它只会记录在第一个虚拟CPU的脏环中。
示例:
sudo virsh qemu-monitor-command 2 '{"execute": "calc-dirty-rate", "arguments":
{"calc-time": 10, "mode": "dirty-ring"}}' --pretty
[sudo] nans 的密码:
{
"id": "libvirt-541",
"error": {
"class": "GenericError",
"desc": "mode dirty-ring is not enabled, use other method instead." //没有给虚拟机启动脏环模式
}
}
修改虚拟机xml文件:
sudo gedit /etc/libvirt/qemu/ubuntu22.04.xml
#添加下面参数
<qemu:commandline>
<qemu:arg value='-accel'/>
<qemu:arg value='kvm,dirty-ring-size=65536'/>
</qemu:commandline>
sudo define /etc/libvirt/qemu/ubuntu22.04.xml #使xml文件生效后,dirty ring没有生效
使用qemu命令行-accel kvm,dirty-ring-size=65536
启用dirty ring后使用qemu monitor监控虚拟机的脏页速率
查看脏页产生速率:
(qemu) info dirty_rate
Status: measured
Start Time: 2786416 (ms)
Sample Pages: 0 (per GB)
Period: 3 (sec)
Mode: dirty-ring
Dirty rate: 97 (MB/s)
vcpu[0], Dirty rate: 20 (MB/s)
vcpu[1], Dirty rate: 36 (MB/s)
vcpu[2], Dirty rate: 24 (MB/s)
vcpu[3], Dirty rate: 17 (MB/s)
(qemu) info dirty_rate
Status: measured
Start Time: 2786476 (ms)
Sample Pages: 0 (per GB)
Period: 10 (sec)
Mode: dirty-ring
Dirty rate: 19 (MB/s)
vcpu[0], Dirty rate: 4 (MB/s)
vcpu[1], Dirty rate: 5 (MB/s)
vcpu[2], Dirty rate: 6 (MB/s)
vcpu[3], Dirty rate: 4 (MB/s)
可以看到脏环模式是以一个vcpu为单位记录的脏页速率。
参考
官方文档:https://www.qemu.org/docs/master/system/monitor.html#
wiki:https://wiki.qemu.org/Documentation/QMP#Examples
xml文件配置qemu命令行参数:https://unix.stackexchange.com/questions/235414/libvirt-how-to-pass-qemu-command-line-args,https://www.libvirt.org/kbase/qemu-passthrough-security.ht