L'incident VPN début décembre : une analyse rétrospective

TL;DR : la version courte

Le 2 décembre 2025, nous avons reçu une alerte indiquant qu'un composant système critique (Redis) était à court d'espace disque. La résolution du problème de capacité en soi était simple. Les vrais problèmes ont commencé lorsque nous avons essayé de « faire le ménage » après coup et d'adapter la configuration à la réalité.

Comme ce composant fonctionne sous Kubernetes et est configuré de manière à ne pas permettre un ajustement simple des ressources, nous avons tenté une solution de contournement qui semblait sûre : recréer l'objet contrôleur sans supprimer les pods en cours d'exécution. Kubernetes a interprété ce changement différemment de ce que nous avions prévu et a redémarré l'ensemble du cluster Redis. Le processus de redémarrage n'était pas assez « prudent », il s'est déroulé trop rapidement et a entraîné une dégradation partielle du cluster. Pendant que nous réparions les nœuds dégradés, un service backend a été submergé par le trafic de reconnexion et a commencé à rencontrer des erreurs de mémoire insuffisante. Après une mise à l'échelle et un réglage, le service a été rétabli. Le temps d'indisponibilité total a été d'environ 50 minutes.

Ce qui с'est en fait passé

Nous avons reçu une alerte indiquant que plusieurs instances du cluster Redis étaient à court d'espace disque disponible. Mais dans notre cas, augmenter l'espace disque pose certains problèmes. En effet, dans Kubernetes, les éléments tels que Redis sont généralement créés via un StatefulSet. Celui-ci est spécialement conçu pour les charges de travail qui nécessitent des identités et un stockage stables.

Cependant, certaines parties du StatefulSet sont verrouillées, ce qui peut poser problème. Les StatefulSets définissent le stockage via des volumeClaimTemplates. Quelques détails importants :

  • le modèle de stockage est effectivement verrouillé une fois créé
  • vous ne pouvez pas simplement le modifier sur place pour augmenter la taille du disque

Ainsi, même si vous savez exactement ce que vous voulez, Kubernetes ne vous permettra pas de modifier ce champ spécifique dans la spécification StatefulSet.

Un correctif immédiat : augmentation de capacité sans réinitialisation

Notre stockage interne prend en charge l'extension de volume en ligne. Cela signifie que nous pouvons augmenter la capacité du disque sous un pod en cours d'exécution sans le redémarrer.

Nous avons donc redimensionné directement les PersistentVolumeClaims (PVC). Cela a permis de résoudre le problème de surcharge du disque rapidement et en toute sécurité.

Mais cela a créé un nouveau problème :

  • la configuration déclarative (Git + modèle StatefulSet) indiquait toujours l'ancienne taille du disque
  • l'état réel des PVC/PV dans le cluster reflétait désormais la nouvelle taille du disque

Cette incompatibilité est un exemple classique de « dérive de configuration ».

La tentative de « nettoyage » : ajuster le code à la réalité

Le modèle de stockage StatefulSet étant immuable, la seule façon d'aligner l'état déclaré avec l'état réel consiste à :

  1. Supprimer l'objet StatefulSet.
  2. Le recréer avec le modèle de stockage mis à jour.

Bien sûr, la suppression du StatefulSet entraîne généralement la suppression des ressources secondaires. Nous avons donc utilisé une astuce Kubernetes :

Suppression orpheline : « licencier le responsable, maintenir l'usine opérationnelle »

Nous avons supprimé le StatefulSet à l'aide d'une suppression orpheline / non en cascade. L'idée était la suivante :

  • Kubernetes oublie l'objet « responsable »...
  • ... mais les pods (et leurs disques) restent actifs.

Nous avons ensuite recréé le StatefulSet afin que Git et le cluster soient à nouveau cohérents.

Cela semblait sûr, mais Kubernetes en a eu une interprétation différente.

L'imprévu : Kubernetes a redémarré l'ensemble du cluster Redis

Après avoir recréé le StatefulSet, Kubernetes a réconcilié la charge de travail en fonction des nouvelles spécifications. Même si les disques avaient déjà été redimensionnés, Kubernetes « ignorait » que toutes les modifications avaient déjà été appliquées. Il a donc lancé le processus de réconciliation, ce qui a entraîné le redémarrage complet du cluster Redis.

Un cluster de bases de données/caches distribué peut survivre à une séquence de redémarrage, mais uniquement si celui-ci est effectué avec prudence.

Cela nous amène au plus grand amplificateur de cet incident.

## Pourquoi la situation s'est aggravée : les redémarrages étaient trop optimistes.

Notre comportement de redémarrage n'était pas assez prudent :

  • aucune vérification du démarrage et de la disponibilité
  • un pod simplement « en cours d'exécution » était considéré comme « suffisamment opérationnel » (alors qu'en réalité, il ne pouvait toujours pas gérer la charge réelle des requêtes)
  • Kubernetes passait trop rapidement au pod suivant

En clair : notre processus de redémarrage n'attendait pas vraiment que Redis soit en état de marche avant de passer à l'étape suivante.

Au début, cela ne semblait pas catastrophique : le service backend se plaignait des connexions Redis, mais les graphiques globaux du service ne s'effondraient pas immédiatement.

Cluster Redis dégradé : les répliques sont restées « bloquées » sur les anciens maîtres

Une fois la situation stabilisée, le cluster Redis s'est retrouvé partiellement dégradé :

  • certains nœuds répliques ne pouvaient pas se reconnecter correctement
  • ils conservaient des informations obsolètes sur le maître auquel ils appartenaient

À ce stade, la récupération nécessitait des opérations manuelles sur le cluster Redis :

  • oublier les identités de nœuds obsolètes sur les maîtres
  • réinitialiser les répliques affectées
  • les reconnecter au cluster et attendre que la réplication rattrape son retard

Cette procédure a pris plus de temps que prévu. Et alors qu'elle était encore en cours, nous avons rencontré une deuxième défaillance.

Impact secondaire : surcharge du service backend et pannes dues à un manque de mémoire

Pendant que les nœuds Redis se reconnectaient et se synchronisaient, le service backend a été confronté à une avalanche de reconnexions et à une charge élevée. Cela s'est traduit par une cascade de pannes dues à un manque de mémoire.

Deux facteurs ont aggravé la situation :

  • nous avons initialement mal identifié le composant qui était réellement en manque de mémoire
  • la mise à l'échelle du service backend a été retardée en raison de la capacité de réserve limitée dans le cadre des contraintes actuelles en matière de ressources

Une fois le composant défaillant correctement identifié, nous avons stabilisé le système en :

  • adaptant le service backend
  • augmentant les limites de mémoire pour le composant concerné
  • ... et en assouplissant temporairement les sondes pour permettre aux instances d'entrer immédiatement dans le service afin de faciliter la récupération. Sinon, cela aurait eu un effet en cascade : une fois que le contrôle du pod indique qu'un pod est « prêt », celui-ci est rapidement surchargé de trafic et redevient « non prêt ». Cela augmente la charge sur le pod suivant, la situation se répète, et ainsi de suite.

Après cela, le service s'est progressivement rétabli et le trafic des clients est revenu à la normale. La durée totale de l'indisponibilité a été d'environ 50 minutes : 8 h 10 UTC - 9 h 00 UTC.

La chronologie des événements

7 h 00 UTC : augmentation de la taille du disque dans Git.
7 h 05 UTC : mise à jour manuelle de la taille du disque pour tous les PVC du cluster.
7 h 10 UTC : tous les pods ont terminé le redimensionnement du disque en ligne.
8 h 10 UTC : suppression et recréation des sts orphelins.
8 h 10-8 h 15 UTC : Kubernetes a rapidement effectué un redémarrage complet de StatefulSet. C'est à ce moment-là que les problèmes ont commencé à apparaître.
8 h 15-8 h 35 UTC : restauration manuelle du cluster — recréation et réaffectation des répliques.
8 h 35-8 h 50 UTC : traitement des erreurs OOM de l'application. Une fois ces problèmes résolus, le service a été rétabli avec certaines limitations (réponses lentes, par exemple).
10 h 00 UTC : les solutions de contournement temporaires utilisées pour le redémarrage d'urgence du service ont été annulées. Le service est entièrement rétabli et opérationnel.

Les leçons apprises

1) Ce n'est pas le redimensionnement du disque qui a causé l'incident
La correction de la capacité était simple. L'incident a commencé avec l'étape d'alignement d'état et la recréation du contrôleur.

2) Les champs immuables vous poussent vers des workflows risqués
Lorsque le système ne vous permet pas de modifier un champ, la solution consiste souvent à le remplacer, ce qui peut déclencher un comportement de réconciliation à grande échelle.

3) Les sondes de démarrage et la disponibilité stricte ne sont pas seulement « agréables à avoir »
Elles constituent les freins d'un redémarrage en cours. Sans elles, les systèmes distribués redémarrent de la manière la plus fragile possible. C'est la pire partie de cet incident : la présence de sondes aurait empêché Kubernetes de redémarrer le pod suivant tant que le pod actuel n'était pas vraiment prêt.

4) Les plans de reprise doivent tenir compte d'une réparation lente du cluster
Les reconnexions et les rattrapages de réplication prennent du temps. Les services dépendants doivent être conçus pour se dégrader progressivement pendant cette période.

## Que va-t-on faire ?

1) Mettre en place des sondes de démarrage manquantes
Des sondes de démarrage simples telles que « ne pas marquer le pod comme « prêt » tant que l'ensemble de données n'est pas chargé » permettront d'éviter que cette situation ne se reproduise.

2) Créer notre propre « plan de contrôle » pour Redis
C'est une tâche plus complexe, mais elle s'avère très utile dans une situation où un pod redémarré est « bloqué » sur une ancienne adresse IP maître.

3) Réduire la dépendance à l'égard de ce cluster Redis
À l'heure actuelle, il semble que le cluster Redis soit le SPOF (point de défaillance unique) de l'ensemble du service. Nous prévoyons donc de migrer certaines informations importantes vers d'autres sources de données, comme un cluster Redis distinct pour chaque serveur. Nous prévoyons également de commencer à utiliser Kafka pour certains composants du backend.

Vous avez aimé cet article ?
9 332 9332 avis
Excellent !

AdGuard VPN
pour Windows

Utilisez un navigateur ou une application de votre choix et ne vous souciez plus jamais de votre anonymat. Le monde entier est à la portée de votre main avec AdGuard VPN.
Télécharger
En téléchargeant le programme, vous acceptez les termes des Conditions générales d'utilisation
En savoir plus
9 332 9332 avis
Excellent !

AdGuard VPN
pour Mac

En deux clics, sélectionnez une ville dans n'importe quel coin du monde — nous offrons 80+ localisations — et vos données seront cachées des regards indiscrets.
Télécharger
En téléchargeant le programme, vous acceptez les termes des Conditions générales d'utilisation
En savoir plus
9 332 9332 avis
Excellent !

AdGuard VPN
pour Android

Restez anonyme où que vous alliez avec AdGuard VPN ! Des dizaines d'emplacements, une connexion rapide et fiable - le tout dans votre poche.
Google Play
En téléchargeant le programme, vous acceptez les termes des Conditions générales d'utilisation
En savoir plus
Télécharger
En téléchargeant le programme, vous acceptez les termes des Conditions générales d'utilisation
En savoir plus
9 332 9332 avis
Excellent !

AdGuard VPN
pour iOS

Renforcez votre protection en ligne en l'emportant avec vous partout où vous allez. Profitez de vos séries et émissions préférées avec AdGuard VPN !
App Store
En téléchargeant le programme, vous acceptez les termes des Conditions générales d'utilisation
En savoir plus
9 332 9332 avis
Excellent !

AdGuard VPN
pour Android TV

Découvrez AdGuard VPN pour Android TV ! Profitez d’un streaming fluide, d’une sécurité renforcée et d'une configuration facile.
Google Play
En téléchargeant le programme, vous acceptez les termes des Conditions générales d'utilisation
Télécharger
En téléchargeant le programme, vous acceptez les termes des Conditions générales d'utilisation
9 332 9332 avis
Excellent !

AdGuard VPN
pour Chrome

Cachez votre véritable emplacement et émergez d'un autre endroit dans le monde — accédez à n'importe quel contenu sans restrictions et préservez votre anonymat sur le web.
En savoir plus
Installer
En téléchargeant le programme, vous acceptez les termes des Conditions générales d'utilisation

AdGuard VPN
pour Edge

Rendez-vous dans un autre pays en un seul clic, gagnez l'accès au contenu restreint, faites de votre surfing du web une expérience extra sécurisée et anonyme.
En savoir plus
Installer
En téléchargeant le programme, vous acceptez les termes des Conditions générales d'utilisation

AdGuard VPN
pour Firefox

Protégez votre confidentialité, dissimulez votre véritable localisation, décidez où vous avez besoin du VPN et où non !
En savoir plus
Installer
En téléchargeant le programme, vous acceptez les termes des Conditions générales d'utilisation

AdGuard VPN
pour Opera

Soyez un ninja dans votre navigateur Opera : déplacez-vous rapidement dans n'importe quelle partie du monde et restez inaperçu.
En savoir plus
Installer
En téléchargeant le programme, vous acceptez les termes des Conditions générales d'utilisation
9 332 9332 avis
Excellent !

AdGuard VPN
pour les routeurs

Installez AdGuard VPN sur votre routeur pour sécuriser l'ensemble de votre réseau. Décidez quels appareils protéger et à quel moment
Cette option n'est disponible qu'avec un abonnement à AdGuard VPN
9 332 9332 avis
Excellent !

AdGuard VPN
pour Linux

Obtenez le meilleur VPN gratuit pour Linux et profitez d’une navigation web transparente, d’une sécurité améliorée, du cryptage du trafic Internet et d’une protection contre les fuites DNS. Choisissez parmi plusieurs serveurs VPN et accédez aux emplacements de votre choix
9 332 9332 avis
Excellent !

AdGuard VPN
pour Apple TV

Découvrez AdGuard VPN pour Apple TV ! Profitez d’un streaming fluide, d’une sécurité renforcée et d'une configuration facile
Cette option n'est disponible qu'avec un abonnement à AdGuard VPN
9 332 9332 avis
Excellent !

AdGuard VPN pour Xbox

Protégez votre Xbox avec AdGuard VPN et profitez de jeux en ligne fluides, d'une sécurité améliorée et d'une configuration facile
Cette option n'est disponible qu'avec un abonnement à AdGuard VPN
9 332 9332 avis
Excellent !

AdGuard VPN
pour PS4/PS5

Protégez votre PlayStation avec AdGuard VPN et profitez de jeux en ligne fluides, d'une sécurité améliorée et d'une configuration facile. Choisissez parmi plusieurs serveurs VPN et accédez aux emplacements de votre choix
Cette fonctionnalité est uniquement disponible avec un abonnement AdGuard VPN
9 332 9332 avis
Excellent !

AdGuard VPN
pour Chromecast

Installez AdGuard VPN sur votre Google TV (Chromecast Gen 4) ou sur votre routeur réseau (Chromecast Gen 3) et profitez du streaming de contenu avec Chromecast tout en restant anonyme en ligne et en accédant au contenu de n'importe où. Pour Chromecast Gen 3, vous avez besoin d'un abonnement AdGuard VPN
AdGuard VPN
téléchargement en cours
Cliquez le bouton indiqué par la flèche pour déclencher l'installation.
Analyser pour installer AdGuard VPN sur votre appareil mobile