Despliegue Zero-downtime de servicios TypeScript: guía paso a paso

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Cómo actualizar un servicio sin despertar a los usuarios?



Imagine: viernes por la noche, decide subir rápidamente un bugfix a producción. Presiona deploy — y su mensajero, cine en línea o CRM se cae durante 30 segundos. Miles de usuarios en ese momento ven un 502 Bad Gateway. ¿Y si es una pasarela de pago? Calcule las pérdidas.



El despliegue Zero-downtime (implementación con tiempo de inactividad cero) no es un lujo, sino un estándar para los servicios TypeScript modernos. En este artículo, analizaremos técnicas reales utilizadas en producción: desde graceful shutdown hasta despliegue azul-verde y rolling update. Obtendrá patrones listos para usar en TypeScript.



¿Por qué es peligroso el despliegue normal de una aplicación TypeScript?



Cuando ejecuta npm run build && node dist/index.js, ocurren varios problemas:



  • Rotura de conexiones — el proceso antiguo se elimina mientras aún llegan nuevas solicitudes.
  • Pérdida de datos en memoria — sesiones no guardadas, caché, colas.
  • Inicio sucio — el nuevo proceso no ha tenido tiempo de calentarse y ya recibe tráfico.


En TypeScript (Node.js), todo se agrava por la asincronía: no sabe qué promesas se están ejecutando aún. Pero hay solución.



Técnica #1: Graceful Shutdown (finalización elegante)



La base del zero-downtime es el manejo correcto de las señales del SO. Cuando el orquestador (Kubernetes, PM2) decide detener su servicio, envía SIGTERM. Su código debe dejar de aceptar nuevas solicitudes, esperar a que finalicen las actuales y solo entonces terminar.



Implementación en TypeScript con Express



import express, { Request, Response } from 'express';import http from 'http';

const app = express();let server: http.Server;

app.get('/health', (req: Request, res: Response) => { res.status(200).json({ status: 'ok' });});

// Señal de Docker/K8sprocess.on('SIGTERM', async () => { console.log('SIGTERM recibido, iniciando graceful shutdown...'); // 1. Dejar de escuchar nuevas conexiones server.close(() => { console.log('Servidor HTTP cerrado'); // 2. Cerrar conexiones a BD, Redis, etc. // await db.disconnect(); process.exit(0); });

// 3. Dar tiempo a las solicitudes actuales (timeout de 30 seg) setTimeout(() => { console.error('Finalización forzada por timeout'); process.exit(1); }, 30000);});

const PORT = process.env.PORT || 3000;server = app.listen(PORT, () => { console.log(`Servicio iniciado en el puerto ${PORT}`);});


Importante: en TypeScript use http.Server del módulo http, no el tipo Express. El método server.close() maneja correctamente las conexiones keep-alive.



Técnica #2: Rolling Update (actualización gradual)



Si tiene varias réplicas del servicio (3-5 instancias ejecutándose), el rolling update las actualiza una por una. Mientras una se reinicia, las otras reciben el tráfico.



Ejemplo para Kubernetes Deployment



# deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata:  name: my-typescript-servicespec:  replicas: 3  strategy:    type: RollingUpdate    rollingUpdate:      maxUnavailable: 1      # cuántos pods pueden no estar disponibles      maxSurge: 1            # cuántos pods nuevos se pueden crear además del plan  template:    spec:      containers:      - name: app        image: myregistry/service:v2        ports:        - containerPort: 3000        livenessProbe:          httpGet:            path: /health            port: 3000          initialDelaySeconds: 5          periodSeconds: 10        readinessProbe:          httpGet:            path: /ready            port: 3000          initialDelaySeconds: 3          periodSeconds: 5


Agregue a su aplicación TypeScript un endpoint /ready que devuelva 200 solo cuando el servicio esté listo para recibir tráfico.

Blogs

Book Recommendations