Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qemu monitor for interactive VM management #286

Open
avoiceofreason opened this issue Mar 15, 2024 · 6 comments
Open

Qemu monitor for interactive VM management #286

avoiceofreason opened this issue Mar 15, 2024 · 6 comments

Comments

@avoiceofreason
Copy link

Seems the qemu docker image doesn't have libvirt or virsh for VM management.

However you can do some interactive management via the qemu monitor interface.

First you have to enable monitor and add an endpoint. Unix sockets is probably more secure but telnet is ok for me. Add this qemu argument to your docker startup:

-e ARGUMENTS="-monitor telnet::55555,server,nowait"

and rebuild your container.

I tried to docker port map 55555 to the host, but there is something very weird with telnet to containers. Anyway no matter we just bash straight into the container with:

docker exec -it windows10 bash

We need the telnet client, so do this:

apt update ; apt install -y telnet

then telnet to the qemu monitor from inside the container:

telnet 127.0.0.1 55555

Now you get the (qemu) prompt and type help for all the commands:

(qemu) help

You can add/remove devices, take snapshots, shutdown the VM etc etc Google is your friend

use ctrl+] to quit out of monitor and then "quit" at the telnet prompt and "exit" from the container bash to your host

My use case was to try and hot swap USB devices. But its super flakey and only really works for USB devices that are plugged in at container boot.

Some useful commands are:

see usb devices in VM:
(qemu) info usb

see host usb devices: (this only seems to work if you have already hooked up a host device already)
(qemu) info usbhost

add a host usb device to VM:
(qemu) device_add usb-host,id=myusb,vendorid=0x1234,productid=0x5678

remove a host usb device from VM:
(qemu) device_del myusb

Remember to eject usb storage devices in the VM OS first

Caveats:
1.Telnet is not secure.
2.You can easily wreck your VM by changing boot devices and removing devices
3.You need to install telnet into the container every time you rebuild it
4.I couldn't get telnet to connect properly by mapping ports to the host
5.I couldn't get USB devices to get recognised after ejecting them from the host after VM starts

@kroese
Copy link
Contributor

kroese commented Mar 15, 2024

Great! However, you can do this much easier I think. First, the monitor is already enabled, because the default value for the MONITOR environment variable is:

MONITOR="telnet:localhost:7100,server,nowait,nodelay"

So you dont need to add one on port 55555, because one is already running on port 7100.

Secondly, you dont need to go inside the machine to connect. You can add this port (7100) to the list of ports that will not be forwarded to the VM, by adding this to the compose file:

environment:
  HOST_PORTS: "7100,8600"

Now you can connect to the IP of the container on port 7100 with your telnet client and control the monitor.

@avoiceofreason
Copy link
Author

Thanks kroese.

I didn't spot that the telnet/monitor had already been enabled. Maybe another one for documentation.

A few things though:

1.Port 7100 appears as "font-service" in "netstat -a" so its not easy to spot in the container
2.The monitor telnet is insecure so might be better to have the default as off with a docker variable to turn on
3.I'm still struggling to map a port or hit the container with telnet from the host. It looks to me like something very funky with telnet in docker containers (or me)

with this mapping:

-p 55555:7100

from container >telnet 127.0.0.1 7100 (works)
from host >telnet {container IP} 7100 (just get trying...)
from host >telnet {host IP} 55555 (just get trying...)
from host >telnet {host IP 55556 (get connection refused)

Is it just me or does this work for others?

@kroese
Copy link
Contributor

kroese commented Mar 26, 2024

The monitor telnet is insecure so might be better to have the default as off with a docker variable to turn on

This monitor is used to send the poweroff signal for graceful shutdown, so it cannot be disabled otherwise there is no way to shutdown Windows cleanly.

Also, it is not really insecure since port 7100 is not mapped in the default Dockerfile, so normally this port is not reachable from outside the container.

Is it just me or does this work for others?

It worked for me last time I tried. Does it work when you dont map 55555, but just use 7100:7100 and add 7100 to HOST_PORTS?

@avoiceofreason
Copy link
Author

Nope, if I map 7100:7100 then I get this:

❯ telnet 192.168.1.6 7100
Trying 192.168.0.5...
^C
❯ telnet 192.168.1.6 7101
Trying 192.168.0.5...
telnet: Unable to connect to remote host: Connection refused

So no connection refused on 7100, but also no telnet
I have no problem port mapping anything else...just telnet...weird

@svargh
Copy link

svargh commented May 9, 2024

This would greatly improve kvm experience for me. I like to snapshot a running vm multiple times, and revert back to a given running state.
It helps debugging running software or starting again from an another running branch of a software.

@chuan1127
Copy link

#!/bin/bash

定义Docker容器的名称

DOCKER_CONTAINERS=("qbittorrent" "nas-tools" "transmission" "xiaoyaliu" "MoviePilot")

获取当前小时(24小时制)

CURRENT_HOUR=$(date +"%H")

获取当前分钟

CURRENT_MINUTE=$(date +"%M")

日志文件路径

LOG_FILE="/mnt/user/domains/docker_control.log"

函数:记录日志

log() {
echo "[$(date +"%Y-%m-%d %H:%M:%S")] $1" >> "$LOG_FILE"
echo "[$(date +"%Y-%m-%d %H:%M:%S")] $1"
}

函数:清理日志

cleanup_logs() {
log_size=$(du -m "$LOG_FILE" | cut -f1)
max_log_size=50
if [ "$log_size" -gt "$max_log_size" ]; then
mv "$LOG_FILE" "/mnt/user/domains/docker_control_$(date +"%Y%m%d%H%M%S").log"
touch "$LOG_FILE" # 清空日志文件
log "日志文件超过50M,已清理."
fi
}

log "开始脚本执行."

如果当前时间在0点到7点59分59秒之间

if [ "$CURRENT_HOUR" -ge 0 ] && [ "$CURRENT_HOUR" -lt 8 ] && [ "$CURRENT_MINUTE" -lt 60 ]; then
log "当前时间在0点到7点59分59秒之间,启动Docker容器..."
for CONTAINER in "${DOCKER_CONTAINERS[@]}"; do
log "启动容器 $CONTAINER..."
echo "启动容器 $CONTAINER..."
if docker start "$CONTAINER" >> "$LOG_FILE" 2>&1; then
log "容器 $CONTAINER 启动成功."
echo "容器 $CONTAINER 启动成功."
sleep 5 # 等待5秒
else
log "容器 $CONTAINER 启动失败. 详细错误信息请查看日志."
echo "容器 $CONTAINER 启动失败. 详细错误信息请查看日志."
fi
done
else
log "当前时间不在0点到7点59分59秒之间,停止Docker容器..."
for CONTAINER in "${DOCKER_CONTAINERS[@]}"; do
log "停止容器 $CONTAINER..."
echo "停止容器 $CONTAINER..."
if docker stop "$CONTAINER" >> "$LOG_FILE" 2>&1; then
log "容器 $CONTAINER 停止成功."
echo "容器 $CONTAINER 停止成功."
sleep 5 # 等待5秒
else
log "容器 $CONTAINER 停止失败. 详细错误信息请查看日志."
echo "容器 $CONTAINER 停止失败. 详细错误信息请查看日志."
fi
done
fi

log "脚本执行结束."

清理日志

cleanup_logs

DOCKER_CONTAINERS=("qbittorrent" "nas-tools" "transmission" "xiaoyaliu" "MoviePilot")你可以修改这里的,定时重启容器. 这是一个脚本.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants