Comment mettre à jour un service sans réveiller les utilisateurs ?
Imaginez : vendredi soir, vous décidez de déployer rapidement un correctif de bug en production. Vous cliquez sur deploy — et votre messagerie, votre cinéma en ligne ou votre CRM tombe en panne pendant 30 secondes. Des milliers d'utilisateurs voient à ce moment-là une erreur 502 Bad Gateway. Et si c'est une passerelle de paiement ? Calculez les pertes.
Le déploiement zero-downtime (déploiement avec zéro temps d'arrêt) n'est pas un luxe, mais un standard pour les services TypeScript modernes. Dans cet article, nous allons analyser les techniques réelles utilisées en production : du graceful shutdown au déploiement bleu-vert et au rolling update. Vous obtiendrez des patterns prêts à l'emploi en TypeScript.
Pourquoi un déploiement classique d'une application TypeScript est-il dangereux ?
Lorsque vous exécutez npm run build && node dist/index.js, plusieurs problèmes se produisent :
- Rupture des connexions — l'ancien processus est tué alors que de nouvelles requêtes arrivent encore.
- Perte de données en mémoire — sessions non sauvegardées, cache, files d'attente.
- Démarrage à froid — le nouveau processus n'a pas eu le temps de chauffer, mais reçoit déjà du trafic.
En TypeScript (Node.js), tout est aggravé par l'asynchronisme : vous ne savez pas quelles promesses sont encore en cours d'exécution. Mais il existe une solution.
Technique #1 : Graceful Shutdown (arrêt élégant)
La base du zero-downtime est le traitement correct des signaux du système d'exploitation. Lorsque l'orchestrateur (Kubernetes, PM2) décide d'arrêter votre service, il envoie SIGTERM. Votre code doit cesser d'accepter de nouvelles requêtes, attendre la fin des requêtes en cours, et seulement ensuite s'arrêter.
Implémentation en TypeScript avec 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' });});
// Signal de Docker/K8sprocess.on('SIGTERM', async () => { console.log('SIGTERM reçu, début du graceful shutdown...'); // 1. Arrêter d'écouter les nouvelles connexions server.close(() => { console.log('Serveur HTTP fermé'); // 2. Fermer les connexions à la BDD, Redis, etc. // await db.disconnect(); process.exit(0); });
// 3. Laisser du temps aux requêtes en cours (timeout 30 sec) setTimeout(() => { console.error('Arrêt forcé après timeout'); process.exit(1); }, 30000);});
const PORT = process.env.PORT || 3000;server = app.listen(PORT, () => { console.log(`Service démarré sur le port ${PORT}`);});Important : en TypeScript, utilisez http.Server du module http, et non le type Express. La méthode server.close() gère correctement les connexions keep-alive.
Technique #2 : Rolling Update (mise à jour progressive)
Si vous avez plusieurs répliques de votre service (3 à 5 instances en cours d'exécution), le rolling update les met à jour une par une. Pendant qu'une se redémarre, les autres acceptent le trafic.
Exemple pour un déploiement Kubernetes
# deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata: name: my-typescript-servicespec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 # combien de pods peuvent être indisponibles maxSurge: 1 # combien de nouveaux pods peuvent être créés en plus du 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: 5Ajoutez à votre application TypeScript un endpoint /ready qui retourne 200 uniquement lorsque le service