Kubernetes: Crear máquinas virtuales en un clúster con KubeVirt
Cuando hablamos de Kubernetes, tendemos a pensar que sólo podemos ejecutar contenedores y no máquinas virtuales. Hoy veremos que disponemos de herramientas que permiten la implementación de máquinas virtuales sobre un cluster de Kubernetes. Esto lo podremos realizar bajo la implementación de KubeVirt.
KubeVirt es un proyecto de código abierto de Cloud Native Computing Foundation (CNCF) que ofrece una API de virtualización y un entorno de ejecución para definir y administrar máquinas virtuales dentro un clúster de Kubernetes.
Vamos a poder montar máquinas tanto Linux como Windows, lo que abre un abanico de posibilidades bastante grande. Lo veo ideal para laboratorios, ya que nos permite trabajar con aplicaciones que no pueden estar en un contenedor o realizar pruebas con aplicaciones contenerizadas (en otros Pods) sin volvernos muy locos (imaginar las posibilidades en un laboratorio de ciberseguridad, por ejemplo, donde quieres simular ataques o bastionados a este tipo de infraestructuras)
Os dejo algo más de información:
DOCUMENTACION OFICIAL: https://kubevirt.io/
OTROS ENLACES DE INTERES:
- https://learn.microsoft.com/es-es/azure/iot-edge/how-to-install-iot-edge-kubernetes?view=iotedge-1.4
- https://github.com/CRASH-Tech/proxmox-operator
Instalar cluster 3 nodos Kubernetes en LXC Debian 12 Proxmox con Terraform
Os dejo la entrada donde lo explico:
Instalar cluster 3 nodos Kubernetes en LXC Debian 12 Proxmox con Terraform
Si usáis k3s os puede servir también esta entrada:
Kubernetes: [INFO] systemd: Starting k3s-agent not finish
Instalar KubeVirt en cluster Kubernetes
Agrego unas líneas al script para poder instalar KubeVirt en el cluster:
- DOCUMENTACION OFICIAL: https://kubevirt.io/user-guide/operations/installation/
- VERSIONES : https://kubevirt.io/blogs/releases.html
Creamos el script y le damos permisos de ejecución:
1 2 |
root@KUBERNETES00:~# nano kubevirt.sh root@KUBERNETES00:~# chmod +x kubevirt.sh |
Con el siguiente contenido:
1 2 3 4 5 6 7 8 9 |
# VARIABLE PARA INSTALAR ULTIMA VERSION ESTABLE kubevirt STABLE_VERSION="v1.1.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" # COMPROBAR COMPONENTES watch kubectl get all -n kubevirt |
Ejecutamos en el clúster para que se generen todos los componentes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
root@KUBERNETES00:~# ./kubevirt.sh namespace/kubevirt created customresourcedefinition.apiextensions.k8s.io/kubevirts.kubevirt.io created priorityclass.scheduling.k8s.io/kubevirt-cluster-critical created clusterrole.rbac.authorization.k8s.io/kubevirt.io:operator created serviceaccount/kubevirt-operator created role.rbac.authorization.k8s.io/kubevirt-operator created rolebinding.rbac.authorization.k8s.io/kubevirt-operator-rolebinding created clusterrole.rbac.authorization.k8s.io/kubevirt-operator created clusterrolebinding.rbac.authorization.k8s.io/kubevirt-operator created deployment.apps/virt-operator created kubevirt.kubevirt.io/kubevirt created Cada 2,0s: kubectl get all -n kubevirt KUBERNETES00: Tue Feb 27 18:28:07 2024 Warning: kubevirt.io/v1 VirtualMachineInstancePresets is now deprecated and will be removed in v2. NAME READY STATUS RESTARTS AGE pod/virt-operator-69985b747d-qcpmp 1/1 Running 0 5m10s pod/virt-operator-69985b747d-nm4ns 1/1 Running 0 5m10s pod/virt-api-688cc569d4-9zwdr 1/1 Running 0 4m28s pod/virt-api-688cc569d4-7hpxm 1/1 Running 0 4m28s pod/virt-controller-58f78787f5-9rc75 1/1 Running 0 3m52s pod/virt-controller-58f78787f5-4s5lt 1/1 Running 0 3m53s pod/virt-handler-9xb9z 1/1 Running 0 3m52s pod/virt-handler-dn9mt 1/1 Running 0 3m52s pod/virt-handler-pjcjp 1/1 Running 0 3m52s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubevirt-prometheus-metrics ClusterIP None <none> 443/TCP 4m31s service/virt-api ClusterIP 10.43.163.152 <none> 443/TCP 4m31s service/kubevirt-operator-webhook ClusterIP 10.43.232.84 <none> 443/TCP 4m31s service/virt-exportproxy ClusterIP 10.43.18.237 <none> 443/TCP 4m31s NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE daemonset.apps/virt-handler 3 3 3 3 3 kubernetes.io/os=linux 3m5 3s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/virt-operator 2/2 2 2 5m10s deployment.apps/virt-api 2/2 2 2 4m28s deployment.apps/virt-controller 2/2 2 2 3m53s NAME DESIRED CURRENT READY AGE replicaset.apps/virt-operator-69985b747d 2 2 2 5m10s replicaset.apps/virt-api-688cc569d4 2 2 2 4m28s replicaset.apps/virt-controller-58f78787f5 2 2 2 3m53s NAME AGE PHASE kubevirt.kubevirt.io/kubevirt 5m8s Deployed |
Podéis revisar los Pods generados:
1 2 3 4 5 6 7 8 9 10 11 |
root@KUBERNETES00:~# kubectl get pods -n kubevirt NAME READY STATUS RESTARTS AGE virt-operator-69985b747d-qcpmp 1/1 Running 0 10m virt-operator-69985b747d-nm4ns 1/1 Running 0 10m virt-api-688cc569d4-9zwdr 1/1 Running 0 10m virt-api-688cc569d4-7hpxm 1/1 Running 0 10m virt-controller-58f78787f5-9rc75 1/1 Running 0 9m39s virt-controller-58f78787f5-4s5lt 1/1 Running 0 9m40s virt-handler-9xb9z 1/1 Running 0 9m39s virt-handler-dn9mt 1/1 Running 0 9m39s virt-handler-pjcjp 1/1 Running 0 9m39s |
Instalar CDI (Containerized Data Importer) Kubevirt
DOCUMENTACION OFICIAL: https://kubevirt.io/user-guide/operations/containerized_data_importer/
Ahora para implementar máquinas virtuales a través de kubevirt, usaremos CDI. Que nos permite utilizar volúmenes persistentes como discos para máquinas virtuales, para ello usa virtctl e imágenes de sistemas operativos con extensión RAW y QCOW2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# INSTALAMOS CDI root@KUBERNETES00:~# export VERSION=$(basename $(curl -s -w %{redirect_url} https://github.com/kubevirt/containerized-data-importer/releases/latest)) root@KUBERNETES00:~# kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-operator.yaml namespace/cdi created customresourcedefinition.apiextensions.k8s.io/cdis.cdi.kubevirt.io created clusterrole.rbac.authorization.k8s.io/cdi-operator-cluster created clusterrolebinding.rbac.authorization.k8s.io/cdi-operator created serviceaccount/cdi-operator created role.rbac.authorization.k8s.io/cdi-operator created rolebinding.rbac.authorization.k8s.io/cdi-operator created deployment.apps/cdi-operator created root@KUBERNETES00:~# kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-cr.yaml cdi.cdi.kubevirt.io/cdi created # COMPROBAMOS EL ESTADO root@KUBERNETES00:~# kubectl get cdi cdi -n cdi NAME AGE PHASE cdi 8s Deploying root@KUBERNETES00:~# kubectl get pods -n cdi NAME READY STATUS RESTARTS AGE cdi-operator-75d5789946-p8f4l 1/1 Running 0 97s cdi-apiserver-78d5585c5d-kbz7h 1/1 Running 0 84s cdi-uploadproxy-5c4d65444d-tj2tm 1/1 Running 0 84s cdi-deployment-74b786dcc6-tljk5 1/1 Running 0 84s |
Preparar un PVC para Importar una Imagen de Disco
Para usar CDI para importar una imagen de disco, necesitas crear un PersistentVolumeClaim (PVC) que especifique la URL de la imagen a descargar. Aquí te muestro un ejemplo de cómo crear un PVC que importará una imagen de disco. Usaré “CirOS” que es una distribución a la mínima expresión:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: vm-disk-import labels: app: containerized-data-importer annotations: cdi.kubevirt.io/storage.import.endpoint: "http://download.cirros-cloud.net/0.5.2/cirros-0.5.2-x86_64-disk.img" spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: "<your-storage-class>" |
- Nota: Debes reemplazar your-storage-class con el nombre de tu clase de almacenamiento específica. Además, asegúrate de que la URL proporcionada es accesible desde tu clúster. Puedes usar “nfs”, “local” o “ceph”, por ejemplo. Como esto es un lab, os muestro como lo monto en local, pero lo normal es que se haga con nfs u otro almacenamiento compartido por todos los nodos:
Configurar una StorageClass para Almacenamiento Local
Generaré el siguiente fichero YAML (“local-storage-class.yaml”) con el siguiente valor:
1 2 3 4 5 6 |
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: local-storage provisioner: kubernetes.io/no-provisioner volumeBindingMode: WaitForFirstConsumer |
Lo lanzamos:
1 |
kubectl apply -f local-storage-class.yaml |
Si todo ha ido bien veremos esto:
1 2 |
root@KUBERNETES00:~# kubectl apply -f local-storage-class.yaml storageclass.storage.k8s.io/local-storage created |
Crear un PersistentVolume
Antes de que puedas utilizar el PVC con almacenamiento local, necesitas crear un PersistentVolume que corresponda a un disco o partición específica en tus nodos. Os dejo un ejemplo:
Genero las carpetas donde dejaremos el disco:
1 |
root@KUBERNETES00:~# mkdir /mnt/disks/vm-disk1 -p |
Ahora generamos el fichero “local-pv.yaml“:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
apiVersion: v1 kind: PersistentVolume metadata: name: local-pv spec: capacity: storage: 10Gi volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: local-storage local: path: /mnt/disks/vm-disk1 # Asegúrate de que este camino exista y tenga los permisos adecuados nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - KUBERNETES00 # Sustituye esto por el nombre del nodo donde está el disco |
Aplicamos:
1 2 |
root@KUBERNETES00:~# kubectl apply -f local-pv.yaml persistentvolume/local-pv created |
Crear un PVC para Importar una Imagen de Disco
Ahora que tienes el PersistentVolume y la StorageClass, puedes definir el PVC que usará CDI para importar una imagen de disco. Aquí os dejo un ejemplo que especifica la URL de la imagen a descargar:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: vm-disk-import annotations: cdi.kubevirt.io/storage.import.endpoint: "http://download.cirros-cloud.net/0.5.2/cirros-0.5.2-x86_64-disk.img" spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: local-storage |
Guardamos como “vm-disk-import-pvc.yaml” y ejecutamos:
1 2 |
root@KUBERNETES00:~# kubectl apply -f vm-disk-import-pvc.yaml persistentvolumeclaim/vm-disk-import created |
Y verificamos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
root@KUBERNETES00:~# kubectl describe pvc vm-disk-import Name: vm-disk-import Namespace: default StorageClass: local-storage Status: Pending Volume: Labels: <none> Annotations: cdi.kubevirt.io/storage.import.endpoint: http://download.cirros-cloud.net/0.5.2/cirros-0.5.2-x86_64-disk.img Finalizers: [kubernetes.io/pvc-protection] Capacity: Access Modes: VolumeMode: Filesystem Used By: <none> Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal WaitForFirstConsumer 11s (x3 over 36s) persistentvolume-controller waiting for first consumer to be created before binding |
Utilizar el PVC en una Máquina Virtual con KubeVirt
Una vez que la imagen ha sido importada correctamente y el PVC está listo, puedes usar este PVC como volumen de disco para una máquina virtual gestionada por KubeVirt. Os dejo como hacerlo, generamos un YAML “crear-maquina-cirros.yaml“:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
apiVersion: kubevirt.io/v1 kind: VirtualMachine metadata: name: mi-vm spec: running: true template: spec: domain: devices: disks: - name: disk0 disk: bus: virtio resources: requests: memory: 1Gi volumes: - name: disk0 persistentVolumeClaim: claimName: vm-disk-import |
Generamos la máquina:
1 2 |
root@KUBERNETES00:~# kubectl apply -f crear-maquina-cirros.yaml virtualmachine.kubevirt.io/mi-vm created |
Gestionar máquina virtual bajo Kubernetes con Virtctl
Utilizaré Virtctl como herramienta de línea de comandos para disponer de comandos adicionales para administrar máquinas virtuales ya que no lo podemos hacer con “kubectl” como con los contenedores:
1 2 3 4 5 6 7 8 |
# Asegúrate de descargar la versión correspondiente a tu sistema operativo y arquitectura root@KUBERNETES00:~# curl -LO https://github.com/kubevirt/kubevirt/releases/download/v0.55.0/virtctl-v0.55.0-linux-amd64 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 56.3M 100 56.3M 0 0 24.9M 0 0:00:02 0:00:02 --:--:-- 43.9M root@KUBERNETES00:~# chmod +x virtctl-v0.55.0-linux-amd64 root@KUBERNETES00:~# sudo mv virtctl-v0.55.0-linux-amd64 /usr/local/bin/virtctl |
Ahora podemos arrancar la máquina virtual:
1 |
virtctl start mi-vm |
O acceder vía consola:
1 |
virtctl console mi-vm |
Espero os parezca interesante…
Te ha gustado la entrada SGUENOS EN TWITTER O INVITANOS A UN CAFE?