Problema
En un homelab o en una pequeña infraestructura de producción, es frecuente mezclar máquinas virtuales (VMs), contenedores LXC y stacks Docker bajo un mismo host. Cada capa tiene su propio mecanismo de actualización: apt upgrade && apt update para sistemas Debian/Ubuntu, scripts de Proxmox para LXC, docker compose pull && docker compose up -d para contenedores, o descargas manuales de binarios desde GitHub. Cuando el número de unidades supera la decena, el proceso manual de conectar por SSH, ejecutar comandos, responder a prompts y reiniciar se vuelve una carga de varias horas cada semana. La necesidad es una solución que coordine esas tareas, preserve la interactividad requerida y sea reutilizable en cualquier entorno similar.
Causa
- Diversidad de orígenes – No todos los nodos usan el mismo gestor de paquetes. LXC gestionado por Proxmox depende de plantillas, mientras que Docker depende de imágenes y compose files.
- Falta de orquestador central – Cada VM/LXC se trata como una entidad aislada; no existe un inventario que permita ejecutar acciones en lote.
- Prompts interactivos –
dpkgpuede preguntar si sobrescribir archivos de configuración, y los scripts de actualización pueden requerir confirmaciones. Sin una capa que capture y responda, la automatización se detiene. - Reinicios y dependencias – Algunas actualizaciones requieren reboot; otros servicios deben reiniciarse en orden. La ausencia de lógica de post‑update genera fallos silenciosos.
Solución
Una estrategia basada en Ansible combinada con hooks de Proxmox API y Docker Compose cubre la mayoría de los casos. Ansible actúa como motor de orquestación: mantiene un inventario de VMs, LXC y hosts Docker, ejecuta módulos específicos y gestiona prompts mediante debconf o expect. Los pasos clave son:
- Inventario dinámico – Usa el módulo
proxmoxpara extraer automáticamente la lista de VMs y contenedores desde el nodo Proxmox. - Roles por tipo – Define roles
vm_update,lxc_updateydocker_update. Cada rol contiene tareas idempotentes que:- Actualizan paquetes con
apten modo no interactivo (DEBIAN_FRONTEND=noninteractive). - Ejecutan scripts personalizados para LXC (por ejemplo,
pct exec <id> -- /usr/local/bin/update-helper.sh). - Actualizan stacks Docker con
docker compose pull && docker compose up -d.
- Actualizan paquetes con
- Manejo de prompts – Configura
debconfantes de la actualización o usa el móduloexpectpara capturar preguntas específicas. - Reboots controlados – Después de aplicar actualizaciones críticas, registra la necesidad de reboot en una variable y, al final del playbook, ejecuta
rebootsolo en los nodos marcados. - Post‑update hooks – Añade tareas que ejecuten comandos personalizados (por ejemplo, recargar iptables) usando
commandoshell. - Programación – Programa el playbook con
systemd timerocronpara que se ejecute semanalmente, enviando un reporte por correo o a un webhook de Discord.
Flujo de trabajo resumido
- Recopilación – Ansible consulta Proxmox y genera el inventario.
- Actualización – Cada host ejecuta su rol correspondiente.
- Validación – Se verifica que no haya paquetes retenidos (
apt list --upgradable). - Reboot – Si se detecta kernel o libc actualizado, se programa el reboot.
- Reporte – Se envía un resumen con estado de cada unidad.
Cuándo aplicar esta solución
- Entornos mixtos con al menos una decena de VMs/LXC/Docker bajo un mismo control de red.
- Política de parches semanal donde la ventana de mantenimiento es limitada.
- Necesidad de auditoría: el playbook genera logs estructurados que facilitan la trazabilidad.
- No aplica cuando todas las unidades usan exactamente el mismo gestor de paquetes y no requieren prompts; en ese caso un simple
apt-get upgrade -yen cron puede ser suficiente.
Código
# inventory/proxmox.yml (dinámico)
plugin: community.general.proxmox
url: https://proxmox.example.com:8006/api2/json
user: root@pam
password: "{{ vault_proxmox_password }}"
validate_certs: false
# playbook/update_all.yml
- hosts: all
become: true
vars:
reboot_required: false
roles:
- { role: vm_update, when: "'vm' in group_names" }
- { role: lxc_update, when: "'lxc' in group_names" }
- { role: docker_update, when: "'docker' in group_names" }
- hosts: all
become: true
tasks:
- name: Reboot if needed
reboot:
reboot_timeout: 600
when: reboot_required
# roles/vm_update/tasks/main.yml
- name: Set non‑interactive frontend
ansible.builtin.environment:
DEBIAN_FRONTEND: noninteractive
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Perform safe upgrade
apt:
upgrade: safe
register: apt_result
- name: Detect reboot requirement
ansible.builtin.stat:
path: /var/run/reboot-required
register: reboot_file
when: apt_result.changed
- name: Flag reboot
set_fact:
reboot_required: true
when: reboot_file.stat.exists
# roles/docker_update/tasks/main.yml
- name: Pull latest images
community.docker.docker_compose:
project_src: /srv/docker/project
pull: yes
state: present
- name: Recreate containers
community.docker.docker_compose:
project_src: /srv/docker/project
recreate: always
state: present
Verificación
- Ejecuta
ansible-playbook -i inventory/proxmox.yml playbook/update_all.yml --checkpara validar que todas las tareas se resuelven sin cambios inesperados. - Revisa el log generado en
/var/log/ansible/update_all.log; busca líneas conchanged=trueyfailed=false. - Confirma que los nodos que requerían reboot se reiniciaron y están accesibles (
ping -c 3 <host>). - En Docker hosts, verifica que
docker psmuestra los contenedores en estadoUpy que la versión de la imagen corresponde a la última disponible (docker images).
Notas adicionales
- Bloqueos de apt: si un nodo tiene
dpkgbloqueado, añade una tarea que elimine/var/lib/dpkg/lock-frontendsolo después de confirmar que no hay procesosaptactivos. - Orden de reinicio: en clústers con dependencias, usa el atributo
serialen el playbook para reiniciar nodos uno a la vez y evitar caídas simultáneas. - Gestión de secretos: almacena contraseñas de Proxmox y tokens de Docker en Ansible Vault; nunca las incluyas en texto plano.
- Extensibilidad: para casos especiales (por ejemplo, Cloudflared LXC que necesita recargar iptables), crea un handler en el rol
lxc_updateque se dispare al final de la actualización. - Monitoreo: integra el reporte con Prometheus Alertmanager o Grafana para visualizar el estado de cada unidad después del ciclo de actualización.