Problema

Muchos administradores de homelab mantienen sus entornos sobre hipervisores tradicionales (por ejemplo, ESXi) y gestionan máquinas virtuales como “mascotas”. Cada VM se crea manualmente, se le asignan recursos a mano y su configuración se mantiene fuera de cualquier sistema declarativo. Con el tiempo, esa práctica genera tres síntomas recurrentes:

  1. Ansiedad operativa – siempre está la duda de si una VM va a fallar porque su estado no está versionado.
  2. Dificultad de replicación – clonar una configuración implica copiar discos, scripts y documentación dispersa.
  3. Escalabilidad limitada – añadir una nueva instancia requiere repetir los mismos pasos manuales, lo que aumenta la probabilidad de errores.

El reto consiste en migrar a una arquitectura donde los recursos son “cattle”: plantillas inmutables, despliegues reproducibles y todo el ciclo gestionado por código. La solución típica combina un hipervisor basado en Kubernetes (Harvester), infraestructura como código (Terraform), orquestación de configuración (Ansible) y GitOps (Fleet, ArgoCD, etc.).

Causa

Los problemas descritos aparecen por una combinación de factores:

  • Dependencia de UI del hipervisor – crear VMs a través de la consola de ESXi obliga a recordar valores de CPU, RAM y discos en la cabeza o en notas sueltas.
  • Ausencia de estado declarativo – sin un repositorio que describa la infraestructura, cualquier cambio se hace “ad‑hoc” y no queda registro.
  • Falta de separación de roles – los mismos nodos ejecutan workloads críticos y tareas de gestión, lo que dificulta la planificación de capacidad y la aplicación de políticas de seguridad.
  • Almacenamiento de datos sin orquestador – volúmenes persistentes gestionados manualmente no se benefician de la reconciliación automática, lo que genera desalineación entre la configuración y la realidad.

Estos patrones son comunes en homelabs que evolucionan de un entorno de pruebas a una plataforma de producción ligera.

Solución

Una arquitectura basada en Harvester permite tratar el hipervisor como un clúster Kubernetes. La solución se divide en cuatro capas:

  1. Hipervisor Harvester – despliega KubeVirt, RKE2 y Longhorn como componentes integrados. Cada VM es un recurso VirtualMachine de Kubernetes.
  2. Infraestructura como código con Terraform – describe VMs, redes y volúmenes en archivos .tf. Terraform mantiene el estado y evita la interacción manual con la UI.
  3. Configuración de la VM con Ansible – una vez que Terraform crea la instancia, Ansible aplica roles (SSH hardening, instalación de paquetes, etc.) usando ansible-playbook.
  4. GitOps para workloads – los clusters K3s/K8s ejecutan aplicaciones declaradas en repositorios Git. Fleet o ArgoCD observan los cambios y los aplican automáticamente.

Paso a paso resumido

  1. Instalar Harvester en el hardware existente (ej. Dell DL380 Gen9). La instalación es idéntica a la de cualquier distro ISO: arranca, sigue el asistente y habilita la integración con Longhorn.
  2. Configurar Terraform:
    • Añade el provider harvester y define recursos harvester_virtualmachine.
    • Usa módulos para crear plantillas reutilizables (CPU, RAM, discos).
  3. Ejecutar terraform plan y terraform apply. Cada VM aparece como un pod KubeVirt; no hay UI que tocar.
  4. Desencadenar Ansible:
    • Usa el inventario dinámico de Terraform (terraform output -json) para generar la lista de hosts.
    • Ejecuta playbooks que instalen los servicios que antes estaban en VMs “pet”.
  5. Desplegar workloads con GitOps:
    • Crea un repo Git con manifests Helm o Kustomize.
    • Configura Fleet (o ArgoCD) para observar el repo y sincronizar el clúster.
  6. Separar roles de nodo:
    • Etiqueta el nodo de control‑plane como node-role.kubernetes.io/control-plane: "".
    • Marca un nodo como longhorn.io/drain: "true" para almacenar volúmenes.
    • El resto quedan como workers sin taints, listos para workloads stateless.

Con este flujo, cualquier nueva aplicación se agrega mediante un commit en Git; Terraform y Ansible se encargan de la infraestructura subyacente, y el clúster mantiene el estado deseado sin intervención manual.

Cuándo aplicar esta solución

Escenarios ideales

  • Homelabs que ya usan ESXi, Proxmox o Hyper‑V y quieren reducir la fricción operativa.
  • Equipos que gestionan más de 5 VMs y la complejidad de la UI se vuelve un cuello de botella.
  • Necesidad de versionar la infraestructura y los workloads (cumplimiento, auditoría).
  • Interés en consolidar almacenamiento con Longhorn o Ceph y aprovechar la auto‑reconciliación.

Señales de que la solución encaja

  • Los despliegues de nuevas VMs tardan más de 15 min por la configuración manual.
  • Los backups de configuraciones se guardan en documentos de texto sueltos.
  • Se percibe “drift” entre lo que debería estar corriendo y lo que realmente está.

Casos donde NO aplicar

  • Entornos con requisitos de certificación que prohíben Kubernetes en la capa de virtualización.
  • Hardware que no soporta la carga de un clúster RKE2 (p.ej., recursos extremadamente limitados).
  • Necesidad de VMs con acceso directo a hardware que KubeVirt no expone (passthrough PCI completo).

Código

# Provider y módulo básico para crear una VM en Harvester
terraform {
  required_providers {
    harvester = {
      source  = "harvester/harvester"
      version = "~> 0.4"
    }
  }
}

provider "harvester" {
  kubeconfig = "~/.kube/harvester.yaml"
}

module "cattle_vm" {
  source = "./modules/cattle_vm"

  name   = "paperless-ngx"
  cpu    = 2
  memory = 4096
  disks = [
    {
      name        = "root"
      size        = 40
      boot_order  = 1
    },
    {
      name        = "data"
      size        = 200
      boot_order  = 0
    }
  ]
}
# Inventario dinámico de Ansible usando salida de Terraform
terraform output -json > tf_output.json
python3 -c "
import json, sys
data = json.load(open('tf_output.json'))
hosts = {vm['value']['name']: vm['value']['ip'] for vm in data['virtual_machines']['value']}
print('[cattle_vms]')
for name, ip in hosts.items():
    print(f'{name} ansible_host={ip}')
" > inventory.ini

# Playbook sencillo para instalar Docker y habilitar el servicio
ansible-playbook -i inventory.ini site.yml --limit paperless-ngx

Verificación

  1. Terraformterraform state list debe mostrar los recursos harvester_virtualmachine creados.
  2. KubeVirtkubectl get vm muestra las VMs con estado Running.
  3. Longhornkubectl -n longhorn-system get pods todos en Running y kubectl get pvc muestra los volúmenes vinculados.
  4. Ansibleansible -m ping all -i inventory.ini devuelve pong para cada host.
  5. GitOps – verifica que los pods de la aplicación aparecen tras el commit: kubectl get pods -n paperless.

Notas adicionales

  • Versionado del provider: bloquea la versión de harvester en Terraform para evitar cambios inesperados en la API.
  • Persistencia de datos: al migrar PVs de ESXi a Longhorn, usa rsync o snapshots y restaura los datos antes de aplicar los manifests.
  • Tolerancias: etiqueta los nodos de Longhorn con node-role.kubernetes.io/storage: "" y usa nodeSelector en los PVC para forzar la afinidad.
  • Backup del estado: almacena terraform.tfstate en un bucket S3 o en un repo Git‑encrypted para evitar pérdida de información.
  • Monitoreo: habilita Prometheus y Grafana en el clúster para observar la reconciliación de Longhorn y la latencia de los workloads GitOps.

Con este enfoque, el homelab pasa de ser una colección de máquinas “mascotas” a una plataforma declarativa, reproducible y prácticamente libre de mantenimiento manual. La verdadera ganancia no es solo la automatización, sino la tranquilidad de saber que cualquier falla se puede revertir con un git revert y un terraform apply.