Solución de errores 502 en Node.js durante despliegues progresivos en la nube
Las aplicaciones Node.js que migran a la nube suelen sufrir breves cortes durante los despliegues debido a un manejo inadecuado de señales y una configuración incorrecta de las comprobaciones de estado.
Traducido automáticamente del original en inglés.
Los equipos que migran aplicaciones Node.js a entornos de nube contenedorizados se encuentran frecuentemente con ráfagas breves pero disruptivas de errores 502 en cada despliegue. Estos cortes ocurren porque los procesos estándar de Node.js no coordinan inherentemente con los balanceadores de carga al recibir señales de apagado, lo que provoca la caída de solicitudes activas. La implementación de endpoints específicos de comprobación de estado y lógica de apagado ordenado resuelve estas discrepancias temporales entre el orquestador y la infraestructura.
Qué ocurrió
Cuando un servicio pasa de una máquina virtual única a un entorno de contenedores gestionado como Kubernetes o ECS, la estrategia de despliegue suele cambiar a actualizaciones progresivas (rolling updates). En este modelo, se inician nuevas instancias mientras se detienen las antiguas. El problema central surge de una condición de carrera: el orquestador envía una señal SIGTERM al contenedor antiguo e instruye simultáneamente al balanceador de carga para que lo elimine de la rotación. Sin embargo, el balanceador de carga suele tardar varios segundos en actualizar su tabla de rutas. Dado que Node.js sale inmediatamente al recibir SIGTERM por defecto, el proceso muere antes de que el balanceador deje de enviarle tráfico, resultando en solicitudes fallidas.
El problema se agrava por los comportamientos de inicio y la gestión de conexiones. Los nuevos contenedores pueden recibir tráfico antes de que sus conexiones a la base de datos estén completamente establecidas si no hay una comprobación de disponibilidad (readiness check) implementada. Además, los tiempos de espera keep-alive desajustados entre Node.js y los balanceadores de carga de la nube causan más errores. Por ejemplo, los AWS Application Load Balancers mantienen conexiones inactivas durante 60 segundos, mientras que Node.js usa un valor predeterminado de 5 segundos. Cuando el balanceador intenta reutilizar un socket que Node ya ha cerrado, los usuarios experimentan errores 502 incluso fuera de las ventanas de despliegue.
Detalles clave
- Los despliegues progresivos disparan señales SIGTERM que matan los procesos de Node.js antes de que los balanceadores de carga dejen de enrutar tráfico hacia ellos.
- Las sondas de vida (liveness probes) solo deben verificar si el bucle de eventos es receptivo, nunca la conectividad a la base de datos, para evitar reinicios en cascada.
- Las sondas de disponibilidad (readiness probes) deben verificar la disponibilidad de dependencias y devolver un estado 503 durante el apagado para drenar el tráfico de forma segura.
- El
keepAliveTimeoutde Node.js debe configurarse con un valor superior al tiempo de inactividad del balanceador de carga para prevenir errores de reutilización de sockets. - Usar
CMD npm starten Docker impide el correcto reenvío de señales; los desarrolladores deben usar la forma exec o un proceso init como tini. - Los pools de bases de datos y otros recursos deben cerrarse solo después de que el servidor HTTP haya terminado de procesar las solicitudes en curso.
Contexto
Comprender la distinción entre vida (liveness) y disponibilidad (readiness) es crítico para operaciones estables en la nube. Las comprobaciones de vida responden si un proceso está bloqueado y necesita reiniciarse, mientras que las comprobaciones de disponibilidad determinan si una instancia puede aceptar tráfico. Confundir estos dos conceptos lleva a trampas comunes, como reiniciar todas las instancias simultáneamente cuando la base de datos se ralentiza, convirtiendo un pico menor de latencia en un corte total. El apagado ordenado es la práctica de detener nuevo trabajo, finalizar tareas existentes y luego salir, asegurando que no se pierda ni corrompa ningún dato durante la transición.
El manejo de señales en contenedores Linux también requiere atención a los IDs de proceso. En muchas configuraciones de Docker, la shell o el gestor de paquetes se ejecutan como PID 1, que no reenvía de manera fiable las señales de terminación al proceso hijo de Node.js. Sin una propagación correcta de señales, la aplicación ignora la solicitud de apagado hasta que el orquestador fuerza la muerte tras expirar el periodo de gracia, saltándose cualquier código de limpieza por completo.
Por qué importa
Ignorar estos breves errores de despliegue crea un impuesto oculto sobre la velocidad de ingeniería y la fiabilidad del sistema. Los equipos a menudo toleran pequeños fallos, lo que lleva a despliegues menos frecuentes y lotes de lanzamiento más grandes y arriesgados. Esta vacilación ralentiza los bucles de retroalimentación y dificulta las revertidas. Además, los reintentos del lado cliente provocados por errores 502 aleatorios pueden duplicar acciones no idempotentes, como crear múltiples pedidos o cobrar a clientes dos veces, convirtiendo un fallo técnico en un incidente empresarial.
Para líderes de TI e ingenieros DevOps, estas alertas inducidas por ruido degradan la eficacia de la monitorización. Cuando incidentes reales quedan sepultados bajo fallos rutinarios de despliegue, los equipos relajan los umbrales de alerta, reduciendo su capacidad para detectar problemas genuinos. Garantizar despliegues sin tiempo de inactividad restaura la confianza en el proceso de lanzamiento, permitiendo a los equipos publicar cambios más pequeños con mayor frecuencia sin miedo a interrumpir a los usuarios o corromper datos.
Qué puedes hacer
- Implementa endpoints separados
/healthzy/readyz, asegurando que las comprobaciones de vida permanezcan ligeras e independientes de dependencias externas. - Configura tu servidor Node.js para escuchar SIGTERM, establecer una bandera para rechazar nuevas solicitudes y esperar el retraso de desregistro del balanceador de carga antes de cerrar conexiones.
- Establece
server.keepAliveTimeouten un valor que supere el tiempo de inactividad de tu balanceador de carga, como 65 segundos para AWS ALB. - Usa la forma exec
CMD ["node", "server.js"]en tu Dockerfile o incluye un proceso init para asegurar que SIGTERM llegue a tu aplicación. - Cierra los pools de bases de datos y consumidores de trabajos en segundo plano solo después de que el servidor HTTP haya confirmado que todas las solicitudes en curso están completas.
- Valida tu configuración ejecutando pruebas de carga durante un reinicio progresivo, apuntando a cero respuestas no-2xx durante todo el proceso.



