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.
Surveillance du plan de contrôle
Serveur d'API
Lorsque vous examinez notre serveur API, il est important de se rappeler que l'une de ses fonctions est de limiter les requêtes entrantes afin d'éviter de surcharger le plan de contrôle. Ce qui peut sembler être un goulot d'étranglement au niveau du serveur d'API pourrait en fait le protéger contre des problèmes plus graves. Nous devons prendre en compte les avantages et les inconvénients de l'augmentation du volume de demandes transitant par le système. Pour déterminer si les valeurs du serveur API doivent être augmentées, voici un petit échantillon des éléments auxquels nous devons faire attention :
-
Quelle est la latence des demandes transitant par le système ?
-
Cette latence est-elle due au serveur API lui-même ou à quelque chose « en aval » comme etcd ?
-
La profondeur de la file d'attente du serveur d'API est-elle un facteur de cette latence ?
-
Les files d'attente APF (API Priority and Fairness) sont-elles correctement configurées pour les modèles d'appels d'API que nous voulons ?
Où est le problème ?
Pour commencer, nous pouvons utiliser la métrique de latence de l'API pour avoir un aperçu du temps nécessaire au serveur d'API pour traiter les demandes. Utilisons la carte thermique ProMQL et Grafana ci-dessous pour afficher ces données.
max(increase(apiserver_request_duration_seconds_bucket{subresource!="status",subresource!="token",subresource!="scale",subresource!="/healthz",subresource!="binding",subresource!="proxy",verb!="WATCH"}[$__rate_interval])) by (le)
Note
Pour en savoir plus sur la façon de surveiller le serveur d'API à l'aide du tableau de bord d'API utilisé dans cet article, veuillez consulter le blog suivant
Ces demandes sont toutes traitées en moins d'une seconde, ce qui est une bonne indication que le plan de contrôle traite les demandes en temps opportun. Et si ce n'était pas le cas ?
Le format que nous utilisons dans la durée de demande d'API ci-dessus est une carte thermique. L'avantage du format Heatmap, c'est qu'il nous indique la valeur du délai d'expiration de l'API par défaut (60 secondes). Cependant, ce que nous devons vraiment savoir, c'est à quel seuil cette valeur devrait-elle être préoccupante avant d'atteindre le seuil de temporisation. Pour une idée approximative des seuils acceptables, nous pouvons utiliser le SLO Kubernetes en amont, qui se trouve ici https://github.com/kubernetes/community/blob/master/sig-scalability/slos/slos.md#steady-state-slisslos
Note
Avez-vous remarqué la fonction max sur cette instruction ? Lorsque vous utilisez des métriques qui regroupent plusieurs serveurs (par défaut, deux serveurs d'API sur EKS), il est important de ne pas faire la moyenne de ces serveurs ensemble.
Schémas de trafic asymétriques
Et si un serveur API [pod] était légèrement chargé et l'autre lourdement chargé ? Si nous faisions la moyenne de ces deux chiffres ensemble, nous pourrions mal interpréter ce qui se passait. Par exemple, nous avons ici trois serveurs API mais toute la charge se trouve sur l'un de ces serveurs API. En règle générale, tout ce qui possède plusieurs serveurs, tels que les serveurs ETCD et API, doit être ventilé lors de l'investissement en cas de problèmes d'échelle et de performances.
Avec le passage à la priorité et à l'équité des API, le nombre total de requêtes sur le système n'est qu'un facteur parmi d'autres pour vérifier si le serveur d'API est surabonné. Étant donné que le système fonctionne désormais sur une série de files d'attente, nous devons vérifier si l'une de ces files d'attente est pleine et si le trafic de cette file d'attente est interrompu.
Examinons ces files d'attente avec la requête suivante :
max without(instance)(apiserver_flowcontrol_nominal_limit_seats{})
Note
Pour plus d'informations sur le fonctionnement de l'API A&F, veuillez consulter le guide des meilleures pratiques suivant
Nous voyons ici les sept groupes de priorités différents qui figurent par défaut sur le cluster.
Ensuite, nous voulons voir quel pourcentage de ce groupe de priorité est utilisé, afin de comprendre si un certain niveau de priorité est saturé. Il peut être souhaitable de limiter les demandes au niveau le plus faible de la charge de travail, mais une baisse du niveau d'élection d'un chef ne le serait pas.
Le système API Priority and Fairness (APF) comporte un certain nombre d'options complexes, dont certaines peuvent avoir des conséquences imprévues. Un problème courant que nous constatons sur le terrain est l'augmentation de la profondeur de la file d'attente au point d'ajouter une latence inutile. Nous pouvons suivre ce problème à l'aide de la apiserver_flowcontrol_current_inqueue_request métrique. Nous pouvons vérifier les baisses à l'aide duapiserver_flowcontrol_rejected_requests_total. Ces métriques auront une valeur différente de zéro si un bucket dépasse sa simultanéité.
L'augmentation de la profondeur de la file d'attente peut faire du serveur API une source importante de latence et doit être effectuée avec précaution. Nous vous recommandons d'être judicieux en ce qui concerne le nombre de files d'attente créées. Par exemple, le nombre de partages sur un système EKS est de 600. Si nous créons trop de files d'attente, cela peut réduire les parts dans les files d'attente importantes nécessitant un débit suffisant, telles que la file d'attente pour l'élection du leader ou la file d'attente système. La création d'un trop grand nombre de files d'attente supplémentaires peut rendre plus difficile le dimensionnement correct de ces files d'attente.
Pour nous concentrer sur un changement simple et percutant que vous pouvez apporter à l'APF, nous prenons simplement les actions des compartiments sous-utilisés et augmentons la taille des compartiments dont l'utilisation est maximale. En redistribuant intelligemment les actions entre ces catégories, vous pouvez réduire les risques de baisse.
Pour plus d'informations, consultez les paramètres de priorité et d'équité des API dans le guide des meilleures pratiques EKS.
Latence entre API et etcd
Comment pouvons-nous utiliser le metrics/logs serveur API pour déterminer s'il y a un problème avec le serveur API, s'il s'agit d'un problème lié au serveur API, ou d'une combinaison des deux. upstream/downstream Pour mieux comprendre cela, voyons comment API Server et etcd peuvent être liés, et à quel point il peut être facile de dépanner le mauvais système.
Dans le graphique ci-dessous, nous voyons la latence du serveur API, mais nous constatons également qu'une grande partie de cette latence est corrélée au serveur etcd en raison des barres du graphique montrant la majeure partie de la latence au niveau etcd. S'il y a 15 secondes de latence etcd en même temps que 20 secondes de latence du serveur API, alors la majeure partie de la latence se situe en fait au niveau etcd.
En examinant l'ensemble du flux, nous voyons qu'il est sage de ne pas se concentrer uniquement sur le serveur API, mais également de rechercher des signaux indiquant qu'etcd est soumis à des contraintes (c'est-à-dire que les compteurs d'application lents augmentent). La capacité d'un tableau de bord à passer rapidement à la bonne zone problématique d'un simple coup d'œil est ce qui rend un tableau de bord puissant.
Note
Le tableau de bord de la section se trouve à l'adresse https://github.com/RiskyAdventure/Troubleshooting-Dashboards/blob/main/api-troubleshooter.json
Problèmes liés au plan de contrôle et au côté client
Dans ce graphique, nous recherchons les appels d'API dont l'exécution a pris le plus de temps au cours de cette période. Dans ce cas, nous voyons qu'une ressource personnalisée (CRD) appelle une fonction APPLY qui est l'appel le plus latent pendant la période de 05:40.
Grâce à ces données, nous pouvons utiliser une requête Ad-Hoc ProMQL ou CloudWatch Insights pour extraire les demandes LIST du journal d'audit pendant cette période afin de voir de quelle application il s'agit.
Trouver la source avec CloudWatch
Les métriques sont mieux utilisées pour trouver le problème que nous voulons examiner et pour affiner à la fois le calendrier et les paramètres de recherche du problème. Une fois que nous aurons ces données, nous souhaitons passer aux journaux pour des heures et des erreurs plus détaillées. Pour ce faire, nous transformerons nos logs en métriques à l'aide de CloudWatch Logs Insights.
Par exemple, pour étudier le problème ci-dessus, nous utiliserons la requête CloudWatch Logs Insights suivante pour extraire les champs UserAgent et RequestURI afin de déterminer quelle application est à l'origine de cette latence.
Note
Un nombre approprié doit être utilisé pour ne pas modifier le List/Resync comportement normal d'une montre.
fields @timestamp, @message | filter @logStream like "kube-apiserver-audit" | filter ispresent(requestURI) | filter verb = "list" | parse requestReceivedTimestamp /\d+-\d+-(?<StartDay>\d+)T(?<StartHour>\d+):(?<StartMinute>\d+):(?<StartSec>\d+).(?<StartMsec>\d+)Z/ | parse stageTimestamp /\d+-\d+-(?<EndDay>\d+)T(?<EndHour>\d+):(?<EndMinute>\d+):(?<EndSec>\d+).(?<EndMsec>\d+)Z/ | fields (StartHour * 3600 + StartMinute * 60 + StartSec + StartMsec / 1000000) as StartTime, (EndHour * 3600 + EndMinute * 60 + EndSec + EndMsec / 1000000) as EndTime, (EndTime - StartTime) as DeltaTime | stats avg(DeltaTime) as AverageDeltaTime, count(*) as CountTime by requestURI, userAgent | filter CountTime >=50 | sort AverageDeltaTime desc
À l'aide de cette requête, nous avons trouvé deux agents différents exécutant un grand nombre d'opérations de liste à latence élevée. Splunk et CloudWatch agent. Grâce à ces données, nous pouvons décider de supprimer, de mettre à jour ou de remplacer ce contrôleur par un autre projet.
Note
Pour plus de détails à ce sujet, veuillez consulter le blog suivant
Planificateur
Étant donné que les instances du plan de contrôle EKS sont exécutées dans un compte AWS distinct, nous ne serons pas en mesure de récupérer ces composants pour les métriques (le serveur API étant l'exception). Cependant, comme nous avons accès aux journaux d'audit de ces composants, nous pouvons transformer ces journaux en métriques pour voir si l'un des sous-systèmes est à l'origine d'un goulot d'étranglement en matière de dimensionnement. Utilisons CloudWatch Logs Insights pour voir combien de pods non planifiés se trouvent dans la file d'attente du planificateur.
Pods non planifiés dans le journal du planificateur
Si nous pouvions extraire les métriques du planificateur directement sur un Kubernetes autogéré (tel que Kops), nous utiliserions le ProMQL suivant pour comprendre le backlog du planificateur.
max without(instance)(scheduler_pending_pods)
Comme nous n'avons pas accès à la métrique ci-dessus dans EKS, nous utiliserons la requête CloudWatch Logs Insights ci-dessous pour voir le backlog en vérifiant combien de pods n'ont pas pu être planifiés au cours d'une période donnée. Nous pourrions ensuite approfondir les messages aux heures de pointe pour comprendre la nature du goulot d'étranglement. Par exemple, les nœuds ne tournent pas assez vite ou le limiteur de débit du planificateur lui-même.
fields timestamp, pod, err, @message
| filter @logStream like "scheduler"
| filter @message like "Unable to schedule pod"
| parse @message /^.(?<date>\d{4})\s+(?<timestamp>\d+:\d+:\d+\.\d+)\s+\S*\s+\S+\]\s\"(.*?)\"\s+pod=(?<pod>\"(.*?)\")\s+err=(?<err>\"(.*?)\")/
| count(*) as count by pod, err
| sort count desc
Ici, nous voyons les erreurs du planificateur indiquant que le pod n'a pas été déployé car le PVC de stockage n'était pas disponible.
Note
La journalisation des audits doit être activée sur le plan de contrôle pour activer cette fonction. Il est également recommandé de limiter la conservation des journaux afin de ne pas augmenter inutilement les coûts au fil du temps. Voici un exemple d'activation de toutes les fonctions de journalisation à l'aide de l'outil EKSCTL.
cloudWatch: clusterLogging: enableTypes: ["*"] logRetentionInDays: 10
Gestionnaire de contrôleurs Kube
Kube Controller Manager, comme tous les autres contrôleurs, limite le nombre d'opérations qu'il peut effectuer à la fois. Passons en revue certains de ces indicateurs en examinant une configuration KOPS dans laquelle nous pouvons définir ces paramètres.
kubeControllerManager: concurrentEndpointSyncs: 5 concurrentReplicasetSyncs: 5 concurrentNamespaceSyncs: 10 concurrentServiceaccountTokenSyncs: 5 concurrentServiceSyncs: 5 concurrentResourceQuotaSyncs: 5 concurrentGcSyncs: 20 kubeAPIBurst: 30 kubeAPIQPS: "20"
Ces contrôleurs ont des files d'attente qui se remplissent en période de taux de désabonnement élevé sur un cluster. Dans ce cas, nous voyons que le contrôleur d'ensemble de réplicasets a un arriéré important dans sa file d'attente.
Nous avons deux manières différentes de faire face à une telle situation. Si vous utilisez l'autogestion, nous pourrions simplement augmenter le nombre de goroutines simultanées, mais cela aurait un impact sur etcd en traitant davantage de données dans le KCM. L'autre option serait de réduire le nombre d'objets de réplicaset utilisés .spec.revisionHistoryLimit lors du déploiement afin de réduire le nombre d'objets de réplicaset que nous pouvons restaurer, réduisant ainsi la pression sur ce contrôleur.
spec: revisionHistoryLimit: 2
D'autres fonctionnalités de Kubernetes peuvent être ajustées ou désactivées pour réduire la pression dans les systèmes à taux de désabonnement élevé. Par exemple, si l'application de nos pods n'a pas besoin de parler directement à l'API k8s, la désactivation du secret projeté dans ces pods diminuerait la charge. ServiceaccountTokenSyncs C'est la manière la plus souhaitable de résoudre ces problèmes si possible.
kind: Pod spec: automountServiceAccountToken: false
Dans les systèmes où nous ne pouvons pas accéder aux métriques, nous pouvons à nouveau consulter les journaux pour détecter les conflits. Si nous voulions voir le nombre de demandes traitées par contrôleur ou au niveau agrégé, nous utiliserions la requête CloudWatch Logs Insights suivante.
Volume total traité par le KCM
# Query to count API qps coming from kube-controller-manager, split by controller type. # If you're seeing values close to 20/sec for any particular controller, it's most likely seeing client-side API throttling. fields @timestamp, @logStream, @message | filter @logStream like /kube-apiserver-audit/ | filter userAgent like /kube-controller-manager/ # Exclude lease-related calls (not counted under kcm qps) | filter requestURI not like "apis/coordination.k8s.io/v1/namespaces/kube-system/leases/kube-controller-manager" # Exclude API discovery calls (not counted under kcm qps) | filter requestURI not like "?timeout=32s" # Exclude watch calls (not counted under kcm qps) | filter verb != "watch" # If you want to get counts of API calls coming from a specific controller, uncomment the appropriate line below: # | filter user.username like "system:serviceaccount:kube-system:job-controller" # | filter user.username like "system:serviceaccount:kube-system:cronjob-controller" # | filter user.username like "system:serviceaccount:kube-system:deployment-controller" # | filter user.username like "system:serviceaccount:kube-system:replicaset-controller" # | filter user.username like "system:serviceaccount:kube-system:horizontal-pod-autoscaler" # | filter user.username like "system:serviceaccount:kube-system:persistent-volume-binder" # | filter user.username like "system:serviceaccount:kube-system:endpointslice-controller" # | filter user.username like "system:serviceaccount:kube-system:endpoint-controller" # | filter user.username like "system:serviceaccount:kube-system:generic-garbage-controller" | stats count(*) as count by user.username | sort count desc
Lorsque vous examinez les problèmes d'évolutivité, il est essentiel d'examiner chaque étape du processus (API, planificateur, KCM, etc.) avant de passer à la phase de dépannage détaillée. En production, vous constaterez souvent qu'il est nécessaire d'ajuster plusieurs parties de Kubernetes pour permettre au système de fonctionner de manière optimale. Il est facile de résoudre par inadvertance ce qui n'est qu'un symptôme (tel qu'un délai d'expiration d'un nœud) d'un goulot de bouteille beaucoup plus grand.
ETCD
etcd utilise un fichier mappé en mémoire pour stocker efficacement les paires clé-valeur. Il existe un mécanisme de protection permettant de définir la taille de cet espace mémoire disponible, généralement aux limites de 2, 4 et 8 Go. Moins d'objets dans la base de données signifie moins de nettoyage à effectuer par etcd lorsque les objets sont mis à jour et que les anciennes versions doivent être nettoyées. Ce processus de nettoyage des anciennes versions d'un objet est appelé compactage. Après un certain nombre d'opérations de compactage, un processus ultérieur permet de récupérer de l'espace utilisable, appelé défragmentation, qui se produit au-dessus d'un certain seuil ou selon un calendrier fixe.
Il existe quelques mesures liées à l'utilisateur que nous pouvons prendre pour limiter le nombre d'objets dans Kubernetes et ainsi réduire l'impact du processus de compactage et de défragmentation. Par exemple, Helm garde un niveau élevérevisionHistoryLimit. Cela permet de conserver les objets plus anciens, par exemple ReplicaSets sur le système, pour pouvoir effectuer des annulations. En fixant les limites de l'historique à 2, nous pouvons réduire le nombre d'objets (tels que ReplicaSets) de dix à deux, ce qui réduirait la charge sur le système.
apiVersion: apps/v1 kind: Deployment spec: revisionHistoryLimit: 2
Du point de vue de la surveillance, si les pics de latence du système se produisent selon un schéma défini espacé de plusieurs heures, il peut être utile de vérifier si ce processus de défragmentation en est la source. Nous pouvons le constater en utilisant CloudWatch Logs.
Si vous voulez voir les start/end heures de défragmentation, utilisez la requête suivante :
fields @timestamp, @message | filter @logStream like /etcd-manager/ | filter @message like /defraging|defraged/ | sort @timestamp asc