Apagado ordenado (graceful shutdown)
Cuando despliegas una versión nueva o escalas hacia abajo, la plataforma detiene
las instancias viejas. No las mata de golpe: primero les envía la señal SIGTERM
para pedirles que terminen ordenadamente. Un proceso bien hecho escucha esa
señal y hace un apagado ordenado (graceful shutdown):
- Deja de aceptar nuevas peticiones (cierra el servidor HTTP).
- Termina las peticiones que ya están en curso.
- Cierra los recursos abiertos: conexiones a la base de datos, colas, ficheros.
- Sale del proceso (
process.exit(0)).
process.on("SIGTERM", async () => {
console.log("SIGTERM recibido, cerrando...");
servidor.close(); // 1 y 2: no acepta nuevas, drena las activas
await db.disconnect(); // 3: cierra la BD
process.exit(0); // 4
});
Sin esto, un despliegue cortaría peticiones a medias y dejaría conexiones colgadas. Suele añadirse un timeout: si en, p. ej., 10 segundos no ha terminado, se fuerza la salida para no quedar bloqueado.
Process managers (PM2)
En producción no ejecutas node app.js a mano. Un process manager como
PM2 se encarga de:
- Reiniciar el proceso si se cae (resiliencia).
- Ejecutar varias instancias en modo cluster para usar todos los núcleos.
- Hacer recarga sin downtime (reload) en los despliegues.
- Centralizar los logs y exponer métricas básicas.
Contenedores (Docker)
Un contenedor Docker empaqueta tu app con su entorno (versión de Node, dependencias, sistema base) en una imagen inmutable. La misma imagen corre igual en tu portátil y en producción ("funciona en mi máquina" deja de ser una excusa). La config y los secretos se inyectan desde fuera como variables de entorno (coherente con 12-factor), nunca dentro de la imagen.
CI/CD
Un pipeline de CI/CD (GitHub Actions, GitLab CI...) automatiza el camino del
commit a producción: en cada push ejecuta los tests y el lint (Continuous
Integration) y, si pasan, construye la imagen y la despliega (Continuous
Delivery/Deployment). El objetivo es que desplegar sea un evento aburrido y seguro,
no un ritual de riesgo.
Escalado horizontal
Para soportar más carga tienes dos vías: vertical (una máquina más grande, con límite físico) y horizontal (más instancias detrás de un balanceador de carga). El escalado horizontal es la base de la alta disponibilidad, pero exige que el servidor sea stateless: no debe guardar estado en memoria (sesiones, caché) que sólo viva en una instancia; ese estado va a un almacén compartido (Redis, la BD). Así cualquier instancia puede atender cualquier petición, y health checks + graceful shutdown permiten añadir y quitar instancias sin que el usuario lo note.
Ejemplos
Esqueleto de graceful shutdown ante SIGTERM
// Simulación: un servidor con un método close y una BD con disconnect.
const servidor = { close() { console.log("Servidor cerrado: no acepta nuevas peticiones"); } };
const db = { async disconnect() { console.log("BD desconectada"); } };
async function apagar(senal) {
console.log(senal + " recibido, iniciando apagado ordenado...");
servidor.close();
await db.disconnect();
console.log("Apagado completo");
}
await apagar("SIGTERM");