View a markdown version of this page

Plan de contrôle Kubernetes - Amazon EKS

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Plan de contrôle Kubernetes

Astuce

Découvrez les meilleures pratiques grâce aux ateliers Amazon EKS.

Le plan de contrôle Kubernetes comprend le serveur API Kubernetes, le gestionnaire de contrôleurs Kubernetes, le planificateur et d'autres composants nécessaires au fonctionnement de Kubernetes. Les limites d'évolutivité de ces composants varient en fonction de ce que vous exécutez dans le cluster, mais les domaines qui ont le plus d'impact sur la mise à l'échelle sont la version de Kubernetes, l'utilisation et le dimensionnement individuel des nœuds.

Vous pouvez exécuter le plan de contrôle de votre cluster dans l'un des deux modes suivants pour répondre aux différentes exigences en matière de charge de travail :

  • Mode standard  : par défaut, tous les clusters EKS utilisent le mode standard. Le plan de commande augmente et diminue automatiquement en fonction de vos exigences en matière de charge de travail. Le mode standard alloue dynamiquement une capacité suffisante au plan de contrôle et constitue l'option recommandée dans la plupart des cas d'utilisation.

  • Mode provisionné  : si vos charges de travail ne peuvent tolérer la variabilité des performances due à la mise à l'échelle du plan de contrôle, ou si elles nécessitent une capacité de plan de contrôle très élevée, vous pouvez utiliser le mode provisionné. Avec le mode Provisioned, vous préallouez la capacité du plan de contrôle qui est toujours prête à répondre aux exigences les plus strictes. Vous obtenez des performances constantes et prévisibles.

Avec le mode EKS Provisioned, vous pouvez choisir parmi un ensemble de niveaux d'échelle (XL, 2XL, 4XL et 8XL). À chaque niveau, vous bénéficiez de performances élevées et prévisibles grâce au plan de contrôle du cluster. Le mode provisionné est particulièrement utile dans les cas d'utilisation suivants :

  • Performance-critical charges de travail

  • Large-scale Opérations d'IA et d'apprentissage automatique

  • Événements anticipés à forte demande

  • Environnements nécessitant une cohérence entre la mise en scène et la production

Avec le mode Provisioned, vous pouvez pré-allouer la capacité du plan de contrôle à l'avance et bénéficier d'un accord de niveau de service (SLA) amélioré de 99,99 % mesuré à intervalles d'une minute. Pour plus d'informations sur le mode EKS Provisioned, y compris les spécifications des niveaux et les prix, consultez EKS Provisioned Control Plane dans le guide de l'utilisateur EKS.

Limitez la charge de travail et l'éclatement des nœuds

Important

Pour éviter d'atteindre les limites de l'API sur le plan de contrôle, vous devez limiter les pics de mise à l'échelle qui augmentent la taille du cluster de pourcentages à deux chiffres à la fois (par exemple, 1 000 nœuds à 1 100 nœuds ou 4 000 à 4 500 pods à la fois).

Le plan de contrôle EKS s'adapte automatiquement à la croissance de votre cluster, mais sa rapidité d'évolution est limitée. Lorsque vous créez un cluster EKS pour la première fois, le plan de contrôle ne sera pas immédiatement en mesure de s'adapter à des centaines de nœuds ou à des milliers de pods. Pour en savoir plus sur la manière dont EKS a amélioré la mise à l'échelle, consultez ce billet de blog.

La mise à l'échelle d'applications de grande envergure nécessite l'adaptation de l'infrastructure pour être totalement prête (par exemple, les équilibreurs de charge de réchauffement). Pour contrôler la vitesse de mise à l'échelle, assurez-vous de procéder à la mise à l'échelle en fonction des indicateurs adaptés à votre application. La mise à l'échelle du processeur et de la mémoire peut ne pas prédire avec précision les contraintes de votre application et l'utilisation de métriques personnalisées (par exemple, les demandes par seconde) dans Kubernetes Horizontal Pod Autoscaler (HPA) peut être une meilleure option de dimensionnement.

Pour utiliser une métrique personnalisée, consultez les exemples de la documentation Kubernetes. Si vous avez des besoins de dimensionnement plus avancés ou si vous devez effectuer une mise à l'échelle en fonction de sources externes (par exemple, la file d'attente AWS SQS), utilisez KEDA pour le dimensionnement de la charge de travail basé sur les événements.

Réduisez la taille des nœuds et des modules en toute sécurité

Remplacez les instances qui fonctionnent depuis longtemps

Le remplacement régulier des nœuds permet de préserver la santé de votre cluster en évitant les dérives de configuration et les problèmes qui ne surviennent qu'après une période de disponibilité prolongée (par exemple, des fuites de mémoire lentes). Le remplacement automatique vous fournira de bons processus et de bonnes pratiques pour les mises à niveau des nœuds et l'application des correctifs de sécurité. Si chaque nœud de votre cluster est remplacé régulièrement, il est plus facile de gérer des processus distincts pour une maintenance continue.

Utilisez les paramètres de durée de vie (TTL) de Karpenter pour remplacer les instances après une durée d'exécution spécifiée. Les groupes de nœuds autogérés peuvent utiliser ce max-instance-lifetime paramètre pour faire fonctionner automatiquement les nœuds. Les groupes de nœuds gérés ne disposent pas actuellement de cette fonctionnalité, mais vous pouvez suivre la demande ici GitHub.

Supprimer les nœuds sous-utilisés

Vous pouvez supprimer des nœuds lorsqu'ils n'ont aucune charge de travail en cours d'exécution en utilisant le seuil de réduction dans le Kubernetes Cluster Autoscaler avec le --scale-down-utilization-threshold ou dans Karpenter, vous pouvez utiliser le paramètre Provisioner. ttlSecondsAfterEmpty

Utilisez les budgets d'interruption des pods et arrêtez les nœuds en toute sécurité

La suppression de pods et de nœuds d'un cluster Kubernetes nécessite que les contrôleurs mettent à jour plusieurs ressources (par exemple). EndpointSlices Cette opération fréquente ou trop rapide peut entraîner une limitation du serveur d'API et des pannes d'applications à mesure que les modifications se propagent aux contrôleurs. https://kubernetes.io/docs/concepts/workloads/pods/disruptions/Les budgets d'interruption des modules constituent une bonne pratique pour ralentir le taux de désabonnement afin de protéger la disponibilité de la charge de travail lorsque des nœuds sont supprimés ou replanifiés dans un cluster.

Utiliser Client-Side le cache lors de l'exécution de Kubectl

L'utilisation inefficace de la commande kubectl peut ajouter une charge supplémentaire au serveur d'API Kubernetes. Vous devez éviter d'exécuter des scripts ou des automatismes qui utilisent kubectl à plusieurs reprises (par exemple dans une boucle for) ou d'exécuter des commandes sans cache local.

kubectldispose d'un cache côté client qui met en cache les informations de découverte provenant du cluster afin de réduire le nombre d'appels d'API requis. Le cache est activé par défaut et est actualisé toutes les 10 minutes.

Si vous exécutez kubectl à partir d'un conteneur ou sans cache côté client, vous risquez de rencontrer des problèmes de limitation de l'API. Il est recommandé de conserver le cache de votre cluster en le montant --cache-dir pour éviter d'effectuer des appels d'API inutiles.

Désactiver la compression kubectl

La désactivation de la compression kubectl dans votre fichier kubeconfig peut réduire l'utilisation de l'API et du processeur du client. Par défaut, le serveur compresse les données envoyées au client afin d'optimiser la bande passante du réseau. Cela augmente la charge du processeur sur le client et le serveur pour chaque demande et la désactivation de la compression peut réduire la surcharge et la latence si vous disposez d'une bande passante adéquate. Pour désactiver la compression, vous pouvez utiliser l'--disable-compression=trueindicateur ou le définir disable-compression: true dans votre fichier kubeconfig.

apiVersion: v1
clusters:
- cluster:
    server: serverURL
    disable-compression: true
  name: cluster

Autoscaler Shard Cluster

Le Kubernetes Cluster Autoscaler a été testé pour évoluer jusqu'à 1 000 nœuds. Sur un cluster de grande taille comportant plus de 1 000 nœuds, il est recommandé d'exécuter plusieurs instances du Cluster Autoscaler en mode partition. Chaque instance de Cluster Autoscaler est configurée pour dimensionner un ensemble de groupes de nœuds. L'exemple suivant montre 2 configurations de mise à l'échelle automatique de cluster configurées pour chaque groupe de nœuds d'échelle 4.

ClusterAutoscaler-1

autoscalingGroups:
- name: eks-core-node-grp-20220823190924690000000011-80c1660e-030d-476d-cb0d-d04d585a8fcb
  maxSize: 50
  minSize: 2
- name: eks-data_m1-20220824130553925600000011-5ec167fa-ca93-8ca4-53a5-003e1ed8d306
  maxSize: 450
  minSize: 2
- name: eks-data_m2-20220824130733258600000015-aac167fb-8bf7-429d-d032-e195af4e25f5
  maxSize: 450
  minSize: 2
- name: eks-data_m3-20220824130553914900000003-18c167fa-ca7f-23c9-0fea-f9edefbda002
  maxSize: 450
  minSize: 2

ClusterAutoscaler-2

autoscalingGroups:
- name: eks-data_m4-2022082413055392550000000f-5ec167fa-ca86-6b83-ae9d-1e07ade3e7c4
  maxSize: 450
  minSize: 2
- name: eks-data_m5-20220824130744542100000017-02c167fb-a1f7-3d9e-a583-43b4975c050c
  maxSize: 450
  minSize: 2
- name: eks-data_m6-2022082413055392430000000d-9cc167fa-ca94-132a-04ad-e43166cef41f
  maxSize: 450
  minSize: 2
- name: eks-data_m7-20220824130553921000000009-96c167fa-ca91-d767-0427-91c879ddf5af
  maxSize: 450
  minSize: 2

Priorité et équité des API

APF

Présentation de

Pour éviter d'être surchargé pendant les périodes d'augmentation des demandes, le serveur API limite le nombre de demandes en cours qu'il peut avoir en attente à un moment donné. Une fois cette limite dépassée, le serveur API commence à rejeter les demandes et renvoie un code de réponse HTTP 429 pour « Trop de demandes » aux clients. Il est préférable que le serveur abandonne les demandes et demande aux clients de réessayer plus tard plutôt que de n'avoir aucune limite côté serveur quant au nombre de demandes et de surcharger le plan de contrôle, ce qui pourrait entraîner une dégradation des performances ou une indisponibilité.

Le mécanisme utilisé par Kubernetes pour configurer la répartition de ces demandes entrantes entre les différents types de demandes est appelé API Priority and Fairness. Le serveur API configure le nombre total de demandes en vol qu'il peut accepter en additionnant les valeurs spécifiées par les --max-requests-inflight indicateurs et. --max-mutating-requests-inflight EKS utilise les valeurs par défaut de 400 et 200 requêtes pour ces indicateurs, ce qui permet d'envoyer un total de 600 demandes à un moment donné. Cependant, à mesure qu'il adapte le plan de contrôle à des tailles plus importantes en réponse à une utilisation accrue et à une perte de charge de travail, il augmente en conséquence le quota de demandes en vol jusqu'en 2000 (sous réserve de modification). L'APF précise comment ce quota de demandes en vol est ensuite subdivisé entre les différents types de demandes. Notez que les plans de contrôle EKS sont hautement disponibles avec au moins 2 serveurs API enregistrés sur chaque cluster. Cela signifie que le nombre total de requêtes en vol que votre cluster peut gérer est le double (ou plus si vous augmentez l'échelle horizontale) du quota en vol défini par kube-apiserver. Cela représente plusieurs milliers de requests/second clusters EKS parmi les plus importants.

Deux types d'objets Kubernetes, appelés PriorityLevelConfigurations et FlowSchemas, configurent la répartition du nombre total de requêtes entre les différents types de requêtes. Ces objets sont gérés automatiquement par le serveur API et EKS utilise la configuration par défaut de ces objets pour la version mineure de Kubernetes donnée. PriorityLevelConfigurations ne représentent qu'une fraction du nombre total de demandes autorisées. Par exemple, 98 demandes sur un total de 600 PriorityLevelConfiguration sont attribuées à la charge de travail la plus élevée. La somme des requêtes allouées à l'ensemble PriorityLevelConfigurations sera égale à 600 (ou légèrement supérieure à 600 car le serveur API arrondira au chiffre supérieur si un niveau donné reçoit une fraction d'une demande). Pour vérifier l'état PriorityLevelConfigurations de votre cluster et le nombre de requêtes allouées à chacun d'entre eux, vous pouvez exécuter la commande suivante. Voici les valeurs par défaut sur EKS 1.32 :

$ kubectl get --raw /metrics | grep apiserver_flowcontrol_nominal_limit_seats apiserver_flowcontrol_nominal_limit_seats{priority_level="catch-all"} 13 apiserver_flowcontrol_nominal_limit_seats{priority_level="exempt"} 0 apiserver_flowcontrol_nominal_limit_seats{priority_level="global-default"} 49 apiserver_flowcontrol_nominal_limit_seats{priority_level="leader-election"} 25 apiserver_flowcontrol_nominal_limit_seats{priority_level="node-high"} 98 apiserver_flowcontrol_nominal_limit_seats{priority_level="system"} 74 apiserver_flowcontrol_nominal_limit_seats{priority_level="workload-high"} 98 apiserver_flowcontrol_nominal_limit_seats{priority_level="workload-low"} 245

Le deuxième type d'objet est FlowSchemas. Les requêtes du serveur d'API avec un ensemble de propriétés donné sont classées dans la même catégorie FlowSchema. Ces propriétés incluent soit l'utilisateur authentifié, soit les attributs de la demande, tels que le groupe d'API, l'espace de noms ou la ressource. A spécifie FlowSchema également à qui PriorityLevelConfiguration ce type de demande doit correspondre. Ensemble, les deux objets indiquent : « Je veux que ce type de demande soit pris en compte dans cette part des demandes en vol. » Lorsqu'une requête atteint le serveur API, celui-ci vérifie chacune des siennes FlowSchemas jusqu'à ce qu'il en trouve une qui correspond à toutes les propriétés requises. Si plusieurs requêtes FlowSchemas correspondent à une demande, le serveur API choisira celle FlowSchema dont la priorité correspondante est la plus faible, qui est spécifiée en tant que propriété dans l'objet.

Le mappage de FlowSchemas to PriorityLevelConfigurations peut être visualisé à l'aide de cette commande :

$ kubectl get flowschemas NAME PRIORITYLEVEL MATCHINGPRECEDENCE DISTINGUISHERMETHOD AGE MISSINGPL exempt exempt 1 <none> 7h19m False eks-exempt exempt 2 <none> 7h19m False probes exempt 2 <none> 7h19m False system-leader-election leader-election 100 ByUser 7h19m False endpoint-controller workload-high 150 ByUser 7h19m False workload-leader-election leader-election 200 ByUser 7h19m False system-node-high node-high 400 ByUser 7h19m False system-nodes system 500 ByUser 7h19m False kube-controller-manager workload-high 800 ByNamespace 7h19m False kube-scheduler workload-high 800 ByNamespace 7h19m False kube-system-service-accounts workload-high 900 ByNamespace 7h19m False eks-workload-high workload-high 1000 ByUser 7h14m False service-accounts workload-low 9000 ByUser 7h19m False global-default global-default 9900 ByUser 7h19m False catch-all catch-all 10000 ByUser 7h19m False

PriorityLevelConfigurations peut avoir un type de file d'attente, de rejet ou d'exemption. Pour les types Queue et Reject, une limite est imposée au nombre maximum de demandes en vol pour ce niveau de priorité, mais le comportement diffère lorsque cette limite est atteinte. Par exemple, le workload-high PriorityLevelConfiguration utilise le type Queue et dispose de 98 requêtes pouvant être utilisées par le controller-manager, le endpoint-controller, le scheduler, les contrôleurs liés à eks et provenant de pods exécutés dans l'espace de noms kube-system. Étant donné que le type Queue est utilisé, le serveur API essaiera de conserver les requêtes en mémoire et espère que le nombre de demandes en vol tombera en dessous de 98 avant l'expiration de ces demandes. Si une demande arrive à expiration dans la file d'attente ou si trop de demandes sont déjà en file d'attente, le serveur API n'a d'autre choix que de supprimer la demande et de renvoyer au client un 429. Notez que la mise en file d'attente peut empêcher une demande de recevoir un 429, mais qu'elle comporte le compromis d'une latence accrue de bout en bout sur la demande.

Considérons maintenant le fourre-tout FlowSchema qui correspond au fourre-tout avec le type PriorityLevelConfiguration Rejeter. Si les clients atteignent la limite de 13 demandes en vol, le serveur API n'exercera pas la mise en file d'attente et supprimera les demandes instantanément avec un code de réponse 429. Enfin, les requêtes correspondant à un PriorityLevelConfiguration objet de type Exempt ne recevront jamais de 429 et seront toujours expédiées immédiatement. Ceci est utilisé pour les requêtes hautement prioritaires telles que les requêtes healthz ou les requêtes provenant du groupe system:masters.

Surveillance de l'APF et des demandes abandonnées

Pour confirmer si des demandes sont abandonnées en raison de l'APF, les métriques du serveur d'API apiserver_flowcontrol_rejected_requests_total peuvent être surveillées afin de vérifier les paramètres concernés FlowSchemas et PriorityLevelConfigurations. Par exemple, cette métrique montre que 100 demandes provenant des comptes de service FlowSchema ont été abandonnées en raison de l'expiration du délai imparti dans les files d'attente où la charge de travail était faible :

% kubectl get --raw /metrics | grep apiserver_flowcontrol_rejected_requests_total
apiserver_flowcontrol_rejected_requests_total{flow_schema="service-accounts",priority_level="workload-low",reason="time-out"} 100

Pour vérifier dans quelle mesure une donnée PriorityLevelConfiguration est proche de la réception de 429 secondes ou si elle connaît une latence accrue en raison de la mise en file d'attente, vous pouvez comparer la différence entre la limite de simultanéité et la simultanéité utilisée. Dans cet exemple, nous avons une mémoire tampon de 100 requêtes.

% kubectl get --raw /metrics | grep 'apiserver_flowcontrol_nominal_limit_seats.*workload-low'
apiserver_flowcontrol_nominal_limit_seats{priority_level="workload-low"} 245

% kubectl get --raw /metrics | grep 'apiserver_flowcontrol_current_executing_seats.*workload-low'
apiserver_flowcontrol_current_executing_seats{flow_schema="service-accounts",priority_level="workload-low"} 145

Pour vérifier si une donnée PriorityLevelConfiguration est confrontée à des files d'attente mais pas nécessairement à des demandes abandonnées, la métrique de apiserver_flowcontrol_current_inqueue_requests peut être référencée :

% kubectl get --raw /metrics | grep 'apiserver_flowcontrol_current_inqueue_requests.*workload-low'
apiserver_flowcontrol_current_inqueue_requests{flow_schema="service-accounts",priority_level="workload-low"} 10

Parmi les autres indicateurs utiles de Prometheus, citons :

  • apiserver_flowcontrol_dispatched_requests_total

  • apiserver_flowcontrol_request_execution_seconds

  • apiserver_flowcontrol_request_wait_duration_seconds

Consultez la documentation en amont pour obtenir la liste complète des métriques https://kubernetes.io/docs/concepts/cluster-administration/flow-control/#observability APF.

Empêcher les demandes abandonnées

Évitez les 429 en modifiant votre charge de travail

Lorsqu'APF supprime des demandes en raison d'un PriorityLevelConfiguration dépassement du nombre maximum de demandes en vol autorisées, les clients concernés FlowSchemas peuvent réduire le nombre de demandes exécutées à un moment donné. Cela peut être accompli en réduisant le nombre total de demandes effectuées au cours de la période au cours de laquelle 429 sont effectuées. Notez que les requêtes de longue durée, telles que les appels de liste coûteux, sont particulièrement problématiques car elles sont considérées comme des demandes en vol pendant toute la durée de leur exécution. La réduction du nombre de ces demandes coûteuses ou l'optimisation de la latence de ces appels de liste (par exemple, en réduisant le nombre d'objets récupérés par demande ou en passant à une demande de surveillance) peut contribuer à réduire la simultanéité totale requise par la charge de travail donnée.

Prévenez les 429 en modifiant les paramètres de votre APF

Avertissement

Ne modifiez les paramètres APF par défaut que si vous savez ce que vous faites. Des paramètres APF mal configurés peuvent entraîner l'abandon des demandes du serveur API et des interruptions importantes de la charge de travail.

Une autre approche pour empêcher les demandes abandonnées consiste à modifier la valeur par défaut FlowSchemas ou à l' PriorityLevelConfigurations installer sur les clusters EKS. EKS installe les paramètres par défaut en amont pour FlowSchemas et PriorityLevelConfigurations pour la version mineure de Kubernetes donnée. Le serveur API réconciliera automatiquement ces objets avec leurs valeurs par défaut en cas de modification, sauf si l'annotation suivante sur les objets est définie sur false :

  metadata:
    annotations:
      apf.kubernetes.io/autoupdate-spec: "false"

À un niveau élevé, les paramètres APF peuvent être modifiés pour :

  • Allouez davantage de capacité en vol aux demandes qui vous intéressent.

  • Isolez les demandes non essentielles ou coûteuses qui peuvent réduire la capacité pour d'autres types de demandes.

Cela peut être réalisé soit en modifiant la valeur par défaut FlowSchemas , PriorityLevelConfigurations soit en créant de nouveaux objets de ces types. Les opérateurs peuvent augmenter les valeurs garanties ConcurrencyShares pour les PriorityLevelConfigurations objets concernés afin d'augmenter la part des demandes en vol qui leur sont allouées. En outre, le nombre de demandes pouvant être mises en file d'attente à un moment donné peut également être augmenté si l'application peut gérer la latence supplémentaire causée par la mise en file d'attente des demandes avant leur expédition.

Il est également possible de créer de nouveaux PriorityLevelConfigurations objets FlowSchema et spécifiques à la charge de travail du client. Sachez que l'attribution de plus de garanties ConcurrencyShares à des demandes existantes PriorityLevelConfigurations ou nouvelles PriorityLevelConfigurations entraînera une réduction du nombre de demandes pouvant être traitées par d'autres compartiments, car la limite globale restera à 600 demandes en vol par serveur API.

Lorsque vous modifiez les valeurs par défaut de l'APF, ces mesures doivent être surveillées sur un cluster hors production afin de s'assurer que la modification des paramètres n'entraîne pas de 429 secondes involontaires :

  1. La métrique de apiserver_flowcontrol_rejected_requests_total doit être surveillée pour tous FlowSchemas afin de s'assurer qu'aucun compartiment ne commence à supprimer des demandes.

  2. Les valeurs pour apiserver_flowcontrol_nominal_limit_seats et apiserver_flowcontrol_current_executing_seats doivent être comparées afin de garantir que la simultanéité utilisée ne risque pas de dépasser la limite de ce niveau de priorité.

L'isolation est un cas d'utilisation courant pour définir un nouveau FlowSchema et PriorityLevelConfiguration . Supposons que nous souhaitions isoler les appels d'événements de longue durée provenant de groupes vers leur propre part de demandes. Cela évitera que les requêtes importantes provenant de pods utilisant les comptes de service existants ne reçoivent 429 requêtes et ne soient privées FlowSchema de capacité de requêtes. N'oubliez pas que le nombre total de demandes en vol est limité. Cependant, cet exemple montre que les paramètres APF peuvent être modifiés pour mieux répartir la capacité des demandes pour une charge de travail donnée :

Exemple FlowSchema d'objet pour isoler les demandes d'événements de liste :

apiVersion: flowcontrol.apiserver.k8s.io/v1
kind: FlowSchema
metadata:
  name: list-events-default-service-accounts
spec:
  distinguisherMethod:
    type: ByUser
  matchingPrecedence: 8000
  priorityLevelConfiguration:
    name: catch-all
  rules:
  - resourceRules:
    - apiGroups:
      - '*'
      namespaces:
      - default
      resources:
      - events
      verbs:
      - list
    subjects:
    - kind: ServiceAccount
      serviceAccount:
        name: default
        namespace: default
  • Cela FlowSchema capture tous les appels d'événements de liste effectués par les comptes de service dans l'espace de noms par défaut.

  • La priorité correspondante 8000 est inférieure à la valeur de 9000 utilisée par les comptes de service existants, de FlowSchema sorte que ces appels d'événements de liste correspondront à des comptes de service par défaut à des comptes de service de liste.

  • Nous utilisons le fourre-tout PriorityLevelConfiguration pour isoler ces demandes. Ce compartiment ne permet d'utiliser que 13 demandes en vol pour ces appels d'événements de liste de longue durée. Les pods commenceront à recevoir 429 demandes dès qu'ils essaieront d'émettre plus de 13 de ces demandes simultanément.

Récupération de ressources sur le serveur API

L'obtention d'informations à partir du serveur d'API est un comportement normal pour les clusters de toutes tailles. Au fur et à mesure que vous augmentez le nombre de ressources du cluster, la fréquence des demandes et le volume de données peuvent rapidement devenir un goulot d'étranglement pour le plan de contrôle et entraîner une latence et une lenteur de l'API. En fonction de la gravité de la latence, cela entraîne des temps d'arrêt imprévus si vous ne faites pas attention.

Pour éviter ce type de problèmes, il faut d'abord être conscient de ce que vous demandez et de la fréquence à laquelle vous le demandez. Voici des conseils pour limiter le volume de requêtes en fonction des meilleures pratiques de dimensionnement. Les suggestions présentées dans cette section sont présentées dans l'ordre, en commençant par les options reconnues comme étant les plus performantes.

Utiliser des informateurs partagés

Lorsque vous créez des contrôleurs et des automatisations qui s'intègrent à l'API Kubernetes, vous devez souvent obtenir des informations auprès des ressources Kubernetes. Si vous interrogez régulièrement ces ressources, cela peut entraîner une charge importante sur le serveur API.

L'utilisation d'un informateur de la bibliothèque client-go vous permettra de surveiller les modifications apportées aux ressources en fonction des événements au lieu de les interroger. Les informateurs réduisent encore la charge en utilisant un cache partagé pour les événements et les modifications, afin que plusieurs contrôleurs surveillent les mêmes ressources n'ajoutent pas de charge supplémentaire.

Les contrôleurs doivent éviter d'interroger les ressources de l'ensemble des clusters sans étiquettes ni sélecteurs de champs, en particulier dans les grands clusters. Chaque sondage non filtré nécessite l'envoi de nombreuses données inutiles depuis etcd via le serveur API pour être filtrées par le client. En filtrant en fonction des étiquettes et des espaces de noms, vous pouvez réduire la quantité de travail que le serveur d'API doit effectuer pour traiter la demande et les données envoyées au client.

Optimisation de l'utilisation de l'API Kubernetes

Lorsque vous appelez l'API Kubernetes à l'aide de contrôleurs personnalisés ou d'une automatisation, il est important de limiter les appels aux seules ressources dont vous avez besoin. Sans limites, vous pouvez entraîner une charge inutile sur le serveur API, etc.

Il est recommandé d'utiliser l'argument watch dans la mesure du possible. En l'absence d'arguments, le comportement par défaut consiste à répertorier les objets. Pour utiliser watch au lieu de list, vous pouvez ajouter ?watch=true à la fin de votre demande d'API. Par exemple, pour obtenir tous les pods dans l'espace de noms par défaut avec une montre, utilisez :

/api/v1/namespaces/default/pods?watch=true

Si vous répertoriez des objets, vous devez limiter l'étendue de ce que vous répertoriez et la quantité de données renvoyées. Vous pouvez limiter les données renvoyées en ajoutant un limit=500 argument aux requêtes. L'fieldSelectorargument et le /namespace/ chemin peuvent être utiles pour vous assurer que la portée de vos listes est aussi étroite que nécessaire. Par exemple, pour répertorier uniquement les pods en cours d'exécution dans l'espace de noms par défaut, utilisez le chemin d'API et les arguments suivants.

/api/v1/namespaces/default/pods?fieldSelector=status.phase=Running&limit=500

Ou listez tous les pods qui fonctionnent avec :

/api/v1/pods?fieldSelector=status.phase=Running&limit=500

Une autre option pour limiter les appels de surveillance ou les objets répertoriés est d'utiliser resourceVersions, que vous pouvez consulter dans la documentation de Kubernetes. Sans resourceVersion argument, vous recevrez la version la plus récente disponible qui nécessite une lecture du quorum etcd, qui est la lecture la plus coûteuse et la plus lente de la base de données. La ResourceVersion dépend des ressources que vous essayez d'interroger et qui se trouvent dans le metadata.resourseVersion champ. Ceci est également recommandé en cas d'utilisation de la fonction Watch Calls et pas seulement de la liste des appels.

Il existe une resourceVersion=0 option spéciale qui renverra les résultats du cache du serveur API. Cela peut réduire la charge d'etcd mais ne prend pas en charge la pagination.

/api/v1/namespaces/default/pods?resourceVersion=0

Il est recommandé d'utiliser watch avec une ResourceVersion définie comme étant la dernière valeur connue reçue de sa liste ou surveillance précédente. Ceci est géré automatiquement dans client-go. Mais il est conseillé de vérifier si vous utilisez un client k8s dans d'autres langues.

/api/v1/namespaces/default/pods?watch=true&resourceVersion=362812295

Si vous appelez l'API sans aucun argument, cela consommera le plus de ressources pour le serveur API et etc. Cet appel obtiendra tous les pods dans tous les espaces de noms sans pagination ni limitation de portée et nécessitera une lecture du quorum depuis etcd.

/api/v1/pods

Prévenir le DaemonSet tonnerre des troupeaux

A DaemonSet garantit que tous (ou certains) nœuds exécutent une copie d'un pod. Lorsque les nœuds rejoignent le cluster, le daemonset-controller crée des pods pour ces nœuds. Lorsque les nœuds quittent le cluster, ces pods sont collectés à la poubelle. La suppression d'un DaemonSet nettoiera les pods qu'il a créés.

Voici quelques utilisations typiques de DaemonSet a :

  • Exécution d'un démon de stockage en cluster sur chaque nœud

  • Exécution d'un démon de collecte de journaux sur chaque nœud

  • Exécution d'un démon de surveillance des nœuds sur chaque nœud

Sur les clusters comportant des milliers de nœuds, la création d'un nouveau DaemonSet nœud DaemonSet, sa mise à jour ou l'augmentation du nombre de nœuds peuvent entraîner une charge élevée sur le plan de contrôle. Si DaemonSet les pods émettent des requêtes coûteuses au serveur d'API au démarrage des pods, ils peuvent entraîner une utilisation importante des ressources sur le plan de contrôle en raison d'un grand nombre de demandes simultanées.

En fonctionnement normal, vous pouvez utiliser un RollingUpdate pour assurer le déploiement progressif des nouveaux DaemonSet pods. Avec une stratégie de RollingUpdate mise à jour, après avoir mis à jour un DaemonSet modèle, le contrôleur supprime DaemonSet les anciens pods et en crée de nouveaux DaemonSet automatiquement de manière contrôlée. Au plus un pod DaemonSet sera exécuté sur chaque nœud pendant tout le processus de mise à jour. Vous pouvez effectuer un déploiement progressif en maxUnavailable réglant sur 1, maxSurge 0 et minReadySeconds 60. Si vous ne spécifiez pas de stratégie de mise à jour, Kubernetes créera par défaut un RollingUpdate avec maxUnavailable 1, maxSurge 0 et minReadySeconds 0.

minReadySeconds: 60 strategy: type: RollingUpdate rollingUpdate: maxSurge: 0 maxUnavailable: 1

A RollingUpdate garantit le déploiement progressif de nouveaux DaemonSet pods s'ils DaemonSet sont déjà créés et s'ils disposent du nombre attendu de Ready pods sur tous les nœuds. Des problèmes liés au tonnerre des troupeaux peuvent survenir dans certaines conditions qui ne sont pas couvertes par les RollingUpdate stratégies.

Empêchez le tonnerre des troupeaux lors de leur création DaemonSet

Par défaut, quelle que soit la RollingUpdate configuration, le daemonset-controller du kube-controller-manager crée des pods pour tous les nœuds correspondants simultanément lorsque vous en créez un nouveau. DaemonSet Pour forcer le déploiement progressif des pods après avoir créé un DaemonSet, vous pouvez utiliser a NodeSelector ouNodeAffinity. Cela créera un DaemonSet qui correspond à zéro nœud, puis vous pourrez mettre à jour progressivement les nœuds pour les rendre éligibles à l'exécution d'un pod à partir de DaemonSet à un débit contrôlé. Vous pouvez suivre cette approche :

  • Ajoutez une étiquette à tous les nœuds pourrun-daemonset=false.

kubectl label nodes --all run-daemonset=false
  • Créez le vôtre DaemonSet avec un NodeAffinity paramètre correspondant à n'importe quel nœud sans run-daemonset=false étiquette. Dans un premier temps, vous n'aurez DaemonSet pas de pods correspondants.

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: run-daemonset
          operator: NotIn
          values:
          - "false"
  • Supprimez l'run-daemonset=falseétiquette de vos nœuds à un rythme contrôlé. Vous pouvez utiliser ce script bash comme exemple :

#!/bin/bash

nodes=$(kubectl get --raw "/api/v1/nodes" | jq -r '.items | .[].metadata.name')

for node in ${nodes[@]}; do
   echo "Removing run-daemonset label from node $node"
   kubectl label nodes $node run-daemonset-
   sleep 5
done
  • Vous pouvez éventuellement supprimer le NodeAffinity paramètre de votre DaemonSet objet. Notez que cela déclenchera également un RollingUpdate et remplacera progressivement tous les DaemonSet pods existants en raison de la modification du DaemonSet modèle.

Empêchez les troupeaux tonitruants lors de l'expansion des nœuds

Comme pour DaemonSet la création, la création rapide de nouveaux nœuds peut entraîner le démarrage simultané d'un grand nombre de DaemonSet pods. Vous devez créer de nouveaux nœuds à une cadence contrôlée afin que le contrôleur crée DaemonSet des pods à la même cadence. Si cela n'est pas possible, vous pouvez rendre les nouveaux nœuds initialement inéligibles aux nœuds existants DaemonSet en utilisantNodeAffinity. Ensuite, vous pouvez ajouter progressivement une étiquette aux nouveaux nœuds afin que le daemonset-controller crée des pods à un rythme contrôlé. Vous pouvez suivre cette approche :

  • Ajoutez une étiquette à tous les nœuds existants pour run-daemonset=true

kubectl label nodes --all run-daemonset=true
  • Mettez à jour le vôtre DaemonSet avec un NodeAffinity paramètre correspondant à n'importe quel nœud doté d'une run-daemonset=true étiquette. Notez que cela déclenchera également un RollingUpdate et remplacera progressivement tous les DaemonSet pods existants en raison de la modification du DaemonSet modèle. Vous devez attendre la fin du RollingUpdate processus avant de passer à l'étape suivante.

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: run-daemonset
          operator: In
          values:
          - "true"
  • Créez de nouveaux nœuds dans votre cluster. Notez que ces nœuds n'auront pas d'run-daemonset=trueétiquette et ne DaemonSet correspondront donc pas à ces nœuds.

  • Ajoutez l'run-daemonset=trueétiquette à vos nouveaux nœuds (qui n'en ont pas actuellement) à un rythme contrôlé. run-daemonset Vous pouvez utiliser ce script bash comme exemple :

#!/bin/bash

nodes=$(kubectl get --raw "/api/v1/nodes?labelSelector=%21run-daemonset" | jq -r '.items | .[].metadata.name')

for node in ${nodes[@]}; do
   echo "Adding run-daemonset=true label to node $node"
   kubectl label nodes $node run-daemonset=true
   sleep 5
done
  • Vous pouvez éventuellement supprimer le NodeAffinity paramètre de votre DaemonSet objet et supprimer l'run-daemonsetétiquette de tous les nœuds.

Prévenez le tonnerre des troupeaux lors des mises à jour DaemonSet

Une RollingUpdate politique ne respectera que le maxUnavailable réglage des DaemonSet pods qui le sontReady. Si DaemonSet a ne contient que NotReady des pods ou un pourcentage élevé de NotReady pods et que vous mettez à jour son modèle, le daemonset-controller créera de nouveaux pods simultanément pour tous les pods. NotReady Cela peut entraîner des problèmes de tonnerre dans le troupeau s'il y a un nombre important de NotReady groupes, par exemple si les groupes sont continuellement en boucle ou s'ils ne parviennent pas à extraire des images.

Pour forcer le déploiement progressif des pods lorsque vous mettez à jour un espace DaemonSet et qu'il existe NotReady des modules, vous pouvez modifier temporairement la stratégie de mise à jour du point DaemonSet de départ RollingUpdate àOnDelete. AvecOnDelete, après avoir mis à jour un DaemonSet modèle, le contrôleur crée de nouveaux modules après que vous ayez supprimé manuellement les anciens afin que vous puissiez contrôler le déploiement des nouveaux modules. Vous pouvez suivre cette approche :

  • Vérifiez si vous avez NotReady des capsules dans votre DaemonSet.

  • Si ce n'est pas le cas, vous pouvez mettre à jour le DaemonSet modèle en toute sécurité et la RollingUpdate stratégie assurera un déploiement progressif.

  • Si oui, vous devez d'abord mettre à jour votre stratégie DaemonSet pour utiliser cette OnDelete stratégie.

updateStrategy:
  type: OnDelete
  • Ensuite, mettez à jour votre DaemonSet modèle en y apportant les modifications nécessaires.

  • Après cette mise à jour, vous pouvez supprimer les anciens DaemonSet modules en émettant des demandes de suppression de modules à un rythme contrôlé. Vous pouvez utiliser ce script bash comme exemple lorsque le DaemonSet nom est fluentd-elasticsearch dans l'espace de noms kube-system :

#!/bin/bash

daemonset_pods=$(kubectl get --raw "/api/v1/namespaces/kube-system/pods?labelSelector=name%3Dfluentd-elasticsearch" | jq -r '.items | .[].metadata.name')

for pod in ${daemonset_pods[@]}; do
   echo "Deleting pod $pod"
   kubectl delete pod $pod -n kube-system
   sleep 5
done
  • Enfin, vous pouvez DaemonSet revenir à la RollingUpdate stratégie précédente.