Mientras profundizaba en el mundo de CI/CD me he dado cuenta que crear máquinas virtuales manualmente no es sostenible en el tiempo. Esto me llevó a leer mucho sobre maneras de automatizar el proceso de crear las VMs de modo que pueda pasar el menor tiempo posible configurando un ambiente para pruebas y/o aprendizaje.

Por esta razón he decidido crear este post demostrando la manera en la que me aseguro de siempre tener una imagen actualizada de la que puedo crear VMs en cuestión de segundos. Para esto estaré usando mi distribución Linux favorita, Ubuntu, en su sabor de soporte extendido cómo la base de la plantilla.

Ten presente que este proceso se puede hacer con cualquier distribución que provea una imagen cloud.

Sin nada más que añadir, vamos a empezar!

Básicamente, los pasos que vamos a seguir son los siguientes:

  1. Descargar y Personalizar una imagen de Ubuntu
  2. Crear y Personalizar la VM
  3. Convertir a Plantilla
  4. Probar!
  5. Automatizar la creación de la plantilla con CRON

1. Descargar y Personalizar una imagen de Ubuntu

La mayoría de las distribuciones Linux proveen imágenes cloud que son adecuadas para la creación de plantillas, y Ubuntu no es la excepción.

Para descargar tu versión preferida, dirígete a https://cloud-images.ubuntu.com/ y búscala. En este caso, ya que la meta es automatizar el proceso, vamos a descargar la imagen de Ubuntu 24.04 desde la terminal usando wget:

wget "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img" --output-document=noble-server-cloudimg-amd64.img

Antes de crear nuestra plantilla, necesitamos asegurarnos de que la imagen cloud incluye el agente de QEMU para que nuestras VMs puedan interactuar mejor con Proxmox (cómo reportarle al host la dirección IP que obtuvieron) y para esto utilizaremos una herramienta llamada libguestfs que nos permite accesar y modificar las imágenes para las VMs.

Puedes verificar si la tienes instalada en tu host de Proxmox usando el siguiente comando:

apt list --installed | grep libguestfs

lo que nos va a reportar algo cómo esto:

root@pve01:~# apt list --installed | grep libguestfs

libguestfs-hfsplus/stable,now 1:1.48.6-2 amd64 [installed,automatic]
libguestfs-perl/stable,now 1:1.48.6-2 amd64 [installed,automatic]
libguestfs-reiserfs/stable,now 1:1.48.6-2 amd64 [installed,automatic]
libguestfs-tools/stable,now 1:1.48.6-2 amd64 [installed]
libguestfs-xfs/stable,now 1:1.48.6-2 amd64 [installed,automatic]
libguestfs0/stable,now 1:1.48.6-2 amd64 [installed,automatic]

Si no lo tienes instalado, lo puedes obtener con:

apt update -y && apt install libguestfs-tools -y

Después de esto, vamos a inyectar el agente de QEMU con el comando virt-customize:

virt-customize -a ./noble-server-cloudimg-amd64.img --install qemu-guest-agent --run-command "cat /dev/null > /etc/machine-id"

2. Crear y Personalizar la VM

Con nuestra imagen lista, ahora podemos crear nuestra VM que se convertirá en nuestra plantilla. Yo estoy usando el ID 999 para la VM ya que parece ser un número popular para esta clase de cosas, pero puedes usar el ID que quieras:

qm create 999 \
    --memory 2048 \
    --name ubuntu-24.04\
    --net0 virtio,bridge=vmbr0 \
    --tags ubuntu,cloud-image \
    --watchdog model=i6300esb,action=reset

Luego tenemos que importar la imagen cloud como un disco virtual en Proxmox. Nota que mi almacenamiento se llama local-lvm pero el tuyo pudiera ser diferente:

qm importdisk 999 ./noble-server-cloudimg-amd64.img local-lvm

Ahora le agregamos nuestro disco recién importado a nuestra VM.

qm set 999 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-999-disk-0

La siguiente porción es totalmente opcional, pero yo recomiendo hacerla. Básicamente podemos almacenar nuestra llave SSH pública en Launchpad lo que nos permite descargarla e importarla en nuestra imagen usando Cloud-init en el usuario que crearemos más adelante de modo que podamos iniciar sesión sin necesidad de colocar la contraseña. Si no te importa iniciar sesión con llave pública, siente libre de saltarte esta parte.

wget https://launchpad.net/~<Usuario de Launchpad>/+sshkeys -O ./keys

Agrega un CDROM de Cloud-init para la creación automática del usuario y contraseña:

qm set 999 --ide2 local-lvm:cloudinit

Vamos a asegurarnos que nuestra VM usará el disco previamente importado para iniciar:

qm set 999 --boot c --bootdisk scsi0

Y ahora terminaremos de personalizar nuestra VM. Vamos a crear un usuario linux-admin con una contraseña segura S3cur3P@ssw0rd, aplicamos la llave SSH al disco de Cloud-init y configuramos algunos parámetros básicos como la cantidad de núcleos, algunas etiquetas, configuramos las red para que use DHCP, nos aseguramos que nuestra VM inicie en caso de que nuestro host de Proxmox se reinicie, y habilitamos el agente de QEMU:

qm set 999 --ciuser linux-admin \
    --cipassword S3cur3P@ssw0rd \
    --cores 2 \
    --sshkeys ./keys \
    --description "Ubuntu 24.04 Image Template generated on `date`" \
    --ipconfig0 ip=dhcp \
    --onboot 1 \
    --ostype l26 \
    --agent 1 \

Cómo ultimo paso, vamos a configurar el tamaño del disco por defecto (Esto puede ser cambiado cuando se creen VMs basados en nuestra plantilla).

qm resize 999 scsi0 20G

3. Convertir a plantilla

En este punto estamos listos para crear la plantilla con el siguiente comando:

qm template 999

4. Probar!

Luego de haber hecho todos los pasos, deberías de tener disponible to recién creada plantilla lista para usar!:

proxmox-vm-template.png

También deberías de poder iniciar sesión en tu servidor sin problemas. Verifica la IP asignada por DHCP:

proxmox-vm-from-template.png

Y ahora simplemente conéctate a tu servidor via SSH usando el usuario y la contraseña que asignaste (linux-admin y Sup3rP@ssw0rd si no cambiaste nada del tutorial). Si usaste launchpad, entonces la contraseña no debería de ser necesaria para iniciar sesión.

5. Automatizar la creación de la plantilla con CRON

Si eres como yo, constantemente estarás creando muchas máquinas virtuales para probar diferentes cosas, y una de las cosas que hago al principio es actualizar las máquinas con el comando apt update && apt upgrade -y pero a veces este proceso se toma un poco más de tiempo del que me gustaría. Afortunadamente podemos hacer un script (el cual encontrarás al final) que se puede correr periódicamente usando cron de modo que nuestra plantilla siempre este actualizada.

Si quieres hacer esto, no te olvides de borrar la plantilla actual y la imagen cloud que habías descargado para que el script siempre descargue la versión más actualizada de la imagen y cree un plantilla fresca con la misma:

qm destroy 999 --purge
rm ./noble-server-cloudimg-amd64.img

Pega el contenido el script en un archivo, asegúrate que puede ser ejecutado y crea el cronjob como sigue:

Puedes crear y editar el archivo usando nano:

nano build-ubuntu-template.sh

Pega el contenido del script en el archivo y guárdalo usando CTRL+S y luego cierra el archivo con CTRL+X.

Haz el script ejecutable:

chmod +x build-ubuntu-template.sh

Ahora abre tu archivo de crontab:

nano /etc/crontab

Y agrega una linea al final para llamar a tu script. En mi caso estoy corriendo el script diariamente a las 4:00 AM:

00 4 * * * root /root/build-ubuntu-cloud-image.sh

Guarda tus cambios usando CTRL+S y luego cierra el archivo con CTRL+X.

Script Completo

#!/bin/bash

# Instalar libguestfs. Solo es necesario hacerlo una vez
# apt update -y && apt install libguestfs-tools -y

# Borra la plantilla actual
qm destroy 999 --purge

# Descarga la imagen
wget "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img" --output-document=noble-server-cloudimg-amd64.img

# Instala el Agente de QEMU
virt-customize -a ./noble-server-cloudimg-amd64.img --install qemu-guest-agent --run-command "cat /dev/null > /etc/machine-id"

# Crea la VM
qm create 999 \
    --memory 2048 \
    --name ubuntu-24.04\
    --net0 virtio,bridge=vmbr0 \
    --tags ubuntu,cloud-image \
    --watchdog model=i6300esb,action=reset

# Importa la imagen como un disco duro al almacenamiento de Proxmox
qm importdisk 999 ./noble-server-cloudimg-amd64.img local-lvm

# Agrega el disco a la VM
qm set 999 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-999-disk-0

# Descarga las llaves SSH desde Launchpad
# wget https://launchpad.net/~<Usuario de Launchpad>/+sshkeys -O ./keys

# Agrega el CD-ROM de Cloud-init a la VM
# qm set 999 --ide2 local-lvm:cloudinit

# Configura el disco de arranque
qm set 999 --boot c --bootdisk scsi0

# Configura las variables de la VM
qm set 999 --ciuser linux-admin \
    --cipassword Sup3rP@ssw0rd \
    --cores 2 \
    --sshkeys ./keys \
    --description "Ubuntu 24.04 Image Template generated on `date`" \
    --ipconfig0 ip=dhcp \
    --onboot 1 \
    --ostype l26 \
    --agent 1 \

# Reduce el tamaño del disco de arranque
qm resize 999 scsi0 20G

# Convierte la VM a una plantilla
qm template 999

# Borra la imagen
rm ./noble-server-cloudimg-amd64.img

Palabras finales

Aunque este script es realmente bueno tal cual esta y se puede usar de inmediato, todavía hay espacio para mejoras. Este script sienta las bases para un mejor sistema de manejo de plantillas mejorado a traves de la implementación de variables, validación de los archivos usando checksums, control de errores y más; también se puede editar para que trabaje con cualquier distribución Linux que provea una imagen cloud.

Quizás en el futuro crearé un post implementando estos cambios… quizás.

Muchas gracias por leer mi post y espero que te haya sido util!.