KubeVirt
Create a Windows VM in Kubernetes using KubeVirt
Windows VM in a Kubernetes Cluster
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:310014.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 TrueNAME 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
10) Install Drivers
Follow the screenshots below