3 histoires de crash de Kubernetes en production: anti-affinité, arrêt gracieux, webhook


Remarque perev. : Nous présentons une mini-sélection de post-mortem sur les problèmes mortels auxquels les ingénieurs de différentes entreprises ont été confrontés lors de l'exploitation de l'infrastructure basée sur Kubernetes. Chaque note parle du problème lui-même, de ses causes et de ses conséquences, et, bien sûr, d'une solution qui permet d'éviter des situations similaires à l'avenir.

Comme vous le savez, apprendre de l'expérience de quelqu'un d'autre est moins cher, et donc - laissez ces histoires vous aider à vous préparer à d'éventuelles surprises. Soit dit en passant, une large sélection de liens vers ces «histoires d'échecs» est régulièrement publiée sur ce site (selon les données de ce référentiel Git ).

N ° 1. Comment la panique du noyau a bloqué un site


Original: clair de lune .

Entre le 18 et le 22 janvier, le site Web Moonlight et l'API ont connu des dysfonctionnements intermittents. Tout a commencé avec des erreurs API aléatoires et s'est terminé par un arrêt complet. Les problèmes ont été résolus et l'application est revenue à la normale.

Informations générales


Moonlight utilise un logiciel appelé Kubernetes. Kubernetes exécute des applications sur des groupes de serveurs. Ces serveurs sont appelés nœuds. Les copies de l'application exécutée sur le nœud sont appelées pods. Kubernetes a un planificateur qui détermine dynamiquement quels pods sur quels nœuds devraient fonctionner.

Chronologie


Vendredi, les premières erreurs étaient liées à des problèmes de connexion à la base de données Redis. L'API Moonlight utilise Redis pour vérifier les sessions pour chaque demande authentifiée. Notre outil de surveillance Kubernetes a signalé que certains nœuds et pods ne répondaient pas. Dans le même temps, Google Cloud a signalé un dysfonctionnement des services réseau , et nous avons décidé qu'ils étaient à l'origine de nos problèmes.

Comme le trafic du week-end a diminué, les erreurs ont semblé être résolues dans leur masse. Cependant, mardi matin, le site de Moonlight est tombé et le trafic externe n'a pas du tout atteint le cluster. Nous avons trouvé une autre personne sur Twitter avec des symptômes similaires et avons décidé que l'hébergement Google avait une panne de réseau. Nous avons contacté l'assistance Google Cloud, qui a rapidement signalé le problème à l'équipe d'assistance technique.

L'équipe d'assistance technique de Google a révélé une certaine tendance dans le comportement des nœuds de notre cluster Kubernetes. La charge CPU des nœuds individuels a atteint 100%, après quoi la panique du noyau s'est produite dans la machine virtuelle et elle s'est bloquée.

Raisons


Le cycle à l'origine de l'échec était le suivant:

  • Le planificateur Kubernetes a hébergé plusieurs pods avec une consommation élevée de CPU sur le même nœud.
  • Les pods ont consommé toutes les ressources CPU du nœud.
  • Vint ensuite la panique du noyau, qui entraîna une période d'indisponibilité pendant laquelle le nœud ne répondit pas au planificateur.
  • Le planificateur a déplacé tous les pods tombés vers un nouveau noeud, et le processus a été répété, exacerbant la situation générale.

Initialement, l'erreur s'est produite dans le pod Redis, mais au final tous les pods qui fonctionnent avec le trafic sont tombés, ce qui a conduit à un arrêt complet. Des retards exponentiels pendant la replanification ont entraîné de plus longues périodes d'indisponibilité.

Solution


Nous avons pu restaurer le site en ajoutant des règles anti-affinité à tous les déploiements majeurs. Ils distribuent automatiquement les modules sur les nœuds, augmentant la tolérance aux pannes et les performances.

Kubernetes lui-même est conçu comme un système hôte tolérant aux pannes. Moonlight utilise trois nœuds sur des serveurs différents pour plus de stabilité, et nous exécutons trois copies de chaque application qui dessert le trafic. L'idée est d'avoir une copie sur chaque nœud. Dans ce cas, même une défaillance de deux nœuds n'entraînera pas de temps d'arrêt. Cependant, Kubernetes a parfois placé les trois modules avec le site sur le même nœud, créant ainsi un goulot d'étranglement dans le système. Dans le même temps, d'autres applications exigeant de la puissance CPU (à savoir le rendu côté serveur) se sont retrouvées sur le même nœud, et non sur un autre.

Un cluster Kubernetes correctement configuré et fonctionnant correctement est nécessaire pour faire face à de longues périodes de charge CPU élevée et placer les pods de manière à maximiser l'utilisation des ressources disponibles. Nous continuons de travailler avec la prise en charge de Google Cloud pour identifier et résoudre la cause première de la panique du noyau sur les serveurs.

Conclusion


Les règles anti-affinité vous permettent de rendre les applications qui fonctionnent avec le trafic externe plus tolérantes aux pannes. Si vous avez un service similaire chez Kubernetes, pensez à les ajouter.

Nous continuons de travailler avec les gars de Google pour trouver et éliminer la cause des défaillances du noyau du système d'exploitation sur les nœuds.

N ° 2. Le «sale» secret de Kubernetes et Ingress endpoint


Original: Phil Pearl de Ravelin .

L'élégance est surfaite


Chez Ravelin, nous avons migré vers Kubernetes (sur GKE). Le processus a été couronné de succès. Nos budgets de perturbation des pods sont aussi complets que jamais, les états sont vraiment majestueux (un jeu de mots difficile à traduire: "nos ensembles avec état sont très majestueux" - environ la traduction) , et le remplacement coulissant des nœuds fonctionne comme une horloge.

La dernière pièce du puzzle consiste à déplacer la couche API des anciennes machines virtuelles vers le cluster Kubernetes. Pour ce faire, nous devons configurer Ingress afin que l'API soit accessible depuis le monde extérieur.

Au début, la tâche semblait simple. Nous définissons simplement le contrôleur Ingress, modifions Terraform pour obtenir un certain nombre d'adresses IP, et Google s'occupe de presque tout le reste. Et tout cela fonctionnera comme par magie. Classe!

Cependant, au fil du temps, ils ont commencé à remarquer que les tests d'intégration recevaient périodiquement des erreurs 502. De là, notre voyage a commencé. Cependant, je vous ferai gagner du temps et irai directement aux conclusions.

Arrêt progressif


Tout le monde parle d'un arrêt progressif ("gracieux", arrêt progressif). Mais vous ne devriez vraiment pas compter sur lui à Kubernetes. Ou du moins, cela ne devrait pas être l'arrêt gracieux que vous avez absorbé avec le lait de votre mère . Dans le monde Kubernetes, ce niveau «d'élégance» est inutile et menace de graves problèmes.

Monde parfait


Voici comment, dans la vue majoritaire, le pod est supprimé du service ou de l'équilibreur de charge dans Kubernetes:

  1. Le contrôleur de réplication décide de supprimer le pod.
  2. Le module de noeud final est supprimé du service ou de l'équilibreur de charge. Le nouveau trafic vers le pod n'arrive plus.
  3. Un crochet de pré-arrêt est appelé ou le pod reçoit un signal SIGTERM.
  4. Le pod "gracieusement" est déconnecté. Il cesse d'accepter les connexions entrantes.
  5. La déconnexion "gracieuse" est terminée et le pod est détruit une fois toutes ses connexions existantes arrêtées ou terminées.

Malheureusement, la réalité est complètement différente.

Monde réel


La plupart de la documentation laisse entendre que tout se passe un peu différemment, mais ils n'écrivent explicitement à ce sujet nulle part. Le problème principal est que l'étape 3 ne suit pas l'étape 2. Elles se produisent simultanément. Dans les services ordinaires, la suppression des points d'extrémité est si rapide que la probabilité de rencontrer des problèmes est extrêmement faible. Cependant, avec Ingresss, tout est différent: ils réagissent généralement beaucoup plus lentement, donc le problème devient évident. Le pod peut obtenir SIGTERM bien avant que les changements de points de terminaison n'entrent dans Ingress.

En conséquence, un arrêt gracieux n'est pas du tout ce qui est requis d'un pod. Il recevra de nouvelles connexions et devra continuer à les traiter, sinon les clients commenceront à recevoir les 500e erreurs et toute l'histoire merveilleuse des déploiements et de la mise à l'échelle simples commencera à s'effondrer.

Voici ce qui se passe réellement:

  1. Le contrôleur de réplication décide de supprimer le pod.
  2. Le module de noeud final est supprimé du service ou de l'équilibreur de charge. Dans le cas d'Ingresss, cela peut prendre un certain temps et le nouveau trafic continuera de circuler dans le pod.
  3. Un crochet de pré-arrêt est appelé ou le pod reçoit un signal SIGTERM.
  4. Dans une large mesure, le pod doit ignorer cela, continuer à fonctionner et maintenir de nouvelles connexions. Si possible, il devrait laisser entendre aux clients que ce serait bien de passer à un autre endroit. Par exemple, dans le cas de HTTP, il peut envoyer Connection: close dans les en-têtes de réponse.
  5. Le pod ne sort que lorsque la période d'attente «élégante» expire et qu'il est tué par SIGKILL.
  6. Assurez-vous que cette période est plus longue que le temps nécessaire pour reprogrammer l'équilibreur de charge.

S'il s'agit d'un code tiers et que vous ne pouvez pas modifier son comportement, la meilleure chose à faire est d'ajouter un crochet de pré-arrêt qui dormira juste pendant une période «élégante», de sorte que le pod continuera de fonctionner comme si de rien arrivé.

Numéro 3. Comment un simple webhook a provoqué une panne de cluster


Original: Jetstack .

Jetstack propose à ses clients des plateformes multi-locataires sur Kubernetes. Parfois, il existe des exigences particulières que nous ne pouvons pas satisfaire avec la configuration standard de Kubernetes. Pour les implémenter, nous avons récemment commencé à utiliser l' Open Policy Agent (nous avons écrit plus en détail sur le projet dans cette revue - environ Transl.) En tant que contrôleur d'accès pour la mise en œuvre de politiques spéciales.

Cet article décrit l'échec provoqué par une mauvaise configuration de cette intégration.

Incident


Nous étions engagés dans la mise à jour de l'assistant pour le cluster de développement, dans lequel diverses équipes ont testé leurs applications pendant la journée de travail. Il s'agissait d'un cluster régional dans la zone europe-ouest1 sur le moteur Google Kubernetes (GKE).

Les commandes ont été averties qu'une mise à jour était en cours, sans aucun temps d'arrêt prévu. Plus tôt dans la journée, nous avons déjà effectué une mise à jour similaire vers un autre environnement de pré-production.

Nous avons commencé la mise à niveau en utilisant notre pipeline GKE Terraform. La mise à jour de l'assistant ne s'est pas terminée avant l'expiration du délai d'expiration Terraform, que nous avons défini pour 20 minutes. Ce fut le premier réveil que quelque chose s'est mal passé, bien que dans la console GKE le cluster soit toujours répertorié comme «mise à niveau».

Le redémarrage du pipeline a conduit à l'erreur suivante

 google_container_cluster.cluster: Error waiting for updating GKE master version: All cluster resources were brought up, but the cluster API is reporting that: component "kube-apiserver" from endpoint "gke-..." is unhealthy 

Cette fois, la connexion avec le serveur API a commencé à être interrompue périodiquement et les équipes n'ont pas pu déployer leurs applications.

Pendant que nous essayions de comprendre ce qui se passait, tous les nœuds ont commencé à être détruits et recréés dans un cycle sans fin. Cela a conduit à un déni de service aveugle pour tous nos clients.

Nous établissons la cause profonde de l'échec


Avec l'assistance Google, nous avons pu déterminer la séquence des événements qui ont conduit à l'échec:

  1. GKE a terminé la mise à niveau sur une instance de l'assistant et a commencé à accepter tout le trafic vers le serveur API sur celui-ci lors de la mise à jour des assistants suivants.
  2. Lors de la mise à niveau de la deuxième instance de l'assistant, le serveur API n'a pas pu exécuter PostStartHook pour enregistrer l'autorité de certification.
  3. Pendant l'exécution de ce hook, le serveur API a essayé de mettre à jour ConfigMap appelé extension-apiserver-authentication dans kube-system . Cela n'a pas été possible car le backend pour le webhook de vérification de l'Open Policy Agent (OPA) que nous avons configuré n'a pas répondu.
  4. Pour que l'assistant réussisse un contrôle d'intégrité, cette opération doit aboutir. Comme cela ne s'est pas produit, le deuxième maître est entré dans le cycle d'urgence et a arrêté la mise à jour.

Le résultat a été des plantages d'API périodiques, en raison desquels les kubelets n'ont pas pu signaler la santé du nœud. À son tour, cela a conduit au fait que le mécanisme de restauration automatique des nœuds GKE (réparation automatique des nœuds ) a commencé à redémarrer les nœuds. Cette fonctionnalité est décrite en détail dans la documentation :

Un état malsain peut signifier: dans un délai donné (environ 10 minutes), le nœud ne donne aucun état.

Solution


Lorsque nous avons découvert que la ressource ValidatingAdmissionWebhook provoquait un accès intermittent au serveur API, nous l'avons supprimé et restauré le cluster pour fonctionner.

Depuis lors, le ValidatingAdmissionWebhook pour OPA a été configuré pour surveiller uniquement les espaces de noms où la stratégie est applicable et auxquels les équipes de développement ont accès. Nous avons également limité le webhook à Ingress and Service , les seuls avec lesquels notre politique fonctionne.

Depuis le premier déploiement de l'OPA, la documentation a été mise à jour pour refléter ce changement.

Nous avons également ajouté un test de vivacité pour nous assurer que l'OPA redémarre en cas d'indisponibilité (et apporté les modifications appropriées à la documentation).

Nous avons également envisagé de désactiver le mécanisme de récupération automatique pour les nœuds GKE, mais nous avons tout de même décidé d'abandonner cette idée.

Résumé


Si nous activions les alertes de temps de réponse du serveur API, nous serions initialement en mesure de constater son augmentation globale pour toutes les demandes CREATE et UPDATE après le déploiement du webhook pour OPA.

Cela souligne l'importance de mettre en place des tests pour toutes les charges de travail. Avec le recul, nous pouvons dire que le déploiement de l'OPA était si trompeusement simple que nous ne nous sommes même pas impliqués dans le diagramme de Helm (bien qu'il le devrait). Le tableau fait un certain nombre d'ajustements au-delà des paramètres de base décrits dans le manuel, y compris le paramètre livenessProbe pour les conteneurs avec un contrôleur d'admission.

Nous n'avons pas été les premiers à rencontrer ce problème: le problème en amont reste ouvert. La fonctionnalité dans ce domaine peut clairement être améliorée (et nous ferons un suivi à ce sujet).

PS du traducteur


Lisez aussi dans notre blog:

Source: https://habr.com/ru/post/fr475026/


All Articles