Déploiement blue-green, zéro downtime en temps réel
Comment nous avons mis en œuvre le déploiement Blue-green dans le contexte d’une application collaborative en temps réel.
WorkAdventure est une plateforme de collaboration en temps réel où les utilisateurs rejoignent des espaces virtuels pour interagir. Contrairement à une application web classique, les utilisateurs peuvent rester connectés pendant des heures, avec des flux audio et vidéo continus et un état partagé en temps réel (notamment les positions des joueurs). Cela modifie ce que l’on entend par « safe deployment » : même de brèves interruptions sont immédiatement visibles, et les situations de « split brain » (deux versions du même monde fonctionnant en même temps) peuvent casser l’expérience utilisateur.
Cet article explique comment nous avions l’habitude de déployer, pourquoi les modèles de déploiement Kubernetes courants ne fonctionnaient pas pour nous, quelles alternatives nous avons explorées (y compris une que nous avons abandonnée), et la stratégie Blue Green que nous avons construite pour atteindre des déploiements sans temps d’arrêt délibéré.
Comment nous effectuions les déploiements
Historiquement, le déploiement d’une nouvelle version impliquait la mise hors ligne du service pendant une courte période, l’application de la mise à jour, puis le rétablissement de l’ensemble. Tous les utilisateurs étaient déconnectés pendant cette période.
Nous avons cherché longuement à réduire la durée de ce temps d’arrêt de la plateforme. Nous sommes passés de déploiements basés sur Docker à Kubernetes, et nous avons optimisé notre procédure de mise à jour. Nous avons notamment réduit le temps d’arrêt d’environ 5 minutes à environ 20 secondes en nous assurant que les images des conteneurs étaient déjà présentes sur chaque nœud Kubernetes avant le basculement. Kubernetes ne fournit pas de préchargement d’image en natif, mais nous avons obtenu un effet similaire en utilisant un modèle DaemonSet approprié pour extraire les images à l’avance.
Cependant, pour un produit en temps réel comme WorkAdventure, même 20 secondes sont perturbantes : les utilisateurs sont déconnectés, les flux audio et vidéo sont interrompus, et les réunions ou les événements sont interrompus. C’est aussi la raison pour laquelle le pourcentage de temps de fonctionnement seul (Uptime SLA) est un mauvais indicateur pour les systèmes en temps réel. Voir notre article pour plus de détails : https://workadventu.re/tech/uptime-percentage-is-bad-sla-for-real-time-apps/
Pour limiter l’impact sur les utilisateurs, nous avons programmé les déploiements en dehors des heures de pointe (la nuit ou le week-end). Au cours des dernières années, à mesure que l’utilisation de WorkAdventure s’est mondialisée, cette approche a cessé de s’adapter : il n’y a plus de période creuse. Lorsque les utilisateurs européens dorment, les utilisateurs américains sont actifs, et lorsque les Américains se reposent, l’Asie commence sa journée de travail.
Pourquoi les déploiements Blue-green sont nécessaires et pourquoi il nous est difficile d’y parvenir
Le déploiement bleu-vert (blue/green) est une stratégie courante pour réduire les temps d’arrêt en maintenant deux environnements de production :
- Bleu, actuellement en service
- Vert, inactif et prêt à recevoir la prochaine version
Une nouvelle version est déployée dans l’environnement inactif, validée, puis le trafic est transféré de l’environnement réel vers l’environnement mis à jour.
Pour de nombreuses applications web, ce changement de trafic peut être progressif (un pourcentage à la fois) en utilisant des cookies ou des en-têtes. Ce modèle est risqué pour WorkAdventure car les utilisateurs doivent rester dans un état partagé cohérent. Si deux utilisateurs sont « dans le même monde » mais sont dirigés vers des versions différentes, ils ne partagent plus le même état de backend en temps réel et ne verront pas la même réalité (scénario « Split brain »).
La contrainte technique essentielle est donc la suivante :
Un monde doit être desservi par une seule version à la fois.
Cette exigence est en contradiction avec la plupart des mécanismes de déploiement qui divisent le trafic par session d’utilisateur.
Déploiement Blue green dans les applications web classiques
Dans une application web classique, un déploiement blue-green consiste à :
- Déployer la nouvelle version dans l’environnement inactif
- Le valider (contrôles de santé, tests de fumée, tests fonctionnels)
- Modifier progressivement le trafic (par exemple 10 %, puis 50 %, puis 100 %).
- Revenir rapidement en arrière si nécessaire
Ceci est généralement mis en œuvre en marquant une session utilisateur (souvent via un cookie) et en laissant le load balancer acheminer les requêtes en conséquence.

Cela ne convient hélas pas à WorkAdventure car nous ne pouvons pas avoir des utilisateurs dans le même monde répartis sur deux versions.
Le déploiement blue-green dans les serveurs de jeux basés sur des sessions et pourquoi il ne correspond pas à WorkAdventure
WorkAdventure étant plus proche d’un jeu que d’une application web classique, nous avons également étudié la manière dont les serveurs de jeux gèrent les mises à jour.
Dans de nombreux jeux qui ne sont pas des MMO, les sessions sont de courte durée. Les joueurs rejoignent un match, le match se termine, tout le monde se déconnecte et le serveur se vide. Blue Green est simple dans ce cas :
- arrêter d’envoyer de nouvelles sessions à l’ancien serveur
- attendre que le serveur soit vide
- le mettre à jour et le remettre en rotation
Les plateformes basées sur Kubernetes comme Agones existent spécifiquement pour ce modèle de cycle de vie du match.
WorkAdventure ne correspond pas à ce modèle sur un certain nombre de points critiques :
- les utilisateurs peuvent rester connectés pendant des heures
- un « monde » a une longue durée de vie, pas une courte durée de vie
- l’exécution d’un processus dédié par monde serait trop gourmande en ressources et trop complexe dans notre architecture
Nous avions donc besoin d’une approche différente qui préserve la cohérence tout en permettant un déploiement progressif.
Approche abandonnée : partage de l’état du monde entre le bleu et le vert
Notre plus grand obstacle est que WorkAdventure maintient l’état en temps réel d’un monde à l’intérieur d’un service unique. Conceptuellement, ce service est la seule source de vérité pour l’état du monde (notamment les positions des joueurs). Si nous dupliquions simplement l’environnement, nous nous retrouverions avec deux sources de vérité distinctes, ce qui est exactement ce que nous devons éviter.
L’une des idées était d’externaliser l’état du monde dans une base de donnée adaptée aux charges de travail en temps réel, tel que Redis, et de faire en sorte que Blue et Green lisent et écrivent le même état partagé.

Cela aurait permis de réduire la contrainte de « l’instance unique de backend », mais cela a soulevé deux problèmes majeurs pour WorkAdventure :
- Contraintes de compatibilité entre les versions : pendant le déploiement, les deux versions devraient lire et écrire la même structure d’état en toute sécurité, de la même manière que les schémas de base de données doivent rester compatibles pendant les transitions classiques bleu-vert.
- Risque lié aux performances : WorkAdventure partage des mises à jour de positions plusieurs fois par seconde et peut produire des dizaines de milliers d’écritures par seconde dans un monde étendu. L’introduction d’un saut de réseau vers un magasin de données pour chaque mise à jour (ainsi que les lectures correspondantes) ajouterait de la latence et de la complexité opérationnelle. Même avec un datastore rapide, ce budget de latence est extrêmement serré pour une expérience en temps réel fluide.
En raison de ces risques, nous avons renoncé à cette option.
Approche choisie : définition des champs d’application et répartition du trafic par champ d’application
Au lieu de diviser le trafic par session d’utilisateur, nous le divisons en fonction de l’étendue de l’interaction.
Un scope est un domaine dans lequel les utilisateurs peuvent interagir en temps réel et doivent donc partager la même version.
Nous avons d’abord considéré chaque salle comme un champ d’application, car les utilisateurs d’une même salle partagent un état en temps réel (positions, groupes audio de proximité, salles de réunion). Les salles ont également des URL distinctes, ce qui rendrait le routage simple.
Dans la pratique, les scopes ont dû être élargis :
- les utilisateurs peuvent passer d’une pièce à l’autre en toute transparence dans le même monde
- les utilisateurs peuvent voir qui est en ligne dans le monde entier grâce à une structure d’état partagée (les « spaces » de WorkAdventure) qui s’étend sur plusieurs salles
C’est pourquoi nous considérons les « worlds » comme le bon champ d’application de scope (un world est un ensemble de cartes utilisées par un même client).
Pourquoi des URL plutôt que des cookies ?
Les cookies sont liés à une session de navigation. Un même utilisateur peut ouvrir deux onglets connectés à deux mondes différents. Le routage basé sur les cookies couplerait ces onglets d’une manière qui ne correspond pas à nos scopes.
Les URL sont déjà organisées par scope.
L’URL d’une salle typique se présente comme suit :
https://play.workadventu.re/@/[organisation]/[monde]/[salle]
Pour les déploiements en marque blanche :
https://[domaine personnalisé]/@/[salle]
En mode marque blanche, un domaine personnalisé correspond à un et un seul monde. Nous disposons ainsi d’une clé d’accès directe à l’espace de travail :
- soit le segment
[organisation]/[monde]. - ou le domaine personnalisé
Architecture résultante
À un niveau élevé, nous ne dupliquons que les composants qui sont directement impliqués dans le trafic en temps réel, et nous gardons les composants de stockage et d’administration partagés communs.

Dupliqué par environnement (blue et green) :
- le frontend qui sert le client
- les services backend en temps réel qui gèrent les mondes
- les services de stockage de cartes nécessaires à la version en cours d’exécution
Partagé entre les environnements :
- les composants qui ne sont pas directement sur le chemin du temps réel, tels que l’API d’administration et la couche de stockage
Cette répartition permet de réduire les coûts tout en préservant une cohérence stricte à l’intérieur d’un monde : à tout moment, un monde est entièrement sur le bleu ou entièrement sur le vert.
Détails de la mise en œuvre
Gestion des URL internes
Une application web n’est pas seulement composée des URL auxquels les utilisateurs accèdent directement.
Il renvoie également à de nombreux URL internes :
- Appels API
- Points d’extrémité WebSocket
- les actifs statiques (JavaScript, CSS, images)
Si un monde est routé vers Blue, toutes les URL internes doivent également être résolues vers l’environnement Blue. Pour ce faire, nous disposons de noms d’hôtes internes spécifiques à l’environnement et nous nous assurons que la page servie pointe vers ceux qui correspondent à sa couleur.
En d’autres termes, l’URL d’entrée publique reste stable, mais l’application peut cibler des points de terminaison internes spécifiques à l’environnement une fois que le routage a choisi une couleur pour ce champ d’application.
Gestion des rappels d’authentification de l’OIDC
WorkAdventure prend en charge l’authentification via OpenID Connect.
Avec l’OIDC, les utilisateurs sont redirigés vers le fournisseur d’identité, puis renvoyés vers un URI de redirection prédéfini qui doit être enregistré à l’avance. Dans de nombreuses configurations, cet URI de redirection doit se trouver sur le même domaine principal que l’application, ce qui empêche l’utilisation d’un nom d’hôte spécifique à l’environnement pour le rappel.
Nous avons résolu ce problème en encodant l’environnement dans le chemin de rappel du domaine principal :
-https://play.workadventu.re/blue/callback-https://play.workadventu.re/green/callback
Cela crée un cas spécial de routage : les rappels atterrissent sur le domaine principal stable, mais le chemin détermine l’environnement qui doit compléter le flux de connexion.
Effectuer le changement en toute sécurité
Nous voulons que le chemin par défaut se déplace vers le nouvel environnement, tout en conservant chaque monde existant dans l’ancien environnement jusqu’à ce qu’il soit possible de basculer en toute sécurité.
Notre procédure de commutation est la suivante :
- Déployer la nouvelle version dans l’environnement inactif (exemple : Green).
- Veiller à ce que chaque monde existant soit explicitement acheminé vers l’environnement réel actuel (exemple : Bleu).
- Modifier le routage par défaut pour que tout monde sans règle explicite aille vers Green.
- Pour chaque monde, une fois qu’il est possible de passer d’un monde à l’autre en toute sécurité, il faut supprimer la règle bleue explicite afin de revenir à la nouvelle règle par défaut (verte).
Cela nécessite un composant d’orchestration qui comprenne les mondes, les activités et les règles de routage.
Pourquoi nous avons abandonné Ingress et adopté l’API Kubernetes Gateway ?
Initialement, le routage était géré via les ressources Ingress de Kubernetes. Ingress est pratique pour les configurations simples, mais il tend à concentrer une grande partie de la logique de routage dans un petit nombre de ressources importantes, souvent une par domaine.
À notre échelle, changer de monde un par un signifierait mettre à jour une très grande ressource Ingress de manière répétée. Nous avons généralement des milliers de mondes qui tournent en même temps. Cela pose deux problèmes :
- la ressource devient énorme et lourde à gérer sur le plan opérationnel
- Les mises à jour peuvent provoquer des recharges de routage qui affectent brièvement des mondes non liés, exactement ce que nous voulons éviter.
Pour y remédier, nous avons cessé de nous appuyer sur Ingress pour ce flux de travail et déplacé le routage vers l’API de passerelle Kubernetes. Avec Gateway API, les règles de routage peuvent être définies de manière plus granulaire, en tant que ressources d’itinéraire distinctes. Cela signifie que le changement d’un monde peut être réduit à la mise à jour ou à la suppression d’une petite ressource de route, sans avoir à tout recharger.
Ce changement était nécessaire pour permettre une orchestration du routage fine et à faible impact.
Le composant d’orchestration et l’heuristique d’activité
Le composant d’orchestration gère les règles de routage et décide quand un monde peut être commuté. Ses responsabilités sont les suivantes :
- épingler chaque monde à l’environnement en cours au début d’un déploiement
- basculer la route par défaut vers le nouvel environnement
- surveiller l’activité des mondes et épingler les mondes lorsqu’ils sont considérés comme pouvant migrer en toute sécurité
Le plus difficile est de déterminer si la migration est disruptive ou non pour un monde donné.
Dans WorkAdventure, un monde peut rester non vide pendant très longtemps parce que les gens laissent des onglets ouverts et gardent leur avatar connecté pendant des jours. Du point de vue du routage, ce monde n’est jamais vide, mais sa mise à jour est généralement peu risquée si personne n’interagit activement.
Nous avons donc mis en place une heuristique pour approcher le « vide effectif » :
- Si un monde n’a qu’un seul utilisateur, il est considéré comme vide (aucune interaction en temps réel avec d’autres personnes n’est possible).
- Si un monde a plusieurs utilisateurs, mais qu’aucun n’est dans une bulle de conversation ou une salle de réunion, et que le nouvel environnement fonctionne depuis plus de 4 heures, le monde est considéré comme vide.
- Si un monde a plusieurs utilisateurs, mais que le nombre actuel d’utilisateurs est inférieur à 10 % du maximum observé depuis le début du nouvel environnement, le monde est considéré comme vide.
- Enfin, si le nouvel environnement fonctionne depuis plus de 24 heures, le monde est considéré comme vide.
Grâce à ces règles, chaque monde finit par migrer dans les 24 heures sans qu’il soit nécessaire d’interrompre l’ensemble de la plate-forme.
Limites et travaux futurs
Cette stratégie permet des déploiements sans temps d’arrêt délibéré au niveau de la plateforme, tout en garantissant que chaque monde reste cohérent sur une seule version à tout moment. Elle améliore également la flexibilité opérationnelle : nous n’avons plus besoin de réaliser des mises à jour uniquement la nuit ou le week-end.
Cependant, il y a des limites :
- Avec seulement deux environnements, nous ne pouvons pas commencer à déployer une deuxième nouvelle version tant que le déploiement précédent n’est pas entièrement terminé.
- Les correctifs d’urgence sont plus difficiles à mettre en œuvre une fois que la mise en production enclenchée, car pour revenir en arrière ou apporter des correctifs, il faut soit attendre la fenêtre de déploiement, soit forcer des changements qui risquent de déconnecter certains utilisateurs.
La prochaine étape naturelle consisterait à explorer les déploiements de type Canary. L’adaptation de Canary à un système d’état partagé en temps réel soulèverait des défis similaires à ceux de Blue Green, notamment en ce qui concerne la définition des scopes, la cohérence et les triggers de migration.
Références :