一、概述
QEMU Guest Agent是运行在虚拟机内部的一个守护程序(qemu-guest-agent.service),用它来辅助Hypervisor实现对Guest 虚拟机的管理。
通过QEMU Guest Agent可以实现在宿主机对VM发送指令进行管理,例如:查看VM内IP地址、CPU、内存信息等。这种通信不依赖网络,而是通过virtio-serial的方式。使用virtio传递消息,对虚拟机和主机的网络设置没有任何要求,且效率更高,类似于VMware Tools。
二、QGA部署
1.通过libvirt启动虚拟机,在XML中增加channel配置
<channel type='unix'>
<source mode='bind' path='/var/lib/libvirt/qemu/org.qemu.guest_agent.0.101a01fe-ee02-1de3-2922-9718b417332d.sock'/>
<target type='virtio' name='org.qemu.guest_agent.0' state='connected'/>
<alias name='channel0'/>
<address type='virtio-serial' controller='0' bus='0' port='1'/>
</channel>
其中,path为virtio通道在主机本地的映射节点文件,宿主机可以基于此socket文件,通过unix sock实现与虚拟机的通信
2.启动vm,并在vm中安装并启动qemu-ga,linux和windows均支持qemu-ga
linux:
yum install qemu-guest-agent
setenforce 0
systemctl restart qemu-guest-agent.service
3.查看vm内qemu-ga进程
[root@VM-170 network-scripts]# systemctl status qemu-guest-agent
● qemu-guest-agent.service - QEMU Guest Agent
Loaded: loaded (/usr/lib/systemd/system/qemu-guest-agent.service; enabled;>
Active: active (running) since Thu 2022-12-29 17:20:21 CST; 4 days ago
Main PID: 837 (qemu-ga)
Tasks: 2 (limit: 4369)
Memory: 1.0M
CPU: 2ms
CGroup: /system.slice/qemu-guest-agent.service
└─ 837 /usr/bin/qemu-ga --method=virtio-serial --path=/dev/virtio->
Dec 29 17:20:21 localhost systemd[1]: Started qemu-guest-agent.service - QEMU G>
// service配置
[root@VM-170 network-scripts]# cat /usr/lib/systemd/system/qemu-guest-agent.service
[Unit]
Description=QEMU Guest Agent
BindsTo=dev-virtiox2dports-org.qemu.guest_agent.0.device
After=dev-virtiox2dports-org.qemu.guest_agent.0.device
IgnoreOnIsolate=True
[Service]
UMask=0077
EnvironmentFile=/etc/sysconfig/qemu-ga
ExecStart=/usr/bin/qemu-ga
--method=virtio-serial
--path=/dev/virtio-ports/org.qemu.guest_agent.0
--blacklist=${BLACKLIST_RPC}
-F${FSFREEZE_HOOK_PATHNAME}
Restart=always
RestartSec=0
[Install]
WantedBy=dev-virtiox2dports-org.qemu.guest_agent.0.device
在/dev/virtio-ports目录下会生成串口设备
[root@VM-170 network-scripts]# ll /dev/virtio-ports/
total 0
lrwxrwxrwx 1 root root 11 Dec 29 17:20 org.qemu.guest_agent.0 -> ../vport2p1
[root@VM-170 network-scripts]# ll /dev/vport2p1
crw------- 1 root root 238, 1 Dec 29 17:20 /dev/vport2p1
qemu-ga服务配置文件/etc/sysconfig/qemu-ga
[root@VM-170 network-scripts]# cat /etc/sysconfig/qemu-ga
# This is a systemd environment file, not a shell script.
# It provides settings for "/lib/systemd/system/qemu-guest-agent.service".
# Comma-separated blacklist of RPCs to disable, or empty list to enable all.
#
# You can get the list of RPC commands using "qemu-ga --blacklist='?'".
# There should be no spaces between commas and commands in the blacklist.
# 禁用指定的指令
#BLACKLIST_RPC=guest-file-open,guest-file-close,guest-file-read,guest-file-write,guest-file-seek,guest-file-flush,guest-exec,guest-exec-status
# Fsfreeze hook script specification.
#
# FSFREEZE_HOOK_PATHNAME=/dev/null : disables the feature.
#
# FSFREEZE_HOOK_PATHNAME=/path/to/executable : enables the feature with the
# specified binary or shell script.
#
# FSFREEZE_HOOK_PATHNAME= : enables the feature with the
# default value (invoke "qemu-ga --help" to interrogate).
FSFREEZE_HOOK_PATHNAME=/etc/qemu-ga/fsfreeze-hook
ubuntu init.d方式
root@vm-993:~# cat /etc/init.d/qemu-guest-agent
#! /bin/sh
### BEGIN INIT INFO
# Provides: qemu-guest-agent
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: QEMU Guest Agent startup script
# Description: Start the QEMU Guest Agent if we're running
# in a QEMU virtual machine
### END INIT INFO
# Author: Michael Tokarev <mjt@tls.msk.ru>
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="QEMU Guest Agent"
NAME=qemu-ga
DAEMON=/usr/bin/$NAME
PIDFILE=/var/run/$NAME.pid
# config
DAEMON_ARGS=""
# default transport
TRANSPORT=virtio-serial:/dev/virtio-ports/org.qemu.guest_agent.0
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/qemu-guest-agent ] && . /etc/default/qemu-guest-agent
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions
#
# Function that checks whenever system has necessary environment
# It also splits $TRANSPORT into $method and $path
#
do_check_transport() {
method=${TRANSPORT%%:*}; path=${TRANSPORT#*:}
case "$method" in
virtio-serial | isa-serial)
if [ ! -e "$path" ]; then
log_warning_msg "$NAME: transport endpoint not found, not starting"
return 1
fi
;;
esac
}
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon -Sq -p $PIDFILE -x $DAEMON --test > /dev/null
|| return 1
start-stop-daemon -Sq -p $PIDFILE -x $DAEMON -- --daemonize
$DAEMON_ARGS -m "$method" -p "$path"
|| return 2
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon -Kq --retry=TERM/30/KILL/5 -p $PIDFILE --name $NAME
}
case "$1" in
start)
do_check_transport || exit 0
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" $NAME
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" $NAME
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" $NAME && exit 0 || exit $?
;;
restart|force-reload) # we do not support reload
do_check_transport || exit 0
log_daemon_msg "Restarting $DESC" $NAME
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: /etc/init.d/qemu-guest-agent {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
:
4.如果串口name为org.qemu.guest_agent.0,可以通过virsh qemu-agent-command方式通信,QGA为了数据安全性,大部分命令采用异步、base64加密传输
$ virsh qemu-agent-command 101a01fe-ee02-1de3-2922-9718b417332d '{"execute": "guest-exec", "arguments": {"path": "/bin/sh", "capture-output": true, "arg": ["-c", "ip a"]}}'
{"return":{"pid":11510}}
$ virsh qemu-agent-command 101a01fe-ee02-1de3-2922-9718b417332d '{"execute": "guest-exec-status", "arguments": {"pid": '11510'}}'
{"return":{"exitcode":0,"out-data":"MTogbG86IDxMT09QQkFDSyxVUCxMT1dFUl9VUD4gbXR1IDY1NTM2IHFkaXNjIG5vcXVldWUgc3RhdGUgVU5LTk9XTiBncm91cCBkZWZhdWx0IHFsZW4gMTAwMAogICAgbGluay9sb29wYmFjayAwMDowMDowMDowMDowMDowMCBicmQgMDA6MDA6MDA6MDA6MDA6MDAKICAgIGluZXQgMTI3LjAuMC4xLzggc2NvcGUgaG9zdCBsbwogICAgICAgdmFsaWRfbGZ0IGZvcmV2ZXIgcHJlZmVycmVkX2xmdCBmb3JldmVyCiAgICBpbmV0NiA6OjEvMTI4IHNjb3BlIGhvc3QgCiAgICAgICB2YWxpZF9sZnQgZm9yZXZlciBwcmVmZXJyZWRfbGZ0IGZvcmV2ZXIKMjogZXRoMDogPEJST0FEQ0FTVCxNVUxUSUNBU1QsVVAsTE9XRVJfVVA+IG10dSAxNTAwIHFkaXNjIGZxX2NvZGVsIHN0YXRlIFVQIGdyb3VwIGRlZmF1bHQgcWxlbiAxMDAwCiAgICBsaW5rL2V0aGVyIGZhOjE2OjNlOjE4OmM5OjExIGJyZCBmZjpmZjpmZjpmZjpmZjpmZgogICAgYWx0bmFtZSBlbnAwczMKICAgIGFsdG5hbWUgZW5zMwogICAgaW5ldCAxMC4xMDAuMTAuNzYvMjQgYnJkIDEwLjEwMC4xMC4yNTUgc2NvcGUgZ2xvYmFsIGR5bmFtaWMgbm9wcmVmaXhyb3V0ZSBldGgwCiAgICAgICB2YWxpZF9sZnQgMzE0OTM2Mzcyc2VjIHByZWZlcnJlZF9sZnQgMzE0OTM2Mzcyc2VjCiAgICBpbmV0NiAxMDA6MToxN2U6ZDgwMDphMDY2OjQyZTY6NThkMjphMjAyLzEyOCBzY29wZSBnbG9iYWwgZHluYW1pYyBub3ByZWZpeHJvdXRlIAogICAgICAgdmFsaWRfbGZ0IDMxNDkzNjY4NXNlYyBwcmVmZXJyZWRfbGZ0IDMxNDkzNjM4NXNlYwogICAgaW5ldDYgZmU4MDo6ZGQyNDo0Y2UzOmIzOWI6ZWZkYi82NCBzY29wZSBsaW5rIG5vcHJlZml4cm91dGUgCiAgICAgICB2YWxpZF9sZnQgZm9yZXZlciBwcmVmZXJyZWRfbGZ0IGZvcmV2ZXIKMzogZXRoMTogPEJST0FEQ0FTVCxNVUxUSUNBU1QsVVAsTE9XRVJfVVA+IG10dSAxNTAwIHFkaXNjIGZxX2NvZGVsIHN0YXRlIFVQIGdyb3VwIGRlZmF1bHQgcWxlbiAxMDAwCiAgICBsaW5rL2V0aGVyIGZhOjE2OjNlOjlmOmRjOjAwIGJyZCBmZjpmZjpmZjpmZjpmZjpmZgogICAgYWx0bmFtZSBlbnAwczQKICAgIGFsdG5hbWUgZW5zNAogICAgaW5ldCAxMC4yMDAuMTAuMTEvMjQgYnJkIDEwLjIwMC4xMC4yNTUgc2NvcGUgZ2xvYmFsIGR5bmFtaWMgbm9wcmVmaXhyb3V0ZSBldGgxCiAgICAgICB2YWxpZF9sZnQgMzE0OTM2Mzczc2VjIHByZWZlcnJlZF9sZnQgMzE0OTM2Mzczc2VjCiAgICBpbmV0NiAxMDA6MToxNTQ6MjEwMDpkODM3OjgwY2Q6Nzc0OTo1ZDI2LzEyOCBzY29wZSBnbG9iYWwgZHluYW1pYyBub3ByZWZpeHJvdXRlIAogICAgICAgdmFsaWRfbGZ0IDMxNDkzNjY4NHNlYyBwcmVmZXJyZWRfbGZ0IDMxNDkzNjM4NHNlYwogICAgaW5ldDYgZmU4MDo6YTRlNzo5MzljOjY3ZTg6MTE3Ni82NCBzY29wZSBsaW5rIG5vcHJlZml4cm91dGUgCiAgICAgICB2YWxpZF9sZnQgZm9yZXZlciBwcmVmZXJyZWRfbGZ0IGZvcmV2ZXIK","exited":true}}
$ echo MTogbG86IDxMT09QQkFDSyxVUCxMT1dFUl9VUD4gbXR1IDY1NTM2IHFkaXNjIG5vcXVldWUgc3RhdGUgVU5LTk9XTiBncm91cCBkZWZhdWx0IHFsZW4gMTAwMAogICAgbGluay9sb29wYmFjayAwMDowMDowMDowMDowMDowMCBicmQgMDA6MDA6MDA6MDA6MDA6MDAKICAgIGluZXQgMTI3LjAuMC4xLzggc2NvcGUgaG9zdCBsbwogICAgICAgdmFsaWRfbGZ0IGZvcmV2ZXIgcHJlZmVycmVkX2xmdCBmb3JldmVyCiAgICBpbmV0NiA6OjEvMTI4IHNjb3BlIGhvc3QgCiAgICAgICB2YWxpZF9sZnQgZm9yZXZlciBwcmVmZXJyZWRfbGZ0IGZvcmV2ZXIKMjogZXRoMDogPEJST0FEQ0FTVCxNVUxUSUNBU1QsVVAsTE9XRVJfVVA+IG10dSAxNTAwIHFkaXNjIGZxX2NvZGVsIHN0YXRlIFVQIGdyb3VwIGRlZmF1bHQgcWxlbiAxMDAwCiAgICBsaW5rL2V0aGVyIGZhOjE2OjNlOjE4OmM5OjExIGJyZCBmZjpmZjpmZjpmZjpmZjpmZgogICAgYWx0bmFtZSBlbnAwczMKICAgIGFsdG5hbWUgZW5zMwogICAgaW5ldCAxMC4xMDAuMTAuNzYvMjQgYnJkIDEwLjEwMC4xMC4yNTUgc2NvcGUgZ2xvYmFsIGR5bmFtaWMgbm9wcmVmaXhyb3V0ZSBldGgwCiAgICAgICB2YWxpZF9sZnQgMzE0OTM2Mzcyc2VjIHByZWZlcnJlZF9sZnQgMzE0OTM2Mzcyc2VjCiAgICBpbmV0NiAxMDA6MToxN2U6ZDgwMDphMDY2OjQyZTY6NThkMjphMjAyLzEyOCBzY29wZSBnbG9iYWwgZHluYW1pYyBub3ByZWZpeHJvdXRlIAogICAgICAgdmFsaWRfbGZ0IDMxNDkzNjY4NXNlYyBwcmVmZXJyZWRfbGZ0IDMxNDkzNjM4NXNlYwogICAgaW5ldDYgZmU4MDo6ZGQyNDo0Y2UzOmIzOWI6ZWZkYi82NCBzY29wZSBsaW5rIG5vcHJlZml4cm91dGUgCiAgICAgICB2YWxpZF9sZnQgZm9yZXZlciBwcmVmZXJyZWRfbGZ0IGZvcmV2ZXIKMzogZXRoMTogPEJST0FEQ0FTVCxNVUxUSUNBU1QsVVAsTE9XRVJfVVA+IG10dSAxNTAwIHFkaXNjIGZxX2NvZGVsIHN0YXRlIFVQIGdyb3VwIGRlZmF1bHQgcWxlbiAxMDAwCiAgICBsaW5rL2V0aGVyIGZhOjE2OjNlOjlmOmRjOjAwIGJyZCBmZjpmZjpmZjpmZjpmZjpmZgogICAgYWx0bmFtZSBlbnAwczQKICAgIGFsdG5hbWUgZW5zNAogICAgaW5ldCAxMC4yMDAuMTAuMTEvMjQgYnJkIDEwLjIwMC4xMC4yNTUgc2NvcGUgZ2xvYmFsIGR5bmFtaWMgbm9wcmVmaXhyb3V0ZSBldGgxCiAgICAgICB2YWxpZF9sZnQgMzE0OTM2Mzczc2VjIHByZWZlcnJlZF9sZnQgMzE0OTM2Mzczc2VjCiAgICBpbmV0NiAxMDA6MToxNTQ6MjEwMDpkODM3OjgwY2Q6Nzc0OTo1ZDI2LzEyOCBzY29wZSBnbG9iYWwgZHluYW1pYyBub3ByZWZpeHJvdXRlIAogICAgICAgdmFsaWRfbGZ0IDMxNDkzNjY4NHNlYyBwcmVmZXJyZWRfbGZ0IDMxNDkzNjM4NHNlYwogICAgaW5ldDYgZmU4MDo6YTRlNzo5MzljOjY3ZTg6MTE3Ni82NCBzY29wZSBsaW5rIG5vcHJlZml4cm91dGUgCiAgICAgICB2YWxpZF9sZnQgZm9yZXZlciBwcmVmZXJyZWRfbGZ0IGZvcmV2ZXIK | base64 -d
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether fa:16:3e:18:c9:11 brd ff:ff:ff:ff:ff:ff
altname enp0s3
altname ens3
inet 10.100.10.76/24 brd 10.100.10.255 scope global dynamic noprefixroute eth0
valid_lft 314936372sec preferred_lft 314936372sec
inet6 100:1:17e:d800:a066:42e6:58d2:a202/128 scope global dynamic noprefixroute
valid_lft 314936685sec preferred_lft 314936385sec
inet6 fe80::dd24:4ce3:b39b:efdb/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether fa:16:3e:9f:dc:00 brd ff:ff:ff:ff:ff:ff
altname enp0s4
altname ens4
inet 10.200.10.11/24 brd 10.200.10.255 scope global dynamic noprefixroute eth1
valid_lft 314936373sec preferred_lft 314936373sec
inet6 100:1:154:2100:d837:80cd:7749:5d26/128 scope global dynamic noprefixroute
valid_lft 314936684sec preferred_lft 314936384sec
inet6 fe80::a4e7:939c:67e8:1176/64 scope link noprefixroute
valid_lft forever preferred_lft forever
5.如果串口name不是org.qemu.guest.agent.0,则可以通过socat指定socket的方式与vm通信
$ socat unix-connect:/var/lib/libvirt/qemu/org.qemu.guest_agent.0.101a01fe-ee02-1de3-2922-9718b417332d.sock readline