Cloud et infrastructure

Comment des mécanismes Kubernetes valides peuvent déclencher des pannes du plan de contrôle

Les plans de contrôle Kubernetes sont confrontés à des chemins de défaillance cachés où des mécanismes valides tels que les informers, l'agrégation de schémas et les sondes de vivacité amplifient les défauts locaux en pannes systémiques.

Un cube en verre avec des couches de circuits lumineux représentant la complexité du plan de contrôle Kubernetes.
Illustration générée pour cet article

Traduit automatiquement depuis l'original anglais.

Les plans de contrôle Kubernetes sont conçus pour isoler les défaillances individuelles des composants, mais leur disponibilité globale reste vulnérable au comportement des composants critiques sous contrainte. Une analyse récente révèle qu'à l'échelle d'une flotte, un composant n'a pas besoin de planter pour provoquer une panne ; au lieu de cela, des mécanismes standards peuvent amplifier les défauts locaux via des métadonnées partagées ou des tâches de démarrage synchronisées.

Ce qui s'est passé

Trois cas spécifiques illustrent comment des fonctionnalités Kubernetes standard peuvent involontairement entraîner une instabilité du plan de contrôle. Dans le premier cas, un serveur API étendu a publié un schéma invalide suite à une mise à jour de dépendance. Cette erreur a bloqué des clients qui n'avaient jamais interagi avec l'API étendue. La défaillance s'est propagée via des métadonnées partagées, spécifiquement un document de schéma combinant les définitions de toutes les API enregistrées dans le cluster. Les utilisateurs tentant des déploiements Helm pour des charts contenant uniquement des ressources Kubernetes standard ont rencontré des erreurs de validation avant même que leurs requêtes n'atteignent le serveur API, malgré l'absence de lien avec l'extension défectueuse.

Le deuxième cas impliquait un agent DaemonSet redémarrant sur une grande flotte. Bien que destiné à être une mesure de récupération, le redémarrage progressif a transformé les tâches de démarrage routinières en une tempête de requêtes que le serveur API ne pouvait absorber. Chaque nouvelle instance d'agent reconstruisait sa vue locale de l'état du cluster en listant tous les Pods. Comme de nombreux agents démarraient simultanément, ils ont généré une montée en puissance synchronisée d'opérations de liste. Sur de grands clusters, ces requêtes renvoyaient des dizaines de milliers d'objets, épuisant la mémoire et le CPU du serveur API lors de la construction de réponses massives pour des milliers de clients concurrents.

Dans le troisième scénario, une vérification approfondie de la vivacité (liveness check) sur un composant situé dans le chemin de requête API traitait une panne de dépendance aval comme une défaillance du processus. La sonde redémarrait répétitivement le composant alors que la dépendance externe restait indisponible. Ce cycle a entraîné des redémarrages répétés sans résoudre le problème sous-jacent, interrompant le trafic API et jetant l'état mis en cache qui aurait pu maintenir une capacité de service partielle pendant la dégradation.

Comment cela fonctionne

Ces défaillances découlent de la manière dont Kubernetes expose les métadonnées API et gère la synchronisation de l'état. Kubernetes utilise des serveurs API agrégés pour étendre les fonctionnalités sans modifier le cœur du système. Pour les clients, ces extensions apparaissent natives. Le système expose les contrats de schéma sous deux formes : OpenAPI v2 et v3. OpenAPI v2 fournit un seul document combiné contenant les définitions du cœur et de toutes les API étendues. Cela crée un domaine de défaillance partagé où une définition invalide provenant d'une extension peut empêcher les clients d'analyser l'intégralité du document, bloquant l'accès à des ressources non liées. OpenAPI v3 sépare les schémas par groupe d'API, réduisant la portée de l'impact.

Les informers fournissent aux contrôleurs une vue locale de l'état du cluster, réduisant les requêtes répétitives vers le serveur API. Cependant, lorsque de nombreux contrôleurs redémarrent simultanément, la reconstruction de ces vues exerce une pression significative sur le plan de contrôle. Les informers utilisent souvent resourceVersion=0 pour lire depuis le cache de surveillance (watch cache) du serveur API plutôt que depuis etcd. Historiquement, ce chemin ignorait les limites de pagination, renvoyant des collections complètes d'objets. Lorsque des milliers d'agents demandent ces listes complètes simultanément lors d'un événement de récupération, le serveur API peine à sérialiser et envoyer des centaines de mégaoctets de données par requête.

Les sondes de vivacité sont conçues pour redémarrer les processus malsains. Toutefois, si une sonde vérifie les dépendances externes plutôt que la capacité locale, elle peut redémarrer un processus encore fonctionnel utilisant un état mis en cache. Par exemple, un fournisseur KMS peut perdre la connectivité avec un service de gestion de clés externe tout en servant toujours des demandes de chiffrement à l'aide de clés mises en cache. Une sonde testant la joignabilité externe redémarrera inutilement le fournisseur, le forçant à rétablir les connexions et à jeter un état mis en cache utile.

Détails clés

  • OpenAPI v2 combine tous les schémas API en un seul document, créant un domaine de défaillance partagé où un schéma d'extension invalide peut bloquer tous les clients.
  • OpenAPI v3 sépare les schémas par groupe d'API, limitant la zone d'impact des définitions invalides aux utilisateurs d'API spécifiques.
  • Les informers utilisant resourceVersion=0 lisent depuis le cache de surveillance, qui historiquement ignorait les limites de pagination, conduisant à des tailles de réponse massives.
  • Les redémarrages synchronisés des agents DaemonSet peuvent générer une tempête de requêtes, submergeant le serveur API avec des opérations de liste concurrentes.
  • Les sondes de vivacité qui vérifient les dépendances externes peuvent redémarrer des processus encore capables de servir des requêtes en utilisant un état mis en cache.
  • L'exactitude au niveau du composant est nécessaire mais insuffisante pour la disponibilité du système ; les mécanismes doivent être évalués quant à leur comportement lorsque les hypothèses sont violées.

Pourquoi c'est important

Pour les équipes construisant et exploitant des plateformes Kubernetes, ces insights soulignent que les modèles opérationnels standards peuvent devenir des amplificateurs de défaillance à grande échelle. Les ingénieurs supposent souvent que les vérifications de santé et les mises à jour progressives sont des comportements sûrs et par défaut. Cependant, lorsque les composants partagent des métadonnées ou synchronisent des tâches de démarrage, ces mécanismes peuvent interagir de manière inattendue. Comprendre la différence entre tester les dépendances et tester les capacités est crucial pour concevoir des systèmes résilients. Une sonde qui signale fidèlement une panne aval peut néanmoins violer le contrat de disponibilité du système en déclenchant des redémarrages inutiles.

Cela concerne particulièrement les équipes exécutant des API étendues, des contrôleurs surveillant de grandes collections d'objets, ou des composants situés dans le chemin de requête API. Le coût de la reconstruction de l'état local ou de la validation des schémas peut dépasser la capacité du plan de contrôle lors de pics de charge ou d'événements de récupération. Les responsables de plateforme doivent examiner les procédures de mise à jour, les exercices de récupération et les conceptions de vérification de santé pour s'assurer qu'ils ne rejettent pas involontairement l'état mis en cache ou ne créent pas une demande synchronisée sur des dépendances dégradées.

Ce que vous pouvez faire

  • Validez les schémas OpenAPI après les mises à jour en récupérant les documents v2 et v3 et en les analysant avec des bibliothèques clientes pour détecter les définitions invalides tôt.
  • Restreignez la portée des informers à la plus petite collection de ressources nécessaire, telle que des namespaces spécifiques ou des vues limitées aux métadonnées, pour réduire la charge de démarrage.
  • Utilisez WatchList ou les fonctionnalités actualisées d'API Priority and Fairness dans Kubernetes 1.31+ pour atténuer le coût des opérations de liste initiales.
  • Concevez les sondes de vivacité pour tester la capacité locale et la validité de l'état mis en cache plutôt que la joignabilité des dépendances externes.
  • Séparez les vérifications de dépendance des sondes de vivacité pour alerter les opérateurs sans déclencher de redémarrages automatiques lors de pannes transitoires en amont.
  • Examinez les stratégies de mise à jour des DaemonSets et les paramètres maxUnavailable pour empêcher que les redémarrages synchronisés ne submergent le serveur API lors de la récupération.
  • #kubernetes
  • #control plane
  • #reliability
  • #openapi
  • #infrastructure

Outils de la Boutique Bytechap

Continuer la lecture

Tous les articles