Como atualizar um serviço sem acordar os usuários?
Imagine: sexta-feira, à noite, você decide aplicar rapidamente uma correção de bug na produção. Você clica em deploy — e seu mensageiro, streaming de cinema ou CRM fica fora do ar por 30 segundos. Milhares de usuários veem um 502 Bad Gateway nesse momento. E se for um gateway de pagamento? Calcule os prejuízos.
A implantação zero-downtime (implantação com tempo de inatividade zero) não é um luxo, mas um padrão para serviços TypeScript modernos. Neste artigo, vamos abordar técnicas reais usadas em produção: desde graceful shutdown até implantação azul-verde e rolling update. Você terá padrões prontos para aplicar em TypeScript.
Por que a implantação comum de uma aplicação TypeScript é perigosa?
Quando você executa npm run build && node dist/index.js, vários problemas ocorrem:
- Quebra de conexões — o processo antigo é encerrado enquanto novas requisições ainda estão chegando.
- Perda de dados na memória — sessões não salvas, cache, filas.
- Inicialização suja — o novo processo não teve tempo de aquecer, mas já está recebendo tráfego.
No TypeScript (Node.js), a assincronicidade agrava a situação: você não sabe quais promises ainda estão sendo executadas. Mas existe solução.
Técnica #1: Graceful Shutdown (encerramento gracioso)
A base do zero-downtime é o tratamento correto dos sinais do SO. Quando o orquestrador (Kubernetes, PM2) decide parar seu serviço, ele envia SIGTERM. Seu código deve parar de aceitar novas requisições, aguardar a conclusão das atuais e só então ser encerrado.
Implementação em TypeScript com 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' });});
// Sinal do Docker/K8sprocess.on('SIGTERM', async () => { console.log('SIGTERM recebido, iniciando graceful shutdown...'); // 1. Para de ouvir novas conexões server.close(() => { console.log('Servidor HTTP fechado'); // 2. Fecha conexões com banco de dados, Redis, etc. // await db.disconnect(); process.exit(0); });
// 3. Dá tempo para as requisições atuais (timeout de 30 seg) setTimeout(() => { console.error('Encerramento forçado por timeout'); process.exit(1); }, 30000);});
const PORT = process.env.PORT || 3000;server = app.listen(PORT, () => { console.log(`Serviço iniciado na porta ${PORT}`);});Importante: em TypeScript, use http.Server do módulo http, não o tipo Express. O método server.close() lida corretamente com conexões keep-alive.
Técnica #2: Rolling Update (atualização gradual)
Se você tem várias réplicas do serviço (3-5 instâncias em execução), o rolling update as atualiza uma por uma. Enquanto uma é reiniciada, as outras recebem o tráfego.
Exemplo para Kubernetes Deployment
# deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata: name: my-typescript-servicespec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 # quantos pods podem ficar indisponíveis maxSurge: 1 # quantos novos pods podem ser criados além do plano 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: 5Adicione ao seu aplicativo TypeScript o endpoint /ready, que retorna 200 apenas quando o serviço