Hace unas semanas atrás estuve jugando con un proyecto de la CNCF llamado KubeVirt, que te permite crear máquinas virtuales, Linux y Windows, dentro de un clúster de Kubernetes como si de otro recurso más se tratara. Me pareció súper interesante, sobre todo pensando en cargas que son difíciles de contenerizar o incluso para los entornos de pruebas donde necesitas instalar/ejecutar herramientas en estas máquinas contra otros pods del clúster. En este artículo quiero compartir contigo cómo usarlo.
Crear un clúster de Kubernetes
Lo primero que necesitas es un clúster de Kubernetes donde instalar Kubevirt. Al principio intenté usar kind pero tuve algunos problemas con algunas de las máquinas usando MacOS como host, por lo que finalmente monté un clúster en Azure con un pool de máquinas que tuvieran habilitado VT-x:
# Variables
RESOURCE_GROUP="kubevirt"
LOCATION="northeurope"
AKS_NAME="kubevirt-on-aks"
# Creare resource group
az group create -n $RESOURCE_GROUP -l $LOCATION
# Create cluster
az aks create \
--resource-group $RESOURCE_GROUP \
--network-policy calico \
--network-plugin kubenet \
--node-vm-size Standard_B4ms \
--node-count 1 \
--name $AKS_NAME
# Add nodepool with Intel virtualization extensions (VT-x) enabled
az aks nodepool add \
--resource-group $RESOURCE_GROUP \
--cluster-name $AKS_NAME \
--name nested \
--node-vm-size Standard_D4s_v3 \
--labels nested=true \
--node-count 1
# Get credentials
az aks get-credentials -g $RESOURCE_GROUP -n $AKS_NAME
# Check access
kubectl get nodes
Instalar Kubevirt en tu clúster
Una vez que ya tienes un clúster listo para tus pruebas lo siguiente es instalar Kubevirt:
# Install kubevirt
STABLE_VERSION="v0.58.0"
# export VERSION=$(curl -s https://api.github.com/repos/kubevirt/kubevirt/releases | grep tag_name | grep -v -- '-rc' | sort -r | head -1 | awk -F': ' '{print $2}' | sed 's/,//' | xargs)
# echo $VERSION
kubectl create -f "https://github.com/kubevirt/kubevirt/releases/download/${STABLE_VERSION}/kubevirt-operator.yaml"
# Again use kubectl to deploy the KubeVirt custom resource definitions
kubectl create -f "https://github.com/kubevirt/kubevirt/releases/download/${STABLE_VERSION}/kubevirt-cr.yaml"
# Check the components
watch kubectl get all -n kubevirt
Para evitar comportamientos anómalos he utilizado la variable STABLE_VERSION con el valor de la última versión estable en el momento de escribir este artículo. Si utilizas el código comentado justo debajo es posible que en ese momento exista alguna versión alfa que no se comporte como esperas y de error al crear las máquinas virtuales.
Si todo ha ido bien, deberías de tener todos los componentes en estado Running.
Instalar Containerized Data Importer
La forma más sencilla de crear máquinas virtuales con Kubevirt en Kubernetes es usando la funcionalidad Containerized Data Importer o CDI. Esto lo que te permite es, como su nombre indica, importar en este caso la ISO que hace falta para instalar la máquina virtual para el sistema operativo que necesites. Para desplegar esta herramienta en el clúster puedes hacerlo con los siguientes comandos:
# Install the CDI
export VERSION=$(basename $(curl -s -w %{redirect_url} https://github.com/kubevirt/containerized-data-importer/releases/latest))
kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-operator.yaml
kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-cr.yaml
# Check the status of the cdi CustomResource (CR) created in the previous step
kubectl get cdi cdi -n cdi
kubectl get pods -n cdi
Ahora, después de tanta configuración, vamos a ver un ejemplo con un Windows 11.
Crear una máquina virtual con Windows 11 con Kubevirt
Uno de los ejemplos que monté es una máquina virtual con Windows 11. Para ello, lo primero que hago es usar CDI de la siguiente forma:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: "windows11"
labels:
app: containerized-data-importer
annotations:
cdi.kubevirt.io/storage.import.endpoint: "https://software.download.prss.microsoft.com/dbazure/Win11_22H2_English_x64v1.iso?t=ee63b31a-215b-49ba-b276-ddbb12882558&e=1672394475&h=297c582f25a7f02fabe15661d97edaf54490ec11c33416ad81dd11fcdeb15f6b"
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 6Gi
Como ves, necesito crear un objeto del tipo PersistentVolumeClaim que tiene una anotación especial, cdi.kubevirt.io/storage.import.endpoint, a la que le paso la URL desde la que puede descargar la ISO, en este caso de Windows 11.
Nota: la URL que aparece en este código de ejemplo no te funcionará. En el caso de Windows 11 debes acceder a esta URL, hacer clic en el apartado Download Windows Disk 10 Image (ISO) seleccionar Windows 11 (multi-edition ISO), hacer clic en Download, seleccionar el idioma y hacer clic en Confirm. Una vez hecho esto te aparecerá un nuevo botón llamado 64-bit Download que tendrá una validez de 24 horas para descargarte la ISO.
Una vez copiada la URL correcta, si creas este recurso y revisas los logs:
# Create the pvc
# https://www.microsoft.com/software-download/windows11
k create -f windows/windows11-pvc.yml
# Check the progress
kubectl logs -f importer-windows11
Comprobarás al cabo de unos instantes que en ellos va apareciendo el porcentaje de descarga de la ISO:
Una vez que este proceso haya finalizado satisfactoriamente puedes definir un YAML como el siguiente para una máquina virtual con Windows 11:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: disk-windows11
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 35Gi
---
apiVersion: kubevirt.io/v1
kind: VirtualMachineInstance
metadata:
labels:
special: vmi-windows
name: vmi-windows
spec:
nodeSelector: #nodeSelector matches nodes where performance key has high as value.
nested: "true"
domain:
clock:
timer:
hpet:
present: false
hyperv: {}
pit:
tickPolicy: delay
rtc:
tickPolicy: catchup
utc: {}
cpu:
cores: 2
devices:
disks:
- disk:
bus: sata
name: pvcdisk
- cdrom:
bus: sata
name: winiso
interfaces:
- masquerade: {}
model: e1000
name: default
tpm: {}
features:
acpi: {}
apic: {}
hyperv:
relaxed: {}
spinlocks:
spinlocks: 8191
vapic: {}
smm: {}
firmware:
bootloader:
efi:
secureBoot: true
uuid: 5d307ca9-b3ef-428c-8861-06e72d69f223
resources:
requests:
memory: 4Gi
networks:
- name: default
pod: {}
terminationGracePeriodSeconds: 0
volumes:
- name: pvcdisk
persistentVolumeClaim:
claimName: disk-windows11
- name: winiso
persistentVolumeClaim:
claimName: windows11
Esta configuración la he copiado de un artículo del blog de KubeVirt, donde explica muchos de los parámetros que son necesarios para este sistema operativo. Si creas el recurso haciendo uso de esta configuración:
# Create the virtual machine using that pvc
kubectl create -f windows/win11vm.yaml
Puedes comprobar el proceso de alocación y de ejecución de la máquina virtual con este otro comando:
# Check virtual machine instance status
watch kubectl get vmi
Ahora que ya tienes la máquina arrancada el siguiente paso es conectarse a ella ¿pero cómo? puedes usar VNC para la conexión con esta:
# Connect VM via VNC
k virt vnc vmi-windows
Cuando arrancas la máquina debes hacer el mismo proceso que si una máquina física o virtual «normal» se tratase. Una vez tengas la misma con su instalación hecha te recomiendo que instales la última versión de los drivers de VirtIO que puedes encontrar aquí.
Probar la comunicación con otros pods dentro del clúster
Una de las cosas que me interesaba comprobar era la posibilidad de comunicarme con otras aplicaciones que tuviera desplegadas en el clúster. Es por ello que probé con mi Tour of Heroes:
# Apply tour of heroes manifests
kubectl apply -f tour-of-heroes --recursive
Una vez hecho esto, dentro de mi máquina virtual con Windows 11 intenté acceder a la API y al frontal web usando el FQDN y el resultado fue satisfactorio:
También puedo acceder a través del navegador web usando el FQDN de mis servicios:
¡Saludos!