Tarea 4: Introducción a OpenTofu + libvirt

  1. Vamos a descargar las imágenes cloud con las que vamos a trabajar. Las vamos a copiar en el directorio correspondiente al pool default:

    cd /var/lib/libvirt/images
    sudo wget https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-genericcloud-amd64-daily.qcow2 -O debian13-base.qcow2
    sudo wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img -O ubuntu2404-base.qcow2

    Las imágenes bases se llaman debian13-base.qcow2 y ubuntu2404-base.qcow2.

    Estos discos son muy pequeños, por lo tanto antes de empezar a utilizarlos vamos a redimensionarlos:

    sudo qemu-img resize debian13-base.qcow2 10G
    sudo qemu-img resize ubuntu2404-base.qcow2 10G
  2. Instala OpenTofu. Vamos a trabajar con el repositorio ejercicios_pi que ya tienes en tu equipo. Para cada ejemplo nos situamos en el directorio opentofu/ejemploX correspondiente.

Ejemplo 1: Máquina virtual conectada a la red “default”

En este ejemplo vamos a crear una máquina virtual conectada a la red default. OpenTofu trabaja con ficheros tf que se pueden llamar como queramos. Veamos los ficheros con los que vamos a trabajar:

  • provider.tf: Configura el provider dmacvicar/libvirt y la URI de conexión (qemu:///system). El plugin del provider se instala en el directorio .terraform cuando ejecutamos tofu init. Este comando normalmente sólo se ejecuta una vez. Este fichero no hay que modificarlo.

  • variables.tf: Se declaran variables globales, que podemos usar en nuestras definiciones. En este caso se definen:

    • var.libvirt_pool_name: nombre del pool de almacenamiento donde se crean los volúmenes. Su valor por defecto es default.
    • var.base_image: nombre de la imagen base en el pool. Su valor por defecto es debian13-base.qcow2.
  • cloud-init/user-data1.yaml: Fichero para configurar la máquina virtual con el mecanismo de cloud-init. En este ejemplo:

    • Se indica el hostname, zona horaria, locale y teclado.
    • Se configura el usuario debian con acceso sudo sin contraseña, clave ssh y contraseña.
    • Se instala qemu-guest-agent y se actualiza el sistema.

    Debes modificar este fichero para añadir tu clave pública en el campo ssh-authorized-keys.

  • main.tf: Aquí está la definición de los recursos con los que queremos trabajar. En este fichero se definen los siguientes recursos:

    • resource "libvirt_volume" "ej1-server1-disk": Un clon ligero sobre la imagen base indicada por var.base_image, usando base_volume_name y base_volume_pool.
    • resource "libvirt_cloudinit_disk" "ej1-server1-cloudinit": Un disco con formato ISO donde se guarda el fichero cloud-init/user-data1.yaml.
    • resource "libvirt_domain" "ej1-server1": Una máquina virtual con 1024 MB de RAM, 2 vCPUs, conectada a la red default y con consola serie habilitada.
  • output.tf: Se define la información que se mostrará al terminar de crear el escenario (nombre e IP de la máquina). Este fichero no hay que modificarlo.

Modifica cloud-init/user-data1.yaml para añadir tu clave pública y, si lo deseas, main.tf para cambiar la memoria o el número de CPUs.

Una vez hechos los cambios, los comandos se ejecutan en el directorio del proyecto:

  • Ejecutamos una sola vez el comando tofu init para instalar el plugin del provider.
  • Ejecutamos el comando tofu plan para ver las acciones que se van a realizar.
  • Para aplicar el escenario descrito ejecutamos tofu apply.
  • Una vez creado el escenario nos saldrá la información definida en output.tf. Esta información siempre se puede mostrar ejecutando tofu output.
  • Podemos ver el estado de los recursos ejecutando tofu show.
  • Para eliminar todos los recursos creados, ejecutamos tofu destroy.

Ejemplo 2: Máquina virtual con disco adicional

Nos situamos en el directorio opentofu/ejemplo2. Este ejemplo es similar al anterior, pero en esta ocasión la máquina virtual tiene un disco adicional de 1 GB. En el fichero main.tf se declaran 4 recursos:

  • libvirt_volume "ej2-server1-disk": el disco principal creado con clonación enlazada.
  • libvirt_volume "ej2-server1-disk-extra1": un disco adicional vacío de 1 GB (el tamaño se indica en bytes: 1 * 1024 * 1024 * 1024).
  • libvirt_cloudinit_disk "ej2-server1-cloudinit": el disco ISO con la configuración cloud-init.
  • libvirt_domain "ej2-server1": la máquina virtual, con dos entradas disk para el disco principal y el extra.

Ejemplo 3: Máquina virtual conectada a dos redes con DHCP

Nos situamos en el directorio opentofu/ejemplo3. En este ejemplo vamos a comenzar a trabajar con las redes. En los dos ejemplos anteriores habíamos conectado la máquina virtual a la red default, que no es gestionada por OpenTofu. En este ejemplo vamos a crear redes gestionadas por OpenTofu, que se crearán con tofu apply y se eliminarán con tofu destroy.

Se ha añadido el fichero network.tf donde se define la red:

  • resource "libvirt_network" "ej3-nat-dhcp": una red NAT con DHCP en el rango 192.168.100.0/24. Estudia los parámetros que hemos indicado.

A continuación estudia la definición del recurso de la máquina virtual en el fichero main.tf y comprueba que la máquina está conectada a dos redes (como en el ejemplo 2, también tiene un disco adicional de 1 GB). Recuerda que cuando conectamos a una red con servidor DHCP indicamos el parámetro wait_for_lease = true.

  • Cuando la red no es creada por OpenTofu, por ejemplo default, indicamos el nombre con el parámetro network_name.
  • Cuando la red es gestionada por OpenTofu, indicamos su id con el parámetro network_id, por ejemplo: network_id = libvirt_network.ej3-nat-dhcp.id.

El hecho de que conectemos una máquina virtual a dos redes no significa que netplan configure las dos interfaces. Tenemos que configurarlo nosotros, para ello:

  • Se ha creado el fichero cloud-init/network-config1.yaml, donde se guarda la configuración netplan de la máquina.
  • Este fichero se añade en la imagen ISO junto al fichero cloud-init/user-data1.yaml. Esto se hace con el parámetro network_config del recurso libvirt_cloudinit_disk "ej3-server1-cloudinit" en el fichero main.tf.