Cookies & Containers

Or how I learned to love the cloud.

Running a Windows VM on KubeVirt on K3s

Running a Windows VM on KubeVirt on K3s

2021-03-17-quick-openstack.html ยท 2020-11-03-windows-kubevirt-k3s.html ยท 2020-08-31-simple-k8s-operator-java.html

November 3, 2020 โ€” To get the obvious "WTF, why would you wanna do this?" out of the way: We had to. For reasons.


In cases where you need a Windows VM, but don't want to leave your Kubernetes platform, KubeVirt is your friend.

Be warned that this is going to be a heavy workload, so go for something with a lot of CPU, memory and disk space.


Let's start with installing K3s and KubeVirt on an Ubuntu 20.10 instance.

apt update && apt -y upgrade curl -sfL | sh -

Check if the install succeeded, e.g. by creating a deployment and seeing if pods are running.

export VERSION=$(curl -s | grep tag_name | grep -v -- '-rc' | head -1 | awk -F': ' '{print $2}' | sed 's/,//' | xargs) echo $VERSION kubectl create -f${VERSION}/kubevirt-operator.yaml kubectl create -f${VERSION}/kubevirt-cr.yaml

Check if the install succeeded with

kubectl get -n kubevirt -o=jsonpath="{.status.phase}" kubectl get all -n kubevirt apt-get install git ( set -x; cd "$(mktemp -d)" && curl -fsSLO "" && tar zxvf krew.tar.gz && KREW=./krew-"$(uname | tr '[:upper:]' '[:lower:]')_$(uname -m | sed -e 's/x86_64/amd64/' -e 's/arm.*$/arm/')" && "$KREW" install krew; ) export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH" kubectl krew install virt export VERSION=$(curl -s | grep -o "v[0-9]\.[0-9]*\.[0-9]*") kubectl create -f$VERSION/cdi-operator.yaml kubectl create -f$VERSION/cdi-cr.yaml

Add Windows

# Get the CDI upload proxy service IP: kubectl get svc -n cdi # Upload kubectl virt image-upload --image-path </path/to/iso> \ --pvc-name iso-win2k19 --access-mode ReadWriteOnce \ --pvc-size 10G --uploadproxy-url <upload-proxy service:443> \ --insecure --wait-secs=240

More info on the upload command can be found here. I had to change the access mode of the PVC to ReadWriteOnce, as the default local-path storageclass of K3s doesn't support any other modes at this point. Since we're only using one node this doesn't make a difference, but will be an issue for larger K3s clusters.

ctr image pull kubevirt/virtio-container-disk

Create the VM

--- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: winhd spec: accessModes: - ReadWriteOnce resources: requests: storage: 15Gi storageClassName: hostpath --- apiVersion: kind: VirtualMachine metadata: name: win2k19-iso spec: running: false template: metadata: labels: win2k19-iso spec: domain: cpu: cores: 4 devices: disks: - bootOrder: 1 cdrom: bus: sata name: cdromiso - disk: bus: virtio name: harddrive - cdrom: bus: sata name: virtiocontainerdisk machine: type: q35 resources: requests: memory: 8G volumes: - name: cdromiso persistentVolumeClaim: claimName: iso-win2k19 - name: harddrive persistentVolumeClaim: claimName: winhd - containerDisk: image: kubevirt/virtio-container-disk name: virtiocontainerdisk kubectl apply -f win2k19.yaml kubectl virt start win2k19-iso # If you're running this on a remote machine, use X-forwarding and # apt-get install virt-viewer kubectl virt vnc win2k19-iso


If your drivers didn't install successfully and your Network connection isn't working because of that, you need to go into the Device Manager on Windows, click on the OtherDevices and update the driver from your local drive where virtio has stored it's files. In my case it was E:\.

apiVersion: v1 kind: Service metadata: name: windows-nodeport spec: externalTrafficPolicy: Cluster ports: - name: nodeport nodePort: 30000 port: 27017 protocol: TCP targetPort: 3389 selector: win2k19-iso type: NodePort

Which then allows you to access your Windows VM via RDP on <NodeIp>:30000.

Further reading

  1. Adapted to K3s from

View source

Gmail icon Twitter icon GitHub icon
Built with Scroll v31.4.1