Los modelos de difusión como Z-Image-Turbo LoRA demandan recursos considerables. Un servicio que depende de GPU puede fallar por múltiples razones: agotamiento de memoria de video, excepciones en la carga de checkpoints, o simplemente timeouts durante la inferencia. Sin un mecanismo de supervisión, cada caída implica intervanción manual.
Supervisor resuelve esta problemática actuando como gestor de procesos. Su función principal consiste en vigilar que los servicios permanezcan activos, reiniciándolos cuando detectan terminaciones anómalas. En este artículo veremos cómo instrumentarlo para un backend basado en FastAPI que expone la API de Z-Image-Turbo LoRA.
2. Instalación y estructura de archivos
En distribuciones Debian/Ubuntu, la instalación es directa:
sudo apt update && sudo apt install -y supervisor
Para instalaciones mediante pip, que permiten versiones más recientes:
pip install --user supervisor
mkdir -p ~/.config/supervisor/conf.d
Los archivos relevantes se organizan así:
| Ruta | Propósito |
|---|---|
/etc/supervisor/supervisord.conf |
Configuración maestra del demonio |
/etc/supervisor/conf.d/*.conf |
Definiciones individuales de programas |
/var/log/supervisor/ |
Logs del propio Supervisor |
3. Configuración del servicio Z-Image-Turbo LoRA
Creamos el archivo de configuración específico. Es fundamental prestar atención a las variables de entorno, ya que Supervisor ejecuta procesos con un entorno mínimo por defecto:
[program:zitl-api]
command=/home/aiuser/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 7860
directory=/opt/services/z-image-turbo-lora/backend
user=aiuser
numprocs=1
autostart=true
autorestart=true
startretries=5
startsecs=15
stopwaitsecs=20
stopsignal=INT
stopasgroup=true
killasgroup=true
environment=PYTHONPATH="/opt/services/z-image-turbo-lora/backend/src",CUDA_VISIBLE_DEVICES="0",TRANSFORMERS_CACHE="/mnt/data/cache/huggingface",HF_HOME="/mnt/data/cache/huggingface",PATH="/home/aiuser/.venv/bin:/usr/local/bin:/usr/bin:%(ENV_PATH)s"
redirect_stderr=true
stdout_logfile=/var/log/zitl/api.log
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=5
Observa que stopsignal=INT envía SIGINT en lugar de SIGTERM, permitiendo que Uvicorn cierre conexiones activas de forma ordenada antes de detenerse.
4. Gestión del ciclo de vida
Tras modificar configuraciones, el flujo de activación es:
sudo supervisorctl reread # Detecta cambios en archivos .conf
sudo supervisorctl update # Aplica sin tocar procesos sin cambios
sudo supervisorctl start zitl-api
Comandos útiles para operación diaria:
# Estado detallado con PID y tiempo activo
sudo supervisorctl status zitl-api
# Reinicio controlado (útil tras despliegues)
sudo supervisorctl restart zitl-api
# Inspección de logs sin acceder al filesystem
sudo supervisorctl tail -50 zitl-api
# Modo interactivo para múltiples operaciones
sudo supervisorctl
5. Estrategias ante escenarios críticos
5.1 Cargas de modelo prolongadas
Z-Image-Turbo puede tardar decenas de segundos en cargar pesos en memoria de video. El parámetro startsecs=15 evita que Supervisor considere fallido un inicio lento pero exitoso. Ajusta este valor según el tamaño de tu modelo base y la velocidad de tu almacenamiento.
5.2 Gestión de códigos de salida
Para distinguir entre fallos reales y terminaciones programadas:
[program:zitl-api]
autorestart=unexpected
exitcodes=0,137
Con esta configuración, un exit 0 (terminación voluntaria) o un 137 (SIGKILL por OOM killer) no provocarán reinicio. Cualquier otro código sí lo hará.
5.3 Instanicas múltiples con balanceo
Si tu hardware lo permite, puedes distribuir peticiones entre varios workers. Nota que cada instancia mantiene su propia copia del modelo en VRAM:
[program:zitl-worker-%(process_num)s]
command=/home/aiuser/.venv/bin/gunicorn -w 1 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:786%(process_num)d main:app
process_name=zitl-worker-%(process_num)s
numprocs=2
6. Verificación de salud externa
Un endpoint de health check permite que Supervisor actúe con información real del servicio, no solo del proceso:
# health_probe.sh
#!/usr/bin/env bash
HEALTH_URL="http://127.0.0.1:7860/health"
RESPONSE=$(curl -sf -m 3 "$HEALTH_URL" 2>/dev/null)
if [[ "$RESPONSE" == *'"status":"ok"'* ]]; then
exit 0
else
exit 1
fi
Integrado con Supervisor mediante un event listener que ejecuta el script periódicamente y emite supervisorctl restart cuando detecta fallo.
7. Interfaz web de administración
Habilita el servidor HTTP interno editando supervisord.conf:
[inet_http_server]
port=127.0.0.1:9001
username=admin
password_file=/etc/supervisor/.htpasswd
Genera credenciales con htpasswd y reinicia el servicio. Accede mediante túnel SSH por seguridad:
ssh -L 9001:localhost:9001 tu-servidor
8. Diagnóstico de incidencias frecuentes
| Síntoma | Verificación | Solución |
|---|---|---|
| STARTING → BACKOFF → FATAL | supervisorctl tail zitl-api |
Revisar ruta del intérprete Python o permisos del directorio |
| RUNNING pero no responde | curl al endpoint /health | Aumentar startsecs, verificar puerto ocupado |
| Reinicios infinitos | Logs con exited too quickly |
Implementar retardo con sleep en script wrapper |
| Logs vacíos | Permisos de /var/log/zitl/ |
Crear directorio con chown aiuser:aiuser |
9. Automatización del despliegue
Para integrar en pipelines CI/CD, un playbook de Ansible simplifica la configuración:
- name: Desplegar configuración Supervisor para ZITL
template:
src: zitl-supervisor.conf.j2
dest: /etc/supervisor/conf.d/zitl-api.conf
notify: recargar supervisor
- name: Asegurar directorio de logs
file:
path: /var/log/zitl
state: directory
owner: aiuser
mode: '0755'