KubeVirt

Create a Windows VM in Kubernetes using KubeVirt

Windows VM in a Kubernetes Cluster

Manas Peçenek
adessoTurkey
Published in
8 min readMay 27, 2022

--

KubeVirt provides a unified development platform where Containers and VMs can reside in a shared environment
kubevirt.io

Containers have revolutionized the tech landscape, providing unparalleled flexibility and efficiency. Despite their dominance, Virtual Machines remain essential for specific tasks. Here comes KubeVirt — a transformative solution. With KubeVirt, you’re not limited to one paradigm. It easily enables the deployment of Virtual Machines within any Kubernetes Cluster, seamlessly combining the agility of Containers with the enduring power of Virtual Machines. It’s a dynamic approach that ensures you’re equipped for the diverse demands of modern computing.

In this article, I will be demonstrating how to create and run a Windows VM inside a KinD Cluster that is running on an Ubuntu machine.

Prerequisites

You need to have an Ubuntu Machine that should have Docker, Kubectl, and Kind installed as well as Nested Virtualization enabled. If you have one already, you can skip over the “Before Starting” part.

Before Starting

In this article, I will be using Google Cloud Console to host the Ubuntu Machine. You can follow along if you do not have one already, but make sure Nested Virtualization is enabled. You can check it via the command below. It should return something other than 0

grep -cw vmx /proc/cpuinfo

Go to GCP console, activate cloud shell and run the command below to create a disk

gcloud compute disks create kubevirt-disk \--zone=us-west4-b \--image-project=ubuntu-os-cloud \--image-family=ubuntu-2004-lts \--size=200GB

Then create an image out of that disk

gcloud compute images create kubevirt-image \--source-disk kubevirt-disk \--source-disk-zone us-west4-b \--licenses "https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx"

Then the virtual machine

gcloud compute instances create kubevirt-vm \--custom-cpu=8 \--custom-memory=16GB \--zone=us-west4-b \--image kubevirt-image

Then create a firewall rule that will allow your local machine to access port 31002 of the Ubuntu machine

gcloud compute firewall-rules create allow-nodeport-31002 \— action=ALLOW \— direction=INGRESS \— network=default \— priority=10 \— rules=tcp:31002 \— source-ranges=<your-local-ip>/32

Be aware that the public IP of this VM is an Ephemeral one and will change if you stop and start the VM again.

Now ssh into the VM and install Docker

sudo su----------------------------------------------------------------apt update && apt upgrade -y && apt install -y ca-certificates curl gnupg lsb-release----------------------------------------------------------------curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg----------------------------------------------------------------echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null----------------------------------------------------------------apt update && apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

Then install kind and kubectl

curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64----------------------------------------------------------------sudo chmod +x ./kind && sudo mv kind /usr/local/bin----------------------------------------------------------------curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"----------------------------------------------------------------sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl----------------------------------------------------------------echo "alias k=kubectl" >> ~/.bashrc && bash --login

1) Create a KinD Cluster

Here we will be creating a KinD cluster and exposing 31001 and 31002 ports for further use. If you have an existing KinD cluster and want to work on it, go to section 1.b

OPTIONAL: You can install “Nginx Ingress Controller” if you want, but we will not be needing it in our case.

k apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

1. b) Configure an existing KinD Cluster

If you want to create the Windows VM on an existing KinD cluster, you just need to run the two commands below in order to expose the NodePorts. Get the container name of your KinD cluster via docker ps command. It will be something like {your-cluster-name}-control-plane

docker run -d -p 31001:1234 --net kind alpine/socat \tcp-listen:1234,fork,reuseaddr tcp-connect:{your-cluster-name}-control-plane:31001----------------------------------------------------------------    docker run -d -p 31002:1234 --net kind alpine/socat \tcp-listen:1234,fork,reuseaddr tcp-connect:{your-cluster-name}-control-plane:31002

2) Install KubeVirt on the Cluster

export VERSION=$(curl -s https://api.github.com/repos/kubevirt/kubevirt/releases | grep tag_name | grep -vE -- 'rc|alpha|beta'| sort -r | head -1 | awk -F': ' '{print $2}' | sed 's/,//' | xargs)----------------------------------------------------------------k create -f https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-operator.yaml----------------------------------------------------------------k create -f https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-cr.yaml----------------------------------------------------------------k get pods -n kubevirt # wait until you see all 6 pods----------------------------------------------------------------## root@virt-vm:/home/manas_petschenek# k pods -n kubevirtNAME                               READY   STATUS    RESTARTS   AGEvirt-api-77df5c4f87-26mgm          1/1     Running   0          103svirt-controller-749d8d99d4-nxvw2   1/1     Running   0          78svirt-controller-749d8d99d4-rmg9j   1/1     Running   0          78svirt-handler-52km7                 1/1     Running   0          78svirt-operator-564f568975-5cccj     1/1     Running   0          2m2svirt-operator-564f568975-k9c54     1/1     Running   0          2m2s

3) Install “kubectl krew” and “kubectl virt”

(set -x; cd "$(mktemp -d)" &&OS="$(uname | tr '[:upper:]' '[:lower:]')" &&ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')" &&KREW="krew-${OS}_${ARCH}" &&curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz" &&tar zxvf "${KREW}.tar.gz" &&./"${KREW}" install krew)----------------------------------------------------------------echo 'export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"' >> ~/.bashrc && bash --login----------------------------------------------------------------k krew install virt

4) Install Containerized Data Importer (CDI)

export TAG=$(curl -s -w %{redirect_url} https://github.com/kubevirt/containerized-data-importer/releases/latest) && export CDI_VERSION=$(echo ${TAG##*/})----------------------------------------------------------------kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$CDI_VERSION/cdi-operator.yaml----------------------------------------------------------------kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$CDI_VERSION/cdi-cr.yaml----------------------------------------------------------------k get cdi cdi -n cdi## root@kubevirt-vm:/home/manas_petschenek# k get cdi cdi -n cdi
NAME AGE PHASE
cdi 118m Deployed
----------------------------------------------------------------k get pods -n cdi## root@kubevirt-vm:/home/manas_petschenek# k get pods -n cdi
NAME READY STATUS RESTARTS AGE
cdi-apiserver-66998fc655-v5lb2 1/1 Running 0 118m
cdi-deployment-7b4986d49d-7n82s 1/1 Running 0 119m
cdi-operator-8496bfc49c-m4zl7 1/1 Running 0 119m
cdi-uploadproxy-5984d69cc7-tcq6j 1/1 Running 0 118m

5) Download Windows ISO

Visit https://www.microsoft.com/en-us/evalcenter/download-windows-server-2012-r2. Copy the dowload link adress and download the iso via wget command

wget -O win.iso 'link-you-just-copied'## It will look like this:wget -O win.iso 'https://go.microsoft.com/fwlink/p/?LinkID=2195443&clcid=0x409&culture=en-us&country=US'

6) Expose the “cdi-uploadproxy” Service

To upload data to the cluster, the cdi-uploadproxy service must be accessible from outside the cluster. Since the data is too much for ingress to handle, we are going to be using a NodePort service for this purpose. In a production environment consider using a LoadBalancer service instead

7) Upload the ISO file into a PVC

Run the following command where you downloaded the Windows ISO

k virt image-upload pvc iso-win10 \
--image-path=win.iso \
--access-mode=ReadWriteOnce \
--size=7G \
--uploadproxy-url=https://localhost:31001 \
--force-bind \
--insecure \
--wait-secs=60

You should see something like

root@kubevirt-vm:/home/manas_petschenek# k virt image-upload \
> --image-path=win.iso \
> --pvc-name=iso-win10 \
> --access-mode=ReadWriteOnce \
> --pvc-size=7G \
> --uploadproxy-url=https://localhost:31001 \
> --insecure \
> --force-bind \
> --wait-secs=60
PVC default/iso-win10 not found
PersistentVolumeClaim default/iso-win10 created
Waiting for PVC iso-win10 upload pod to be ready...
Pod now ready
Uploading data to https://localhost:31001
4.23 GiB / 4.23 GiB [====================================================================================================================================================================================] 100.00% 1m18sUploading data completed successfully, waiting for processing to complete, you can hit ctrl-c without interrupting the progress
Processing completed successfully
Uploading win.iso completed successfully

8) Create the Windows VM

First, pull the Docker image

docker pull quay.io/kubevirt/virtio-container-disk

Then create the VirtualMachine and PersistentVolumeClaim

Then start the VirtualMachine to create a VirtualMachineInstance

k virt start iso-win10

Check if they are running

root@kubevirt-vm:/home/manas_petschenek# k get vm,vmi
NAME AGE STATUS READY
virtualmachine.kubevirt.io/iso-win10 23s Running True
NAME AGE PHASE IP NODENAME READY
virtualmachineinstance.kubevirt.io/iso-win10 11s Running 10.244.0.26 kubevirt-control-plane True

9) Connect to Windows VM

There are two different methods

  • If you have a graphical interface, run:
k virt vnc iso-win10
  • If you do not, then follow the steps below:
wget -O virtvnc.yaml https://github.com/wavezhang/virtVNC/raw/master/k8s/virtvnc.yaml----------------------------------------------------------------sed -i 's/targetPort\: 8001/targetPort\: 8001\n    nodePort\: 31002/' virtvnc.yaml----------------------------------------------------------------k apply -f virtvnc.yaml

Then access the console via http://{public-ip-of-ubuntu-vm}:31002

Click on the VNC button

10) Install Drivers

Follow the screenshots below

Select the language
Select the second option
Accept the license terms
Select the Custom installation path
Click on “Load driver”
Click on “Browse”
Expand the “virtio drive”
Go to the bottom and expand “viostor” and then “2k12R2”. Select “amd64” and click Ok
Click on Next
Click on Next
Wait until it finishes
If you see this page, Do not press any button just wait
Set admin password
Click on the “Send CtrlAltDel” button
Login with the admin password
Close this page
Windows is ready

RESOURCES:

--

--