Pourquoi votre service ne devrait-il pas « faire la sieste » à l'heure du déjeuner ?
Imaginez : vendredi, 18h45. Vous venez de lancer la mise à jour tant attendue de votre service C#. Et immédiatement, les messages fusent dans le chat du support : « le site ne marche pas », « erreur 503 », « j'ai perdu les données de mon panier ». Cela vous dit quelque chose ? C'est le scénario classique où un déploiement « à l'arrache » tue l'expérience utilisateur. Dans le monde moderne, où chaque heure d'indisponibilité peut coûter des millions, le déploiement zero-downtime des services C# n'est pas un luxe, mais une hygiène de base du développement.
Dans cet article, nous n'allons pas faire de cours ennuyeux. Nous allons analyser de vraies stratégies de mise à jour d'applications .NET sans interruption de service, écrire du code pour les health checks et le graceful shutdown, et montrer comment configurer un rolling update dans Kubernetes. Si vous en avez assez des « chutes magiques » de la production après chaque commit, ce contenu est pour vous. C'est parti !
Qu'est-ce que le zero-downtime deployment et pourquoi en tant que développeur C# ?
Zero-downtime deployment (déploiement avec zéro temps d'arrêt) est une méthode de mise à jour d'application où l'ancienne version continue de traiter les requêtes jusqu'à ce que la nouvelle version soit entièrement opérationnelle. L'utilisateur ne remarque pas la bascule : ni erreurs, ni timeouts, ni perte de session.
Problèmes principaux lors de la mise à jour des services C#
- Le graceful shutdown ne fonctionne pas toujours. Le
app.StopAsync()standard peut interrompre les requêtes actives si les timeouts ne sont pas configurés. - Connexions bloquées. L'ancienne version maintient des WebSocket ouverts ou des requêtes HTTP longues.
- Migrations de base de données. La nouvelle version nécessite des changements de schéma incompatibles avec l'ancienne.
- Cache et sessions. Après la bascule, l'utilisateur perd les données en mémoire.
Bonne nouvelle : C# et l'écosystème .NET fournissent tous les outils pour résoudre ces problèmes. Notre tâche est de les assembler en un système fonctionnel.
Stratégie n°1 : Blue-Green Deployment pour C#
C'est le schéma le plus simple et le plus fiable. Vous avez deux environnements identiques : Blue (version actuelle) et Green (nouvelle version). Vous déployez la nouvelle version sur Green, exécutez des smoke-tests, puis basculez instantanément le load balancer de Blue vers Green. Si quelque chose ne va pas, vous revenez en arrière en une seconde.
Comment le mettre en œuvre pour .NET ?
Nous aurons besoin d'un load balancer (NGINX, HAProxy ou un Load Balancer cloud) et de deux instances de votre service C#. Exemple de configuration NGINX :
upstream backend { server blue-server:5000 weight=100; server green-server:5000 weight=0; # Backup}
server { listen 80; location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; }}Après le déploiement de la version Green, changez les poids : blue:0, green:100. Les utilisateurs passent imperceptiblement à la nouvelle version.
Avantages : simplicité, retour en arrière instantané.
Inconvénients : nécessite deux fois plus de ressources (serveurs).
Stratégie n°2 : Rolling Update avec Kubernetes (idéal pour les microservices)
Kubernetes sait faire un rolling update « prêt à l'emploi ». Il remplace progressivement les anciens Pods par de nouveaux, sans interrompre le trafic. Mais pour que cela fonctionne correctement avec C#, il faut configurer les health probes et le graceful shutdown.
Health Checks dans .NET 6/8
Ajoutez dans votre Program.cs un endpoint pour la vérification de disponibilité (readiness) et de vivacité (liveness) :
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks() .AddCheck<DatabaseHealthCheck>("database") .AddCheck<CacheHealthCheck>("redis");
var app = builder.Build();
app.MapHea