Kubernetes KubeVirt

Info
Don’t have any use case for this yet, all of my running services is in microservice/container. Will update post if I have more time to explore this, or maybe if used in work which I don’t have currently - HIRE ME PO!
Architecture Diagram

Kubernetes is designed to run containerized workloads, but many real-world systems still rely on virtual machines (VMs).

KubeVirt extends Kubernetes by allowing you to run and manage Virtual Machines alongside containers using the same Kubernetes API.

Table of Contents

What Is KubeVirt?

Kubevirt essentially brings virtualization into Kubernetes.

  • Running Virtual Machines (VMs) inside a Kubernetes cluster
  • Managing VMs using kubectl and Kubernetes resources
  • Combining VM-based and container-based workloads in one platform

Architecture

KubeVirt integrates into Kubernetes using Custom Resource Definitions (CRDs) and controllers.

Main components:

KubeVirt Operator

  • Installs and manages KubeVirt components

virt-controller

  • Manages VM lifecycle (create, delete, migrate)

virt-handler

  • Runs on each node (DaemonSet)
  • Interfaces with the hypervisor (KVM)

virt-launcher

  • Runs inside a Pod
  • Hosts the actual VM

Deploy

KubeVirt

This process can be found here.

Deploy kubevirt operator.

1export VERSION=$(curl -s https://storage.googleapis.com/kubevirt-prow/release/kubevirt/kubevirt/stable.txt)
2echo $VERSION
3kubectl create -f "https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-operator.yaml"

Deploy the KubeVirt custom resource definitions.

1kubectl create -f "https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-cr.yaml"

Check the componets.

 1kubectl get all -n kubevirt
 2Warning: kubevirt.io/v1 VirtualMachineInstancePresets is now deprecated and will be removed in v2.
 3NAME                                  READY   STATUS    RESTARTS       AGE
 4pod/virt-api-7cc64fb85f-ns2qt         1/1     Running   1 (149m ago)   171m
 5pod/virt-api-7cc64fb85f-z6hfh         1/1     Running   1 (149m ago)   171m
 6pod/virt-controller-964c8dbcb-96tzj   1/1     Running   3 (46m ago)    170m
 7pod/virt-controller-964c8dbcb-m84wz   1/1     Running   3 (81m ago)    170m
 8pod/virt-handler-4ln4g                1/1     Running   0              170m
 9pod/virt-handler-fwsl2                1/1     Running   0              170m
10pod/virt-handler-pn6cn                1/1     Running   0              170m
11pod/virt-operator-68f69776bc-6z4p5    1/1     Running   3 (80m ago)    172m
12pod/virt-operator-68f69776bc-bccjh    1/1     Running   5 (44m ago)    172m
13
14NAME                                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
15service/kubevirt-operator-webhook     ClusterIP   10.43.63.183    <none>        443/TCP   171m
16service/kubevirt-prometheus-metrics   ClusterIP   None            <none>        443/TCP   171m
17service/virt-api                      ClusterIP   10.43.47.236    <none>        443/TCP   171m
18service/virt-exportproxy              ClusterIP   10.43.200.237   <none>        443/TCP   171m
19
20NAME                          DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
21daemonset.apps/virt-handler   3         3         3       3            3           kubernetes.io/os=linux   170m
22
23NAME                              READY   UP-TO-DATE   AVAILABLE   AGE
24deployment.apps/virt-api          2/2     2            2           171m
25deployment.apps/virt-controller   2/2     2            2           170m
26deployment.apps/virt-operator     2/2     2            2           172m
27
28NAME                                        DESIRED   CURRENT   READY   AGE
29replicaset.apps/virt-api-7cc64fb85f         2         2         2       171m
30replicaset.apps/virt-controller-964c8dbcb   2         2         2       170m
31replicaset.apps/virt-operator-68f69776bc    2         2         2       172m
32
33NAME                            AGE    PHASE
34kubevirt.kubevirt.io/kubevirt   172m   Deployed

Virtctl

KubeVirt provides an additional binary called virtctl for quick access to the serial and graphical ports of a VM and also handle start/stop operations.

1VERSION=$(kubectl get kubevirt.kubevirt.io/kubevirt -n kubevirt -o=jsonpath="{.status.observedKubeVirtVersion}")
2ARCH=$(uname -s | tr A-Z a-z)-$(uname -m | sed 's/x86_64/amd64/') || windows-amd64.exe
3echo ${ARCH}
4curl -L -o virtctl https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/virtctl-${VERSION}-${ARCH}
5sudo install -m 0755 virtctl /usr/local/bin

Storage

KubeVirt can use PVC and PV but we need additional plugin to install to make it work.

1export VERSION=$(curl -s https://api.github.com/repos/kubevirt/containerized-data-importer/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
2kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-operator.yaml
3kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-cr.yaml

Verify.

1kubectl get cdi -n cdi
2NAME   AGE     PHASE
3cdi    4h22m   Deployed

Network

KubeVirt uses Kubernetes networking (CNI), commonly used is Multus and Bridge Networking. But for the demo we’ll still be using Load Balancer.

Example

Unlike traditional VMs, KubeVirt runs VMs inside Pods. Let’s demonstrate a practical example here.

Let’s run a ubuntu vm inside kubernetes. Like in libvirt we usually first create storage for the VM, it’s the same with KubeVirt.

Data Volume

ubuntu-datavolume.yml

 1apiVersion: cdi.kubevirt.io/v1beta1
 2kind: DataVolume
 3metadata:
 4  name: "ubuntu-datavolume"
 5spec:
 6  source:
 7    http:
 8      url: "https://cloud-images.ubuntu.com/noble/20260307/noble-server-cloudimg-amd64.img"
 9  pvc:
10    accessModes:
11    - ReadWriteMany
12    resources:
13      requests:
14        storage: "50Gi"
1kubectl create -f ubuntu-datavolume.yml -n demo

This will create a 50GB Data Volume, that the vm can consume and will boot our Ubuntu VM.

Verify.

1kubectl get datavolume -n demo
2NAME                PHASE                  PROGRESS   RESTARTS   AGE
3ubuntu-datavolume   WaitForFirstConsumer   N/A                   84s

Virtual Machine

If you are here then you are familiar with cloud-init, define cpu, ram and also user parameter. Check some of my older post regarding cloud-init.

ubuntu-vm.yml

 1apiVersion: kubevirt.io/v1
 2kind: VirtualMachine
 3metadata:
 4  labels:
 5    kubevirt.io/os: linux
 6  name: ubuntu 
 7spec:
 8  runStrategy: Always
 9  template:
10    metadata:
11      creationTimestamp: null
12      labels:
13        kubevirt.io/domain: ubuntu
14    spec:
15      domain:
16        cpu:
17          cores: 1
18        devices:
19          disks:
20          - disk:
21              bus: virtio
22            name: disk0
23          - cdrom:
24              bus: sata
25              readonly: true
26            name: cloudinitdisk
27        resources:
28          requests:
29            memory: 500M
30      volumes:
31      - name: disk0
32        persistentVolumeClaim:
33          claimName: ubuntu-datavolume
34      - cloudInitNoCloud:
35          userData: |
36            system_info:
37              default_user:
38                name: mcbtaguiad
39                home: /home/mcbtaguiad
40            password: AveryStrongPassword123456789!
41            chpasswd: { expire: False }
42            hostname: tags-k8s
43            ssh_pwauth: True
44            disable_root: false
45            ssh_authorized_keys:
46            - YOUR_PUBLIC_KEY
47            name: cloudinitdisk
1kubectl create -f ubuntu-vm.yml -n demo 

Verify.

 1kubectl get all -n demo
 2Warning: kubevirt.io/v1 VirtualMachineInstancePresets is now deprecated and will be removed in v2.
 3NAME                             READY   STATUS    RESTARTS   AGE
 4pod/importer-ubuntu-datavolume   1/1     Running   0          33s
 5
 6NAME                                           PHASE              PROGRESS   RESTARTS   AGE
 7datavolume.cdi.kubevirt.io/ubuntu-datavolume   ImportInProgress   82.46%                8m22s
 8
 9NAME                                        AGE   PHASE     IP    NODENAME   READY
10virtualmachineinstance.kubevirt.io/ubuntu   54s   Pending                    False
11
12NAME                                AGE   STATUS     READY
13virtualmachine.kubevirt.io/ubuntu   54s   Starting   False

We can see here the the importer is running, if you check the progress on datavolume it is progressing to 100%. Also the VM Instance is still pending, we can verify that the VM is running when the import is complete.

 1kubectl get all -n demo
 2Warning: kubevirt.io/v1 VirtualMachineInstancePresets is now deprecated and will be removed in v2.
 3NAME                             READY   STATUS    RESTARTS   AGE
 4pod/virt-launcher-ubuntu-2rwqz   2/2     Running   0          2m30s
 5
 6NAME                                           PHASE       PROGRESS   RESTARTS   AGE
 7datavolume.cdi.kubevirt.io/ubuntu-datavolume   Succeeded   100.0%                11m
 8
 9NAME                                        AGE     PHASE     IP              NODENAME   READY
10virtualmachineinstance.kubevirt.io/ubuntu   3m55s   Running   172.16.235.45   master03   True
11
12NAME                                AGE     STATUS    READY
13virtualmachine.kubevirt.io/ubuntu   3m55s   Running   True

The VM is now running.

Console

To connect to the VM we use virtctl, quite similar to virsh. Same with virsh use the domain we set ubuntu.

1virtctl console ubuntu -n demo
2Successfully connected to ubuntu console. Press Ctrl+] or Ctrl+5 to exit console.
3
4tags-k8s login: mcbtaguiad

Explore other options with virtctl --help.

Service

Like pods/container in Kubernetes we can also attach service/network to the Virtual Machine instance.

Make sure to put the correct selector, it’s the label we set when creating the Virtual Machine kubevirt.io/domain: ubuntu.

ubuntu-svc.yml

 1apiVersion: v1
 2kind: Service
 3metadata:
 4  name: ubuntu-svc
 5  annotations:
 6    metallb.universe.tf/address-pool: metallb-ip-pool
 7spec:
 8  type: LoadBalancer
 9  selector:
10    kubevirt.io/domain: ubuntu
11  ports:
12    - port: 22
13      targetPort: 22
1kubectl create -f ubuntu-svc.yml -n demo

Verify.

1kubectl get svc -n demo
2NAME         TYPE           CLUSTER-IP     EXTERNAL-IP       PORT(S)        AGE
3ubuntu-svc   LoadBalancer   10.43.73.139   192.168.254.220   22:30099/TCP   4s

SSH to the VM using the external IP.

 1ssh 192.168.254.220         
 2Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
 3Warning: Permanently added '192.168.254.220' (ED25519) to the list of known hosts.
 4Welcome to Ubuntu 24.04.4 LTS (GNU/Linux 6.8.0-101-generic x86_64)
 5
 6 * Documentation:  https://help.ubuntu.com
 7 * Management:     https://landscape.canonical.com
 8 * Support:        https://ubuntu.com/pro
 9
10 System information as of Thu Mar 19 10:40:49 UTC 2026
11
12  System load:  0.08               Processes:               112
13  Usage of /:   13.8% of 11.78GB   Users logged in:         0
14  Memory usage: 19%                IPv4 address for enp1s0: 172.16.235.45
15  Swap usage:   0%
16
17Expanded Security Maintenance for Applications is not enabled.
18
190 updates can be applied immediately.
20
21Enable ESM Apps to receive additional future security updates.
22See https://ubuntu.com/esm or run: sudo pro status
23
24
25The list of available updates is more than a week old.
26To check for new updates run: sudo apt update
27
28
29The programs included with the Ubuntu system are free software;
30the exact distribution terms for each program are described in the
31individual files in /usr/share/doc/*/copyright.
32
33Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
34applicable law.
35
36To run a command as administrator (user "root"), use "sudo <command>".
37See "man sudo_root" for details.
38
39mcbtaguiad@tags-k8s:~$