10 erreurs courantes lors de l'utilisation de Kubernetes

Noter. trad.: Les auteurs de cet article sont des ingénieurs d'une petite entreprise tchèque, pipetail. Ils ont réussi à dresser une magnifique liste de problèmes et d'idées fausses [parfois banales, mais quand même] très urgents liés au fonctionnement des clusters Kubernetes.

10 erreurs courantes lors de l'utilisation de Kubernetes

Au fil des années d'utilisation de Kubernetes, nous avons travaillé avec un grand nombre de clusters (à la fois gérés et non gérés - sur GCP, AWS et Azure). Au fil du temps, nous avons commencé à remarquer que certaines erreurs se répétaient constamment. Il n’y a cependant aucune honte à cela : nous en avons réalisé la plupart nous-mêmes !

L'article contient les erreurs les plus courantes et mentionne également comment les corriger.

1. Ressources : demandes et limites

Cet article mérite certainement la plus grande attention et la première place sur la liste.

Demande de CPU généralement soit pas spécifié du tout, soit a une valeur très faible (pour placer autant de pods que possible sur chaque nœud). Ainsi, les nœuds deviennent surchargés. Pendant les périodes de charge élevée, la puissance de traitement du nœud est pleinement utilisée et une charge de travail particulière ne reçoit que ce qu'elle a « demandé » par Limitation du processeur. Cela entraîne une augmentation de la latence des applications, des délais d'attente et d'autres conséquences désagréables. (En savoir plus à ce sujet dans notre autre traduction récente : «Limites du processeur et limitation agressive dans Kubernetes" - environ. trad.)

Meilleur effort (extrêmement aucun recommandé):

resources: {}

Demande CPU extrêmement faible (extrêmement aucun recommandé):

   resources:
      Requests:
        cpu: "1m"

D'un autre côté, la présence d'une limite de CPU peut conduire à un saut déraisonnable de cycles d'horloge par les pods, même si le processeur du nœud n'est pas complètement chargé. Encore une fois, cela peut entraîner une augmentation des retards. La polémique continue autour du paramètre Quota CFS du processeur dans le noyau Linux et limitation du processeur en fonction des limites définies, ainsi que la désactivation du quota CFS... Hélas, les limites du processeur peuvent causer plus de problèmes qu'elles ne peuvent en résoudre. Plus d’informations à ce sujet peuvent être trouvées sur le lien ci-dessous.

Sélection excessive (surengagement) les problèmes de mémoire peuvent entraîner des problèmes plus importants. Atteindre la limite du processeur implique de sauter des cycles d'horloge, tandis qu'atteindre la limite de mémoire implique de tuer le pod. Avez-vous déjà observé MOOtuer? Oui, c'est exactement de cela dont nous parlons.

Voulez-vous minimiser la probabilité que cela se produise ? Ne surallouez pas de mémoire et utilisez la qualité de service garantie (QoS) en définissant la demande de mémoire à la limite (comme dans l'exemple ci-dessous). En savoir plus à ce sujet dans Présentations Henning Jacobs (Ingénieur en chef chez Zalando).

Éclatable (plus de chances d'être tué par OOM) :

   resources:
      requests:
        memory: "128Mi"
        cpu: "500m"
      limits:
        memory: "256Mi"
        cpu: 2

Sans conditions:

   resources:
      requests:
        memory: "128Mi"
        cpu: 2
      limits:
        memory: "128Mi"
        cpu: 2

Qu’est-ce qui pourrait être utile lors de la configuration des ressources ?

Avec serveur de métriques vous pouvez voir la consommation actuelle des ressources CPU et l'utilisation de la mémoire par les pods (et les conteneurs qu'ils contiennent). Très probablement, vous l'utilisez déjà. Exécutez simplement les commandes suivantes :

kubectl top pods
kubectl top pods --containers
kubectl top nodes

Cependant, ils ne montrent que l'utilisation actuelle. Cela peut vous donner une idée approximative de l'ordre de grandeur, mais en fin de compte, vous aurez besoin historique des changements dans les métriques au fil du temps (pour répondre à des questions telles que : « Quelle était la charge maximale du CPU ? », « Quelle était la charge hier matin ? », etc.). Pour cela, vous pouvez utiliser Prométhée, DataDog et d'autres outils. Ils obtiennent simplement les métriques du serveur de métriques et les stockent, et l'utilisateur peut les interroger et les tracer en conséquence.

VerticalPodAutoscaler il permet automatiser ce processus. Il suit l'historique d'utilisation du processeur et de la mémoire et définit de nouvelles demandes et limites en fonction de ces informations.

Utiliser efficacement la puissance de calcul n’est pas une tâche facile. C'est comme jouer à Tetris tout le temps. Si vous payez trop cher pour une puissance de calcul avec une faible consommation moyenne (disons ~ 10 %), nous vous recommandons de vous tourner vers des produits basés sur AWS Fargate ou Virtual Kubelet. Ils sont construits sur un modèle de facturation sans serveur/paiement à l'utilisation, qui peut s'avérer moins cher dans de telles conditions.

2. Sondages de vivacité et de préparation

Par défaut, les contrôles d'activité et de préparation ne sont pas activés dans Kubernetes. Et parfois ils oublient de les allumer...

Mais comment pouvez-vous autrement lancer un redémarrage du service en cas d’erreur fatale ? Et comment l'équilibreur de charge sait-il qu'un pod est prêt à accepter du trafic ? Ou qu'il peut gérer plus de trafic ?

Ces tests sont souvent confondus entre eux :

  • Vivacité — contrôle de « survivabilité », qui redémarre le pod en cas d'échec ;
  • Préparation — vérification de l'état de préparation, en cas d'échec, il déconnecte le pod du service Kubernetes (cela peut être vérifié en utilisant kubectl get endpoints) et le trafic n'y arrive qu'après une vérification réussie.

Ces deux contrôles RÉALISÉ PENDANT TOUT LE CYCLE DE VIE DU POD. Il est très important.

Une idée fausse courante est que les sondes de préparation ne sont exécutées qu'au démarrage afin que l'équilibreur puisse savoir que le pod est prêt (Ready) et peut commencer à traiter le trafic. Cependant, ce n'est qu'une des options pour leur utilisation.

Une autre est la possibilité de découvrir que le trafic sur le pod est excessif et le surcharge (ou le pod effectue des calculs gourmands en ressources). Dans ce cas, le contrôle de préparation aide réduire la charge sur le pod et le « refroidir ». La réussite d'un contrôle de préparation à l'avenir permet augmenter à nouveau la charge sur le pod. Dans ce cas (si le test de préparation échoue), l’échec du test de vivacité serait très contre-productif. Pourquoi redémarrer un pod qui est sain et qui travaille dur ?

Par conséquent, dans certains cas, il vaut mieux ne procéder à aucune vérification que de les activer avec des paramètres mal configurés. Comme indiqué ci-dessus, si contrôle de vivacité des copies contrôle de préparation, alors vous avez de gros ennuis. L'option possible est de configurer test de préparation uniquementEt vivacité dangereuse laisser de côté.

Les deux types de vérifications ne doivent pas échouer lorsque les dépendances communes échouent, sinon cela entraînera une défaillance en cascade (semblable à une avalanche) de tous les pods. Autrement dit, ne te fais pas de mal.

3. LoadBalancer pour chaque service HTTP

Très probablement, vous disposez de services HTTP dans votre cluster que vous souhaitez transmettre au monde extérieur.

Si vous ouvrez le service en tant que type: LoadBalancer, son contrôleur (en fonction du fournisseur de services) fournira et négociera un LoadBalancer externe (pas nécessairement fonctionnant sur L7, mais plutôt même sur L4), et cela peut affecter le coût (adresse IPv4 statique externe, puissance de calcul, facturation à la seconde ) en raison de la nécessité de créer un grand nombre de ces ressources.

Dans ce cas, il est beaucoup plus logique d'utiliser un équilibreur de charge externe, en ouvrant les services comme type: NodePort. Ou mieux encore, développez quelque chose comme contrôleur d'entrée nginx (ou Traefik), qui sera le seul Port de nœud point de terminaison associé à l'équilibreur de charge externe et acheminera le trafic dans le cluster à l'aide entrée-Ressources Kubernetes.

D'autres (micro)services intra-cluster qui interagissent les uns avec les autres peuvent « communiquer » à l'aide de services tels que ClusterIP et un mécanisme de découverte de services intégré via DNS. N'utilisez simplement pas leur DNS/IP public, car cela peut avoir un impact sur la latence et augmenter le coût des services cloud.

4. Autoscaling d'un cluster sans prendre en compte ses fonctionnalités

Lorsque vous ajoutez et supprimez des nœuds d'un cluster, vous ne devez pas vous fier à certaines mesures de base telles que l'utilisation du processeur sur ces nœuds. La planification des pods doit prendre en compte de nombreux restrictions, tels que l'affinité pod/nœud, les teintes et tolérances, les demandes de ressources, la qualité de service, etc. L’utilisation d’un autoscaler externe qui ne prend pas en compte ces nuances peut entraîner des problèmes.

Imaginez qu'un certain pod doive être planifié, mais que toute la puissance CPU disponible soit demandée/démontée et le pod reste coincé dans un état Pending. L'autoscaler externe voit la charge CPU actuelle moyenne (pas celle demandée) et ne lance pas l'expansion (évolutivité) - n'ajoute pas d'autre nœud. Par conséquent, ce pod ne sera pas planifié.

Dans ce cas, inversez la mise à l'échelle (mise à l'échelle) — supprimer un nœud d'un cluster est toujours plus difficile à mettre en œuvre. Imaginez que vous disposez d'un pod avec état (avec un stockage persistant connecté). Volumes persistants appartiennent habituellement à zone de disponibilité spécifique et ne sont pas reproduits dans la région. Ainsi, si un autoscaler externe supprime un nœud avec ce pod, le planificateur ne pourra pas planifier ce pod sur un autre nœud, puisque cela ne peut se faire que dans la zone de disponibilité où se trouve le stockage persistant. Le pod sera bloqué dans cet état Pending.

Très populaire dans la communauté Kubernetes mise à l'échelle automatique de cluster. Il fonctionne sur un cluster, prend en charge les API des principaux fournisseurs de cloud, prend en compte toutes les restrictions et peut évoluer dans les cas ci-dessus. Il est également capable d'évoluer tout en maintenant toutes les limites définies, économisant ainsi de l'argent (qui serait autrement dépensé en capacité inutilisée).

5. Négliger les capacités IAM/RBAC

Méfiez-vous de l'utilisation d'utilisateurs IAM avec des secrets persistants pour machines et applications. Organiser l'accès temporaire à l'aide de rôles et de comptes de service (comptes de services).

Nous rencontrons souvent le fait que les clés d'accès (et les secrets) sont codées en dur dans la configuration de l'application, ainsi que la négligence de la rotation des secrets malgré l'accès à Cloud IAM. Utilisez des rôles et des comptes de service IAM plutôt que des utilisateurs, le cas échéant.

10 erreurs courantes lors de l'utilisation de Kubernetes

Oubliez kube2iam et accédez directement aux rôles IAM pour les comptes de service (comme décrit dans note du même nom Stepan Vraný) :

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-app-role
  name: my-serviceaccount
  namespace: default

Une annotation. Pas si difficile, non ?

N'accordez pas non plus de privilèges aux comptes de service et aux profils d'instance. admin и cluster-admins'ils n'en ont pas besoin. C'est un peu plus difficile à mettre en œuvre, en particulier dans les RBAC K8, mais cela en vaut vraiment la peine.

6. Ne comptez pas sur l'anti-affinité automatique pour les pods

Imaginez que vous disposez de trois répliques d'un déploiement sur un nœud. Le nœud tombe, et avec lui toutes les répliques. Situation désagréable, non ? Mais pourquoi toutes les répliques étaient-elles sur le même nœud ? Kubernetes n'est-il pas censé fournir une haute disponibilité (HA) ?!

Malheureusement, le planificateur Kubernetes, de sa propre initiative, ne respecte pas les règles d'existence séparée (anti-affinité) pour les dosettes. Ils doivent être explicitement indiqués :

// опущено для краткости
      labels:
        app: zk
// опущено для краткости
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"

C'est tout. Désormais, les pods seront planifiés sur différents nœuds (cette condition n'est vérifiée que lors de la planification, mais pas pendant leur fonctionnement - donc requiredDuringSchedulingIgnoredDuringExecution).

Ici, nous parlons de podAntiAffinity sur différents nœuds : topologyKey: "kubernetes.io/hostname", - et non sur les différentes zones de disponibilité. Pour mettre en œuvre une HA à part entière, vous devrez approfondir ce sujet.

7. Ignorer les PodDisruptionBudgets

Imaginez que vous ayez une charge de production sur un cluster Kubernetes. Périodiquement, les nœuds et le cluster lui-même doivent être mis à jour (ou mis hors service). PodDisruptionBudget (PDB) est quelque chose comme un accord de garantie de service entre les administrateurs de cluster et les utilisateurs.

PDB permet d'éviter les interruptions de service causées par un manque de nœuds :

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

Dans cet exemple, en tant qu'utilisateur du cluster, vous déclarez aux administrateurs : "Hé, j'ai un service zookeeper, et quoi que vous fassiez, j'aimerais avoir au moins 2 répliques de ce service toujours disponibles."

Vous pouvez en savoir plus à ce sujet ici.

8. Plusieurs utilisateurs ou environnements dans un cluster commun

Espaces de noms Kubernetes (espaces de noms) ne fournit pas une isolation solide.

Une idée fausse courante est que si vous déployez une charge non-prod dans un espace de noms et une charge de production dans un autre, alors ils ne s'influenceront en aucune façon... Cependant, un certain niveau d'isolement peut être atteint en utilisant des demandes/limitations de ressources, en définissant des quotas et en définissant des classes prioritaires. Une certaine isolation « physique » dans le plan des données est assurée par des affinités, des tolérances, des teintes (ou des sélecteurs de nœuds), mais une telle séparation est assez complexe. difficile mettre en œuvre.

Ceux qui doivent combiner les deux types de charges de travail dans le même cluster devront faire face à la complexité. Si ce n’est pas nécessaire et que vous pouvez vous permettre d’en avoir un encore un cluster (par exemple, dans un cloud public), alors il vaut mieux le faire. Cela permettra d'atteindre un niveau d'isolation beaucoup plus élevé.

9. externalTrafficPolicy : cluster

Très souvent, nous constatons que tout le trafic à l'intérieur du cluster passe par un service comme NodePort, pour lequel la politique par défaut est définie externalTrafficPolicy: Cluster. Cela signifie que Port de nœud est ouvert sur chaque nœud du cluster et vous pouvez utiliser n'importe lequel d'entre eux pour interagir avec le service souhaité (ensemble de pods).

10 erreurs courantes lors de l'utilisation de Kubernetes

Dans le même temps, les vrais pods associés au service NodePort mentionné ci-dessus ne sont généralement disponibles que sur un certain sous-ensemble de ces nœuds. En d'autres termes, si je me connecte à un nœud qui ne dispose pas du pod requis, il redirigera le trafic vers un autre nœud, ajouter un saut et une latence croissante (si les nœuds sont situés dans différentes zones de disponibilité/centres de données, la latence peut être assez élevée ; en outre, les coûts du trafic de sortie augmenteront).

D'un autre côté, si un certain service Kubernetes a une stratégie définie externalTrafficPolicy: Local, alors NodePort s'ouvre uniquement sur les nœuds sur lesquels les pods requis sont réellement en cours d'exécution. Lors de l'utilisation d'un équilibreur de charge externe qui vérifie l'état (bilan de santé) points de terminaison (comment ça marche AWS ELB), Il enverra le trafic uniquement aux nœuds nécessaires, ce qui aura un effet bénéfique sur les délais, les besoins informatiques, les factures de sortie (et le bon sens dicte la même chose).

Il y a de fortes chances que vous utilisiez déjà quelque chose comme Traefik ou contrôleur d'entrée nginx en tant que point de terminaison NodePort (ou LoadBalancer, qui utilise également NodePort) pour acheminer le trafic HTTP entrant, et la définition de cette option peut réduire considérablement la latence de ces requêtes.

В cette publication Vous pouvez en savoir plus sur externalTrafficPolicy, ses avantages et ses inconvénients.

10. Ne vous liez pas aux clusters et n'abusez pas du plan de contrôle

Auparavant, il était d'usage d'appeler les serveurs par leurs noms propres : Anton, HAL9000 et Colossus... Aujourd'hui, ils ont été remplacés par des identifiants générés aléatoirement. Cependant, l'habitude est restée, et désormais les noms propres vont aux grappes.

Une histoire typique (basée sur des événements réels) : tout a commencé par une preuve de concept, le cluster avait donc un nom fier vers les tests… Les années ont passé et il est TOUJOURS utilisé en production, et tout le monde a peur d'y toucher.

Il n'y a rien d'amusant à ce que les clusters se transforment en animaux de compagnie, nous vous recommandons donc de les supprimer périodiquement pendant l'entraînement. reprise après sinistre (CA aidera ingénierie du chaos - environ. trad.). De plus, cela ne ferait pas de mal de travailler sur la couche de contrôle (avion de contrôle). Avoir peur de le toucher n’est pas bon signe. etc. mort? Les gars, vous avez vraiment des ennuis !

En revanche, il ne faut pas se laisser emporter par sa manipulation. Avec le temps la couche de contrôle peut devenir lente. Très probablement, cela est dû au fait qu'un grand nombre d'objets sont créés sans leur rotation (une situation courante lors de l'utilisation de Helm avec les paramètres par défaut, c'est pourquoi son état dans configmaps/secrets n'est pas mis à jour - par conséquent, des milliers d'objets s'accumulent dans la couche de contrôle) ou avec une édition constante des objets kube-api (pour la mise à l'échelle automatique, pour CI/CD, pour la surveillance, les journaux d'événements, les contrôleurs, etc.).

De plus, nous vous recommandons de vérifier les accords SLA/SLO avec le fournisseur Kubernetes géré et de prêter attention aux garanties. Le vendeur peut garantir disponibilité de la couche de contrôle (ou ses sous-composants), mais pas le délai p99 des requêtes que vous lui envoyez. En d'autres termes, vous pouvez saisir kubectl get nodes, et recevez une réponse seulement après 10 minutes, et cela ne constituera pas une violation des termes du contrat de service.

11. Bonus : utiliser le dernier tag

Mais c'est déjà un classique. Dernièrement, nous avons rencontré moins souvent cette technique, car beaucoup, ayant tiré les leçons d'une expérience amère, ont arrêté d'utiliser le tag. :latest et j'ai commencé à épingler les versions. Hourra!

ECR maintient l'immuabilité des balises d'image; Nous vous recommandons de vous familiariser avec cette fonctionnalité remarquable.

Résumé

Ne vous attendez pas à ce que tout fonctionne du jour au lendemain : Kubernetes n’est pas une panacée. Mauvaise application cela restera ainsi même dans Kubernetes (et ça va probablement empirer). La négligence entraînera une complexité excessive, un travail lent et stressant de la couche de contrôle. De plus, vous risquez de vous retrouver sans stratégie de reprise après sinistre. Ne vous attendez pas à ce que Kubernetes offre une isolation et une haute disponibilité prêtes à l'emploi. Passez du temps à rendre votre application véritablement cloud native.

Vous pouvez vous familiariser avec les expériences infructueuses de diverses équipes dans ce recueil d'histoires par Henning Jacobs.

Ceux qui souhaitent compléter la liste d'erreurs donnée dans cet article peuvent nous contacter sur Twitter (@MarekBartik, @MstrsObservateur).

PS du traducteur

A lire aussi sur notre blog :

Source: habr.com

Ajouter un commentaire