Skip to main content

Zero-Downtime Deployment

Metodología y mecanismos de ingeniería para actualizar servicios en producción sin interrumpir el servicio a los usuarios, romper conexiones TCP existentes o generar errores HTTP 502/503.

1. Visión general del concepto y problema sistémico

La actualización primitiva de servicios mediante el reinicio del proceso (systemctl restart app o docker restart container) provoca un tiempo de inactividad (Downtime). Durante 5-30 segundos, mientras el nuevo proceso inicializa el entorno de ejecución, se conecta a las bases de datos y compila código JIT:

  • Todas las solicitudes HTTP activas actuales se interrumpen en medio de la transferencia de datos.
  • El balanceador de carga o el proxy inverso devuelve errores 502 Bad Gateway o 504 Gateway Timeout a los usuarios.
  • Las transacciones de los usuarios (pagos, guardado de estado, generación de LLM) quedan en un estado semi-ruinoso.

Zero-Downtime Deployment elimina el tiempo de inactividad mediante la orquestación del ciclo de vida de los procesos. La nueva versión de la aplicación se inicia en paralelo con la antigua. El tráfico se cambia únicamente después de que la nueva instancia haya respondido exitosamente al Health Check de la sonda, y la antigua instancia finaliza correctamente todas las operaciones iniciadas en modo Graceful Shutdown.

Fase 1: Versión activa v1.0
[Clientes] ---> [Reverse Proxy / Nginx] ---> [Contenedor v1.0 (Activo)]

Fase 2: Inicio de v1.1 y Healthcheck
[Clientes] ---> [Reverse Proxy / Nginx] ---> [Contenedor v1.0 (Activo)]
                                             [Contenedor v1.1 (Iniciando... /healthz: 200 OK)]

Fase 3: Cambio de tráfico y Graceful Shutdown v1.0
[Clientes] ---> [Reverse Proxy / Nginx] ---> [Contenedor v1.1 (Activo)]
                                       \---> [Contenedor v1.0 (Finalizando solicitudes activas... SIGTERM)]

Fase 4: Finalización (v1.0 apagado)
[Clientes] ---> [Reverse Proxy / Nginx] ---> [Contenedor v1.1 (Activo)]

2. Taxonomía arquitectónica y modelo mental

Estrategias de actualización continua:

  1. Blue/Green Deployment:
    • Duplicación completa de la capa de infraestructura.
    • Cambio de tráfico instantáneo y atómico a nivel de Nginx, Traefik o DNS/ALB.
    • La mayor fiabilidad, la reversión más sencilla, pero requiere el doble de memoria y recursos del host.
  2. Rolling Update:
    • Los contenedores se actualizan secuencialmente uno a uno o en grupos.
    • Se mantiene la capacidad constante del clúster (por ejemplo, mínimo 3 réplicas activas de 4).
    • Ahorro de recursos de hardware, estándar por defecto en Kubernetes y Docker Swarm.
  3. Canary Release:
    • La nueva versión recibe un porcentaje fijo de tráfico real (por ejemplo, 2-5%) o usuarios de un grupo interno específico.
    • Se monitorean métricas del sistema (Tasa de Errores, Latencia). Si no se detectan anomalías, la parte del tráfico se incrementa gradualmente hasta el 100%.

3. Pipeline técnico y mecánica interna

Implementación de Graceful Shutdown en Node.js / TypeScript

El proceso debe interceptar correctamente las señales del sistema operativo SIGTERM y SIGINT:

import express from "express";
import http from "http";

const app = express();
let isShuttingDown = false;

// Endpoint de Healthcheck para el orquestador
app.get("/healthz", (req, res) => {
  if (isShuttingDown) {
    // No enviar nuevas solicitudes al balanceador
    return res.status(503).json({ status: "shutting_down" });
  }
  return res.status(200).json({ status: "healthy" });
});

const server = http.createServer(app);
server.listen(3000);

// Interceptar la señal de terminación de Docker / systemd
process.on("SIGTERM", () => {
  console.log("SIGTERM recibido. Iniciando apagado elegante...");
  isShuttingDown = true;

  // 1. Detener la aceptación de nuevas conexiones HTTP
  server.close(async () => {
    console.log("Cerradas todas las conexiones HTTP restantes.");

    try {
      // 2. Cerrar pools de conexiones a bases de datos y Redis
      await dbPool.end();
      await redisClient.quit();
      console.log("Conexiones de infraestructura cerradas. Saliendo del proceso.");
      process.exit(0);
    } catch (err) {
      console.error("Error durante el cierre:", err);
      process.exit(1);
    }
  });

  // 3. Fail-safe: terminación forzada si los sockets se cuelgan
  setTimeout(() => {
    console.error("Terminación forzada: conexiones activas agotadas.");
    process.exit(1);
  }, 20000); // 20 segundos de límite
});

Configuración de Healthcheck en Docker Compose

Para asegurar el cambio de tráfico por parte del orquestador, se configura el parámetro de verificación de salud:

services:
  api:
    image: my-company/api:v1.2.0
    restart: always
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/healthz"]
      interval: 5s
      timeout: 3s
      retries: 3
      start_period: 10s
    stop_grace_period: 30s # Tiempo para realizar Graceful Shutdown

4. Escenarios prácticos de ingeniería en producción

01. Despliegue a través de Coolify sin interrupción del tráfico

La plataforma Coolify utiliza un proxy inverso Traefik integrado. Al presionar Deploy, compila una nueva imagen de Docker, inicia un nuevo contenedor en un puerto aleatorio, consulta su endpoint de healthcheck y solo después de confirmar su operatividad, reconfigura la ruta de Traefik sobre la marcha, tras lo cual envía la señal SIGTERM a la antigua réplica.

02. Recarga de configuración en caliente de Nginx (nginx -s reload)

Al actualizar certificados SSL o agregar nuevas configuraciones, el proceso maestro de Nginx inicia un nuevo conjunto de procesos worker con la nueva configuración. Los antiguos workers dejan de aceptar nuevas conexiones, finalizan la transferencia de archivos activos a los clientes actuales y solo después de esto se autodestruyen sin perder un solo paquete.

03. Despliegue de migraciones de bases de datos sin bloquear tablas en PostgreSQL

Al agregar índices a grandes tablas (millones de filas), el estándar CREATE INDEX bloquea la tabla para escritura (EXCLUSIVE LOCK), lo que provoca que la cola de consultas se cuelgue. Los ingenieros utilizan la opción CREATE INDEX CONCURRENTLY, que construye el índice en segundo plano sin bloquear operaciones paralelas de lectura y escritura.


5. Errores comunes, trampas y seguridad

  1. Falta de stop_grace_period en Docker: Por defecto, Docker da al contenedor solo 10 segundos para manejar SIGTERM, después de lo cual envía un SIGKILL mortal (muerte inmediata del proceso). Si una solicitud prolongada a LLM o un webhook de pago dura 12 segundos, la operación se interrumpirá con daño a los datos. Aumente el límite a 30-60 segundos.
  2. Incompatibilidad de versiones de código con la base de datos: Intentar renombrar una columna de tabla (ALTER TABLE users RENAME COLUMN email TO contact_email) rompe instantáneamente la antigua versión del código, que sigue funcionando durante el despliegue en paralelo. Siempre aplique el patrón Expand-and-Contract en dos versiones separadas.
  3. Falsos positivos en los endpoints de Health Check: Si el endpoint /healthz verifica la disponibilidad de todas las API externas (por ejemplo, OpenAI o Twitter API) y estas se ralentizan temporalmente, el orquestador considerará su propio servicio muerto y entrará en un ciclo infinito de reinicios (CrashLoopBackOff / Healthcheck Storm), destruyendo el sistema de producción operativo. La sonda de liveness debe verificar exclusivamente el proceso local.
/ Preguntas frecuentesSchema.org FAQPage

FAQ: Zero-Downtime Deployment

El despliegue Blue-Green implica crear una segunda copia completamente aislada de todo el entorno (Green) junto a la activa (Blue). Tras pasar las pruebas, el tráfico se cambia instantáneamente a nivel del balanceador (0% -> 100%), y la reversión consiste en cambiar de nuevo en 1 segundo. Rolling Update actualiza contenedores/instancias de manera gradual en lotes (por ejemplo, 25%), ahorrando recursos de hardware, pero requiere compatibilidad hacia atrás durante la fase de coexistencia.
/ Enlaces internos
Todos los términos