Last active
September 23, 2021 10:34
-
-
Save mark-kubacki/18d34b52a61906cc24f94ec39c8d34ff to your computer and use it in GitHub Desktop.
Windows in Docker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Image meant to host Windows in QEMU, hence we install QEMU | |
# and everything that is needed to get the networking for it working. | |
FROM ubuntu:latest | |
RUN apt-get -q update \ | |
&& apt-get -y install \ | |
kvm qemu-kvm bridge-utils psmisc \ | |
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* | |
RUN update-alternatives --install /usr/bin/qemu qemu /usr/bin/qemu-system-x86_64-spice 10 | |
EXPOSE 3389 5900 | |
# 3389 for Windows RDP | |
# 5900 for QEMU's SPICE | |
VOLUME /var/cache/media /var/vm | |
COPY kvm-bootstrap.sh /sbin/kvm-bootstrap.sh | |
ENTRYPOINT ["/sbin/kvm-bootstrap.sh"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# This file encapsulates the rather elaborate networking setup within Docker. | |
# Furthermore, absent any Windows VM file, starts the Windows installation from a provided ISO file. | |
set -eo pipefail | |
if [[ ! -z ${debug+x} ]]; then | |
set -x | |
fi | |
# Again, this is run within the Docker container. | |
# Usually runtimes will replace /dev with a standardized set of devices. | |
# That’s why we need to extend them: | |
if [[ ! -c "/dev/kvm" ]]; then | |
rm /dev/kvm || true | |
set +e | |
read -r NODNUM _ < <(grep '\<kvm\>' /proc/misc) | |
mknod /dev/kvm c 10 "${NODNUM}" | |
set -e | |
else | |
if ! dd if=/dev/kvm count=0 2>/dev/null; then | |
>&2 printf "Cannot open /dev/kvm - please run this in a privileged context.\n" | |
# see: /usr/include/sysexits.h: EX_OSFILE | |
exit 72 | |
fi | |
fi | |
if [[ ! -z "${BRIDGE_IF+x}" ]]; then | |
printf "allow ${BRIDGE_IF}" >/etc/qemu/bridge.conf | |
# Make sure we have the tun device node | |
if [[ ! -c "/dev/net/tun" ]]; then | |
rm /dev/net/tun || true | |
mkdir -p /dev/net | |
set +e | |
read -r NODNUM _ < <(grep '\<tun\>' /proc/misc) | |
mknod /dev/net/tun c 10 "${NODNUM}" | |
set -e | |
fi | |
fi | |
# End of network plumbing. | |
# Start of setting up, or setting up and starting the actual VM. | |
# This script accepts some arguments (the first one must be "windows"; else this runs any supplied KVM), | |
# which are referred to by shortcuts such as: | |
# windows <spice address> <spice port> <spice password> <MAC> <nic-device> <name> <memory in MB> <boot ISO> | |
# example: | |
# windows ${PUBLIC_IPV4} 5900 geheim 52:54:00:xx:xx:xx macvtap0 "windows-1" $((16 * 8 * 1024)) Windows-10-threshold-2-take-1.iso | |
if (( $# >= 8 )) && [[ "$1" == "windows" ]]; then | |
# No prior Windows installation? Create a fresh image: | |
if [[ ! -e /var/vm/disks/"$7".img ]]; then | |
mkdir -p /var/vm/disks | |
chmod 0700 /var/vm/disks | |
qemu-img create -f qcow2 /var/vm/disks/"$7".img 80G | |
fi | |
read MAJOR MINOR < <(cat /sys/devices/virtual/net/$6/tap*/dev | tr ':' ' ') | |
mknod /dev/tap-vm c ${MAJOR} ${MINOR} | |
# -device virtio-balloon,id=balloon0,bus=pci.0,addr=0x7 \ Windows 10 won't start with this | |
if [[ -z ${ncores+x} ]]; then | |
: ${ncores:="4"} | |
if (( $(nproc) > 16 )); then | |
# MCC or HCC cpu(s) | |
if (( $(nproc) > 20)); then | |
let ncores="$[ $(nproc --ignore 4)/2 ]" | |
else | |
let ncores="$(nproc --ignore 2)" | |
fi | |
fi | |
fi | |
drives=() | |
drives+=("-drive" "file=/var/vm/disks/$7.img,if=virtio,index=0,media=disk") | |
if [[ -s /var/cache/media/virtio-win.iso ]]; then | |
drives+=("-drive" "file=/var/cache/media/virtio-win.iso,index=3,media=cdrom,readonly") | |
else | |
>&2 printf "Virtio drivers not found in: %s\n" "/var/cache/media/virtio-win.iso" | |
>&2 printf "Probably okay if this is no Windows, else download them from here:\n" | |
>&2 printf " https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso\n" | |
fi | |
# 9th argument is expected to be an ISO file to install a fresh Windows from. | |
if (( $# >= 9 )); then | |
if [[ -s "/var/cache/media/$9" ]]; then | |
drives+=("-drive" "file=/var/cache/media/$9,index=2,media=cdrom,readonly") | |
drives+=("-boot" "once=d") | |
else | |
>&2 printf "Ignored, because it is no file: %s\n" "$9" | |
fi | |
fi | |
spice=() | |
spice+=("-vga" "qxl") | |
spice+=("-spice" "addr=$2,port=$3,password=$4") | |
spice+=("-chardev" "spicevmc,id=vdagent,name=vdagent") | |
spice+=("-device" "virtserialport,chardev=vdagent,name=com.redhat.spice.0") | |
exec /usr/bin/qemu -enable-kvm -nographic -rtc base=utc \ | |
-monitor unix:/run/kvm/"$7".monitor,server,nowait \ | |
-cpu host -m $8 -smp ${ncores},sockets=1 -k de -usbdevice tablet \ | |
-device virtio-serial \ | |
${spice[*]} \ | |
-net nic,model=virtio,macaddr=$5 -net tap,fd=3 3<>/dev/tap-vm \ | |
${drives[*]} \ | |
-name "$7" | |
else | |
exec /usr/bin/qemu -enable-kvm "$@" | |
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ExecStart is the important part. It shows how to run the Docker container this all is for. | |
[Unit] | |
Description=KVM with Windows using macvtap | |
Wants=network-online.target docker.service | |
Requires=docker.service | |
After=network-online.target | |
[Service] | |
Restart=on-abort | |
RestartSec=30s | |
SuccessExitStatus=2 | |
EnvironmentFile=/etc/environment | |
Environment=PATH=/opt/sbin:/opt/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin | |
Environment=VM_NAME=windows-1 | |
Environment=VM_MAC=52:54:00:12:34:56 | |
Environment=VM_TAP=macvtap0 | |
Environment=VM_PASSWORD=geheim | |
ExecStartPre=/bin/sh -c "[ -d /run/kvm ] || mkdir /run/kvm && chmod 0700 /run/kvm" | |
ExecStartPre=/bin/sh -c "/bin/ip link add link ext0 name ${VM_TAP} type macvtap && /bin/ip link set ${VM_TAP} address ${VM_MAC} up" | |
ExecStart=/bin/docker run -t --rm --privileged \ | |
--net host \ | |
--cgroup-parent machine.slice \ | |
-v /var/cache/media:/var/cache/media \ | |
-v /var/vm:/var/vm \ | |
-v /run/kvm:/run/kvm \ | |
wmark/docker-kvm \ | |
windows ${PUBLIC_IPV4} 5900 "${VM_PASSWORD}" ${VM_MAC} ${VM_TAP} "${VM_NAME}" 8192 Windows10.iso | |
ExecReload=/bin/sh -c "echo system_reset | nc -U /run/kvm/${VM_NAME}.monitor" | |
ExecStop=/bin/sh -c "echo system_powerdown | nc -U /run/kvm/${VM_NAME}.monitor || rm /run/kvm/${VM_NAME}.monitor" | |
ExecStopPost=/bin/sh -c "/bin/ip link set dev ${VM_TAP} down && /bin/ip link del ${VM_TAP}" | |
[Install] | |
WantedBy=multi-user.target |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
QEMU/KVM using macvtap, in Docker
If this is in your LAN, pick a unique MAC address (a random one will do). It’s for Windows.
Example: Windows 10
Prepare the host:
Install the systemd unit file which takes care of starting, resetting, and stopping the KVM:
And finally, start the KVM
systemctl start kvm-windows-1.service systemctl enable kvm-windows-1.service
… and point the Remote Viewer to
spice://<host ip>:5900
. That’s it!Hints for your custom ISO to install Windows
Connect to the VM, the virtio drivers for Windows—which you need to install for QEMU—will be available in the seconds virtual DVD drive.
Have the Windows installer load NetKVM first, even though it is no storage driver;
then viostor. For Windows 10 both are in subfolder
2k12R2/amd64
.Once the system is ready you can install all remaining drivers by right-clicking on the corresponding INF file.
Don't forget the guest agent!