一、qga简介
1、qga工具
qemu guest agent,简称qga,是一个运行在虚拟机内部的普通应用程序,可实现宿主机libvirt和虚拟机的通信,宿主机可通过向虚机中的qga发送命令控制虚机。
这种通信不依赖网络,而是通过virtio-serial的方式。使用virtio传递消息,对虚拟机和主机的网络设置没有任何要求,且效率更高,类似于VMware Tools。
2、宿主机libvirt和VM通信方式选择
宿主机若要和VM通信,需要满足以下两个条件:
1)vm内部和外部宿主机之间,打开一个通道,进行数据交互
2)vm内部需要安装agent,用于接收外部宿主机具体的指令,并反馈结果
其中,打开通道有2类方法:
(1)走网络:需预插入一张管理网卡,增加网络拓扑复杂性
(2)走设备:只需在VM内部和宿主之间建立一个设备通道,实现简单,为当前使用方式
QEMU通过串口设备的模拟,为宿主机和虚拟机提供了一个数据通道(channel),这个通道的两端分别是在虚拟机内看到的串口和在宿主机上看到的unix socket文件。
串口设备的速率较低,适合小数据量的交换。
3、基本实现原理
1)创建虚机时,需要在xml中配置channel段,写入相关参数,启动虚机时,会在宿主机上生成对应unix socket文件,同时,会在vm里生成一个字符设备,生成的unix socket和字符设备可以理解为一个channel隧道的两端
2)虚拟机镜像制作时,安装好qemu-ga守护进程并配置开启自启动,qemu-ga进程会监听字符设备。
3)宿主机上,将虚机中qga支持的rpc指令,通过channel发送,虚机中的qemu-guest-agent从串口设备收到数据后,执行相关指令。可实现文件读写、密码修改等
二、制作Guest镜像,部署qemu-ga
1、linux虚机安装qemu-ga
(1)rhel/centos安装qga
yum install qemu-guest-agent
(2)配置开机启动
systemctl enable qemu-guest-agent
(3)按需修改/etc/sysconfig/qemu-ga配置文件
[root@centos7-001 ~]# cat /etc/sysconfig/qemu-ga
# Default: /dev/virtio-ports/org.qemu.guest_agent.0
DEVPATH="/dev/virtio-ports/org.qemu.guest_agent.1"
# 禁用指定的指令
#BLACKLIST_RPC=""
2、Windows虚机安装qemu-ga
windows系统需要下载virtio-win,以及qemu-ga安装包
https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/
https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-qemu-ga/
三、xml配置virtio对应设备,创建虚机
1、libvirt在虚拟机创建时启动virtio通道,在xml虚机配置中加入virtio通道设备,如下:
<channel type='unix'><source mode='bind' path='/var/lib/libvirt/qemu/org.qemu.guest_agent.1.instance-00000284.sock'/><target type='virtio' name='org.qemu.guest_agent.1'/><alias name='channel0'/><address type='virtio-serial' controller='0' bus='0' port='1'/>
</channel>
其中,path为virtio通道在主机本地的映射节点文件,宿主机可以基于此socket文件,通过unix sock实现与虚拟机的通信
2、 基于以上xml创建的虚拟机中,会发现新的串口设备
设备名可能是/dev/vport0p0 或/dev/vport0p1,如下:
[root@centos7-001 ~]# cat /etc/sysconfig/qemu-ga |grep guest_agent
# Default: /dev/virtio-ports/org.qemu.guest_agent.0
DEVPATH="/dev/virtio-ports/org.qemu.guest_agent.1"
[root@centos7-001 ~]# ll /dev/virtio-ports/org.qemu.guest_agent.1
lrwxrwxrwx 1 root root 11 Jul 17 10:12 /dev/virtio-ports/org.qemu.guest_agent.1 -> ../vport1p1
[root@centos7-001 ~]# ll /dev/vport1p1
crw------- 1 root root 249, 1 Jul 17 10:12 /dev/vport1p1
[root@centos7-001 ~]#
四、通过socat方式指定socket文件与虚机通信
虚机内部默认串口name修改为org.qemu.guest_agent.1之后,在宿主机上连接虚机,可使用socat方式
(默认org.qemu.guest_agent.0 可以使用 virsh qemu-agent-command方式通信)
1、下载socat,安装到宿主机
http://rpmfind.net/linux/centos/7.8.2003/os/x86_64/Packages/socat-1.7.3.2-2.el7.x86_64.rpm
2、宿主机上执行socat命令连接指定虚机socket文件
3、连接成功后,执行对应的参数,会有返回值
[root@compute1 ~]# socat unix-connect:/var/lib/libvirt/qemu/org.qemu.guest_agent.1.instance-00000275.sock readline
# 查看当前所有支持的命令
{"execute": "guest-info"} <------ 直接输入,返回以下return内容,此命令可以查看所有支持的命令
{"return": {"version": "1.5.3", "supported_commands": [{"enabled": true, "name": "guest-default-dir-file-open"}, {"enabled": true, "name": "guest-file-upload"}, {"enabled": true, "name": "guest-command-py"}, {"enabled": true, "name": "guest-file-delete"}, {"enabled": true, "name": "guest-command"}, {"enabled": true, "name": "guest-net-usage"}, {"enabled": true, "name": "guest-disk-io"}, {"enabled": true, "name": "guest-disk-usage"}, {"enabled": true, "name": "guest-memory-usage"}, {"enabled": true, "name": "guest-cpu-usage"}, {"enabled": true, "name": "guest-get-statvfs"}, {"enabled": true, "name": "guest-set-vcpus"}, {"enabled": true, "name": "guest-get-vcpus"}, {"enabled": true, "name": "guest-network-get-interfaces"}, {"enabled": true, "name": "guest-suspend-hybrid"}, {"enabled": true, "name": "guest-suspend-ram"}, {"enabled": true, "name": "guest-suspend-disk"}, {"enabled": true, "name": "guest-fstrim"}, {"enabled": true, "name": "guest-fsfreeze-thaw"}, {"enabled": true, "name": "guest-fsfreeze-freeze"}, {"enabled": true, "name": "guest-fsfreeze-status"}, {"enabled": true, "name": "guest-file-flush"}, {"enabled": true, "name": "guest-file-seek"}, {"enabled": true, "name": "guest-file-write"}, {"enabled": true, "name": "guest-file-read"}, {"enabled": true, "name": "guest-file-close"}, {"enabled": true, "name": "guest-file-open"}, {"enabled": true, "name": "guest-shutdown"}, {"enabled": true, "name": "guest-info"}, {"enabled": true, "name": "guest-set-time"}, {"enabled": true, "name": "guest-get-time"}, {"enabled": true, "name": "guest-ping"}, {"enabled": true, "name": "guest-sync"}, {"enabled": true, "name": "guest-sync-delimited"}]}}
# Ping虚机qemu-ga,如果不返回错误信息,则成功
{"execute":"guest-ping"}
{"return":{}}
# 获取虚机系统时间
{"execute":"guest-get-time"}
{"return": 1595211016927964000}
# 获取cpu使用率、内存使用率、磁盘使用率及磁盘io(磁盘使用率按分区获取、磁盘io按设备获取)
{"execute":"guest-cpu-usage"}
{"return": 0}
{"execute":"guest-memory-usage"}
{"return": {"free_mem": 882544640, "total_mem": 1027674112, "usage": 15}}
{"execute":"guest-disk-usage"}
{"return": [{"total": 52708569088, "name": "/", "free": 48791240704, "type": "ext4"}]}
{"execute":"guest-disk-io"}
{"return": [{"name": "vda", "io_util": 0.008261, "io_time": 8.169811, "read_kB": 0, "read_cnt": 0, "size": 53687091200, "write_kB": 1.24991, "write_cnt": 0.19036}, {"name": "vdb", "io_util": 0, "io_time": 0, "read_kB": 0, "read_cnt": 0, "size": 5368709120, "write_kB": 0, "write_cnt": 0}]}
# 获取当前socket对应虚机的所有网卡接口信息
{"execute": "guest-network-get-interfaces"} <------ 直接输入,用于获取虚机的所有网卡信息,返回以下return内容
{"return": [{"name": "lo", "ip-addresses": [{"ip-address-type": "ipv4", "ip-address": "127.0.0.1", "prefix": 8}, {"ip-address-type": "ipv6", "ip-address": "::1", "prefix": 128}], "hardware-address": "00:00:00:00:00:00"}, {"name": "eth0", "ip-addresses": [{"ip-address-type": "ipv4", "ip-address": "172.16.6.12", "prefix": 24}, {"ip-address-type": "ipv6", "ip-address": "fe80::f816:3eff:fe6e:c5ab", "prefix": 64}], "hardware-address": "fa:16:3e:6e:c5:ab"}, {"name": "eth1", "ip-addresses": [{"ip-address-type": "ipv4", "ip-address": "172.16.5.29", "prefix": 24}, {"ip-address-type": "ipv6", "ip-address": "fe80::f816:3eff:fef1:f88e", "prefix": 64}], "hardware-address": "fa:16:3e:f1:f8:8e"}, {"name": "eth2", "ip-addresses": [{"ip-address-type": "ipv4", "ip-address": "172.16.1.189", "prefix": 24}, {"ip-address-type": "ipv6", "ip-address": "fe80::f816:3eff:feee:ba60", "prefix": 64}], "hardware-address": "fa:16:3e:ee:ba:60"}]}
# 获取当前socket对应虚机的网卡相关监控信息
{"execute":"guest-net-usage", "arguments":{"mac_list":[{"mac":"fa:16:3e:db:3d:85"},{"mac":"fa:16:3e:d5:9d:ff"},{"mac": "fa:16:3e:66:9f:80"},{"mac":"fa:16:3e:51:17:12"},{"mac":"fa:16:3e:09:5e:e2"},{"mac":"fa:16:3e:03:b0:c4"},{"mac":"fa:16:3e:0c:2d:ad"},{"mac":"fa:16:3e:26:d6:d2"},{"mac":"fa:16:3e:f3:b5:00"},{"mac":"fa:16:3e:19: ec:c9"},{"mac":"fa:16:3e:1e:d0:02"},{"mac":"fa:16:3e:2e:5b:38"},{"mac":"fa:16:3e:a7:99:59"},{"mac":"fa:16:3e:2f:b5:4f"},{"mac":"fa:16:3e:4c:c2:68"},{"mac":"fa:16:3e:22:79:91"},{"mac":"fa:16:3e:6e:c5:ab"}]}} <------ 直接输入,返回以下return内容
{"return": [{"name": "eth0", "out_rate": 0, "in_rate": 60, "mac": "fa:16:3e:6e:c5:ab"}]}
4、通过socket文件连接虚机读取文件的方法
1)创建虚机,并使用账号密码登录虚机内部,创建自定义文件test,如下:
[root@centos7-001 ~]# cat /home/testfile
abcd
2)在宿主机上,使用socat指定socket文件,连接虚机读取指定路径文件的内容
[root@compute2 ~]# socat unix-connect:/var/lib/libvirt/qemu/org.qemu.guest_agent.1.instance-00000299.sock readline
{"execute":"guest-file-open","arguments":{"path":"/home/testfile"}}
{"return": 1134}
{"execute":"guest-file-read","arguments":{"handle": 1134}}
{"return": {"count": 5, "buf-b64": "YWJjZAo=", "eof": true}}
3)对获取的文件内容进行base64解码,可获取到文件中的内容
[root@controller1 ~]# echo -n "YWJjZAo=" |base64 -d
abcd
五、禁用socket文件与虚机通信相关操作
若出于安全考虑,需要禁用qga某些功能,可修改配置文件的BLACKLIST_RPC,添加禁用操作黑名单列表
虚拟机里的/etc/sysconfig/qemu-ga内容中的BLACKLIST_RPC参数可以配置禁止哪些指令
例如,需要禁用guest-file-open,需要做如下修改:
[root@centos7-001 ~]# vim /etc/sysconfig/qemu-ga
#BLACKLIST_RPC=""
BLACKLIST_RPC="guest-file-open"
之后,重启qemu-ga服务
[root@centos7-001 ~]# systemctl restart qemu-ga
验证:
在宿主机上,使用socat指定此虚机socket文件,执行guest-file-open,报错如下。
[root@compute2 ~]# socat unix-connect:/var/lib/libvirt/qemu/org.qemu.guest_agent.1.instance-00000299.sock readline
{"execute":"guest-file-open","arguments":{"path":"/home/testfile"}}
{"error": {"class": "GenericError", "desc": "The command guest-file-open has been disabled for this instance"}}
六、qga应用场景功能
qemu对外提供了一个socket接口,主要用于解决虚机外部无法实现的功能,例如:
? 在线修改密码
? 在线增加公钥
? 在线采集监控性能数据(如cpu使用率、负载、内存使用量等性能指标)
? 设备查看、变更
? 数据备份
七、org.qemu.guest_agent.0 和 org.qemu.guest_agent.1 区别
1)若name是org.qemu.guest_agent.0 [指定name],此时宿主机的libvirt可以和qga对应的socket建立连接。
<target type='virtio' name='org.qemu.guest_agent.0'/>
2)若name不是org.qemu.guest_agent.0,那么在宿主机上的libvirt将不会与socket建立连接。
某些情况下,用户可能只希望在其虚机XML中配置qga,但不希望libvirt连接到qga对应的套接字,此时,target name就要使用除了org.qemu.guest_agent.0之外的name,例如org.qemu.guest_agent.1
需要同时修改guest xml的path和name字段,如下:
<source mode='bind' path='/var/lib/libvirt/qemu/org.qemu.guest_agent.1.instance-00000284.sock'/>
<target type='virtio' name='org.qemu.guest_agent.1'/>
同时,在制作虚机镜像的qga对应配置,也需要修改相关路径name为:org.qemu.guest_agent.1
修改作用:
在宿主机上,无法通过libvirt相关命令来 获取/执行 qga相关命令控制虚拟机(但是还可以通过其它方式来交互,例如socat)。
[root@compute1 ~]# virsh qemu-agent-command instance-00000284 --pretty '{"execute":"guest-net-usage"}'
error: argument unsupported: QEMU guest agent is not configured
八、其它
1、socket文件的创建
在创建虚机时,组装xml文件,libvirt指定xml创建虚机,创建好虚机后,对应path的socket文件会由libvirtd自动创建。
2、qmp
QGA的交互协议是qmp(qemu monitor protocol)协议,使用json格式交换数据。
参考:
https://wiki.libvirt.org/page/Qemu_guest_agent
https://www.cnblogs.com/fzxiaomange/p/qmp-qemu-interactive.html
https://www.cnblogs.com/fzxiaomange/p/kvm-inject.html