View a markdown version of this page

Überwachung der Kontrollebene - Amazon EKS

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

Überwachung der Kontrollebene

API-Server

Wenn Sie sich unseren API-Server ansehen, sollten Sie nicht vergessen, dass eine seiner Funktionen darin besteht, eingehende Anfragen zu drosseln, um eine Überlastung der Kontrollebene zu verhindern. Was auf der Ebene des API-Servers wie ein Engpass erscheinen kann, schützt ihn möglicherweise vor ernsteren Problemen. Wir müssen die Vor- und Nachteile einer Erhöhung des Volumens der Anfragen berücksichtigen, die das System durchlaufen. Um zu entscheiden, ob die API-Serverwerte erhöht werden sollten, finden Sie hier eine kleine Auswahl der Dinge, auf die wir achten müssen:

  1. Wie hoch ist die Latenz von Anfragen, die das System durchlaufen?

  2. Ist diese Latenz der API-Server selbst oder etwas „Downstream“ wie etcd?

  3. Ist die Warteschlangentiefe des API-Servers ein Faktor für diese Latenz?

  4. Sind die APF-Warteschlangen (API Priority and Fairness) für die von uns gewünschten API-Aufrufmuster korrekt eingerichtet?

Wo ist das Problem?

Zunächst können wir anhand der Metrik für die API-Latenz erkennen, wie lange es dauert, bis der API-Server Anfragen bearbeitet. Lassen Sie uns die folgende PromQL- und Grafana-Heatmap verwenden, um diese Daten anzuzeigen.

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)
Anmerkung

Eine ausführliche Beschreibung zur Überwachung des API-Servers mit dem in diesem Artikel verwendeten API-Dashboard finden Sie im folgenden Blog https://aws.amazon.com/blogs/containers/troubleshooting-amazon-eks-api-servers-with-prometheus/

Heatmap zur Dauer der API-Anfrage

Diese Anfragen liegen alle unter der Ein-Sekunden-Marke, was ein gutes Zeichen dafür ist, dass die Kontrollebene Anfragen zeitnah bearbeitet. Aber was wäre, wenn das nicht der Fall wäre?

Das Format, das wir in der obigen API-Anforderungsdauer verwenden, ist eine Heatmap. Das Schöne am Heatmap-Format ist, dass es uns standardmäßig den Timeout-Wert für die API mitteilt (60 Sekunden). Was wir jedoch wirklich wissen müssen, ist, bei welchem Schwellenwert dieser Wert von Belang sein sollte, bevor wir den Timeout-Schwellenwert erreichen. Für eine grobe Richtlinie, welche Schwellenwerte akzeptabel sind, können wir das Upstream-Kubernetes-SLO verwenden, das Sie hier finden https://github.com/kubernetes/community/blob/master/sig-scalability/slos/slos.md#steady-state-slisslos

Anmerkung

Beachten Sie die Max-Funktion in dieser Anweisung? Wenn Sie Metriken verwenden, die mehrere Server aggregieren (standardmäßig zwei API-Server auf EKS), ist es wichtig, nicht den Durchschnitt dieser Server zusammen zu berechnen.

Asymmetrische Verkehrsmuster

Was wäre, wenn ein API-Server [Pod] schwach und der andere stark ausgelastet wäre? Wenn wir den Durchschnitt dieser beiden Zahlen zusammen berechnen würden, könnten wir falsch interpretieren, was passiert ist. Hier haben wir zum Beispiel drei API-Server, aber die gesamte Last befindet sich auf einem dieser API-Server. In der Regel sollte alles, was über mehrere Server wie etcd- und API-Server verfügt, bei Investitionen in Skalierungs- und Leistungsprobleme aufgeteilt werden.

Gesamtzahl der Anfragen während des Fluges

Mit der Umstellung auf API-Priorität und Fairness ist die Gesamtzahl der Anfragen im System nur ein Faktor, anhand dessen überprüft werden kann, ob der API-Server überbucht ist. Da das System jetzt mit einer Reihe von Warteschlangen arbeitet, müssen wir überprüfen, ob eine dieser Warteschlangen voll ist und ob der Traffic für diese Warteschlange unterbrochen wird.

Schauen wir uns diese Warteschlangen mit der folgenden Abfrage an:

max without(instance)(apiserver_flowcontrol_nominal_limit_seats{})
Anmerkung

Weitere Informationen zur Funktionsweise von API A&F finden Sie im folgenden Leitfaden mit bewährten Methoden

Hier sehen wir die sieben verschiedenen Prioritätsgruppen, die standardmäßig im Cluster enthalten sind

Geteilte Parallelität

Als Nächstes wollen wir sehen, wie viel Prozent dieser Prioritätsgruppe genutzt werden, damit wir nachvollziehen können, ob eine bestimmte Prioritätsstufe überlastet ist. Eine Drosselung von Anfragen auf der Ebene der Arbeitslast mit niedriger Arbeitsbelastung wäre wünschenswert, eine Senkung der Ebene der Spitzenpolitiker jedoch nicht.

Das APF-System (API Priority and Fairness) bietet eine Reihe komplexer Optionen, von denen einige unbeabsichtigte Folgen haben können. Ein häufiges Problem, das wir in der Praxis beobachten, ist die Erhöhung der Warteschlangentiefe bis zu dem Punkt, an dem unnötige Latenz entsteht. Wir können dieses Problem anhand der apiserver_flowcontrol_current_inqueue_request Metrik überwachen. Wir können mit dem nach Tropfen suchenapiserver_flowcontrol_rejected_requests_total. Diese Metriken haben einen Wert ungleich Null, wenn ein Bucket seine Parallelität überschreitet.

Anfragen werden gerade verwendet

Eine Erhöhung der Warteschlangentiefe kann den API-Server zu einer erheblichen Latenzquelle machen und sollte mit Vorsicht durchgeführt werden. Wir empfehlen, mit der Anzahl der erstellten Warteschlangen vorsichtig umzugehen. Die Anzahl der Shares in einem EKS-System beträgt beispielsweise 600. Wenn wir zu viele Warteschlangen erstellen, kann dies die Anzahl der Anteile in wichtigen Warteschlangen reduzieren, die den Durchsatz benötigen, wie z. B. die Warteschlange für die Wahl des Führers oder die Systemwarteschlange. Wenn Sie zu viele zusätzliche Warteschlangen erstellen, kann es schwieriger werden, diese Warteschlangen richtig zu dimensionieren.

Um uns auf eine einfache, wirkungsvolle Änderung zu konzentrieren, die Sie in APF vornehmen können, nehmen wir einfach Anteile von nicht ausgelasteten Buckets und erhöhen die Größe der Buckets, die ihre maximale Auslastung erreicht haben. Indem Sie die Anteile intelligent auf diese Buckets verteilen, können Sie die Wahrscheinlichkeit von Ausfällen verringern.

Weitere Informationen finden Sie unter Einstellungen für API-Priorität und Fairness im EKS-Leitfaden für bewährte Verfahren.

API-Latenz im Vergleich zu etcd-Latenz

Wie können wir den metrics/logs des API-Servers verwenden, um festzustellen, ob ein Problem mit dem API-Server oder ein Problem upstream/downstream des API-Servers oder eine Kombination aus beidem vorliegt? Um dies besser zu verstehen, schauen wir uns an, wie API-Server und etcd zusammenhängen können und wie einfach es sein kann, Fehler im falschen System zu beheben.

In der folgenden Tabelle sehen wir die API-Serverlatenz, aber wir sehen auch, dass ein Großteil dieser Latenz mit dem etcd-Server korreliert, da die Balken in der Grafik den größten Teil der Latenz auf etcd-Ebene zeigen. Wenn es 15 Sekunden ETCD-Latenz gibt und gleichzeitig 20 Sekunden API-Serverlatenz vorhanden sind, dann liegt der Großteil der Latenz tatsächlich auf der etcd-Ebene.

Wenn wir uns den gesamten Ablauf ansehen, stellen wir fest, dass es ratsam ist, sich nicht nur auf den API-Server zu konzentrieren, sondern auch nach Signalen zu suchen, die darauf hinweisen, dass etcd unter Druck steht (d. h. dass die Zähler bei langsamer Anwendung zunehmen). Die Fähigkeit, mit nur einem Blick schnell zum richtigen Problembereich zu gelangen, macht ein Dashboard leistungsstark.

Anmerkung

Das Dashboard im Abschnitt finden Sie unter https://github.com/RiskyAdventure/Troubleshooting-Dashboards/blob/main/api-troubleshooter.json

ETD-Zwang

Probleme auf der Steuerungsebene und auf der Client-Seite

In diesem Diagramm suchen wir nach den API-Aufrufen, für deren Ausführung in diesem Zeitraum die meiste Zeit in Anspruch genommen wurde. In diesem Fall sehen wir, dass eine benutzerdefinierte Ressource (CRD) eine APPLY-Funktion aufruft, bei der es sich um den latentesten Aufruf im Zeitraum 05:40 handelt.

Langsamste Anfragen

Mit diesen Daten können wir eine Ad-Hoc PromQL- oder CloudWatch Insights-Abfrage verwenden, um während dieses Zeitraums LIST-Anfragen aus dem Audit-Log abzurufen, um zu sehen, um welche Anwendung es sich möglicherweise handelt.

Finden Sie die Quelle mit CloudWatch

Metriken eignen sich am besten, um den Problembereich zu finden, den wir untersuchen möchten, und sowohl den Zeitrahmen als auch die Suchparameter des Problems einzugrenzen. Sobald wir diese Daten haben, wollen wir zu Logs übergehen, um detailliertere Zeiten und Fehler zu erhalten. Dazu werden wir unsere Logs mithilfe CloudWatch von Logs Insights in Metriken umwandeln.

Um beispielsweise das oben genannte Problem zu untersuchen, verwenden wir die folgende CloudWatch Logs Insights-Abfrage, um UserAgent und RequestURI abzurufen, sodass wir herausfinden können, welche Anwendung diese Latenz verursacht.

Anmerkung

Es muss ein entsprechender Count verwendet werden, um List/Resync das normale Verhalten einer Uhr zu verhindern.

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

Mithilfe dieser Abfrage haben wir zwei verschiedene Agenten gefunden, die eine große Anzahl von Listenoperationen mit hoher Latenz ausführen. Splunk und CloudWatch Agent. Anhand der Daten können wir entscheiden, ob dieser Controller entfernt, aktualisiert oder durch ein anderes Projekt ersetzt werden soll.

Ergebnisse abfragen
Anmerkung

Weitere Informationen zu diesem Thema finden Sie im folgenden Blog

Scheduler

Da die EKS-Control-Plane-Instances in einem separaten AWS-Konto ausgeführt werden, können wir diese Komponenten nicht nach Metriken durchsuchen (der API-Server ist die Ausnahme). Da wir jedoch Zugriff auf die Auditprotokolle für diese Komponenten haben, können wir diese Protokolle in Metriken umwandeln, um festzustellen, ob eines der Subsysteme einen Skalierungsengpass verursacht. Lassen Sie uns CloudWatch Logs Insights verwenden, um zu sehen, wie viele ungeplante Pods sich in der Scheduler-Warteschlange befinden.

Ungeplante Pods im Scheduler-Log

Wenn wir Zugriff darauf hätten, die Scheduler-Metriken direkt auf einem selbst verwalteten Kubernetes (wie Kops) zu scrapen, würden wir das folgende PromQL verwenden, um den Scheduler-Backlog zu verstehen.

max without(instance)(scheduler_pending_pods)

Da wir in EKS keinen Zugriff auf die oben genannte Metrik haben, verwenden wir die folgende CloudWatch Logs Insights-Abfrage, um den Backlog einzusehen, indem wir überprüfen, wie viele Pods in einem bestimmten Zeitraum die Planung nicht rückgängig machen konnten. Dann könnten wir uns eingehender mit den Meldungen zu Spitzenzeiten befassen, um die Art des Engpasses zu verstehen. Zum Beispiel Knoten, die sich nicht schnell genug drehen, oder der Ratenbegrenzer im Scheduler selbst.

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

Hier sehen wir die Fehler des Schedulers, der besagt, dass der Pod nicht bereitgestellt wurde, weil der Speicher-PC nicht verfügbar war.

CloudWatch Protokolliert die Abfrage
Anmerkung

Die Auditprotokollierung muss auf der Steuerungsebene aktiviert sein, um diese Funktion zu aktivieren. Es hat sich auch bewährt, die Aufbewahrung von Protokollen zu begrenzen, um die Kosten im Laufe der Zeit nicht unnötig in die Höhe zu treiben. Ein Beispiel für das Einschalten aller Protokollierungsfunktionen mit dem EKSCTL-Tool unten.

cloudWatch: clusterLogging: enableTypes: ["*"] logRetentionInDays: 10

Kube Controller Manager

Kube Controller Manager hat, wie alle anderen Controller, Beschränkungen für die Anzahl der Operationen, die gleichzeitig ausgeführt werden können. Sehen wir uns an, was einige dieser Flags sind, indem wir uns eine KOPS-Konfiguration ansehen, in der wir diese Parameter festlegen können.

kubeControllerManager: concurrentEndpointSyncs: 5 concurrentReplicasetSyncs: 5 concurrentNamespaceSyncs: 10 concurrentServiceaccountTokenSyncs: 5 concurrentServiceSyncs: 5 concurrentResourceQuotaSyncs: 5 concurrentGcSyncs: 20 kubeAPIBurst: 30 kubeAPIQPS: "20"

Diese Controller haben Warteschlangen, die sich in Zeiten hoher Fluktuation auf einem Cluster füllen. In diesem Fall sehen wir, dass der Replicaset-Set-Controller einen großen Backlog in seiner Warteschlange hat.

Queues (Warteschlangen)

Wir haben zwei verschiedene Möglichkeiten, mit einer solchen Situation umzugehen. Bei selbstverwaltetem Betrieb könnten wir einfach die Anzahl der gleichzeitigen Goroutinen erhöhen. Dies würde sich jedoch auf etcd auswirken, da mehr Daten im KCM verarbeitet werden. Die andere Option wäre, die Anzahl der Replicaset-Objekte zu reduzieren, die bei der Bereitstellung verwendet werden, um die Anzahl der Replicaset-Objekte zu reduzieren, die wir rückgängig machen können, wodurch der Druck .spec.revisionHistoryLimit auf diesen Controller verringert wird.

spec: revisionHistoryLimit: 2

Andere Kubernetes-Funktionen können optimiert oder deaktiviert werden, um den Druck in Systemen mit hoher Abwanderungsrate zu reduzieren. Wenn die Anwendung in unseren Pods beispielsweise nicht direkt mit der k8s-API kommunizieren muss, würde das Ausschalten des projizierten Secrets in diesen Pods die Belastung verringern. ServiceaccountTokenSyncs Dies ist der wünschenswertere Weg, um solche Probleme nach Möglichkeit anzugehen.

kind: Pod spec: automountServiceAccountToken: false

In Systemen, in denen wir keinen Zugriff auf die Metriken haben, können wir uns die Protokolle erneut ansehen, um Konflikte zu erkennen. Wenn wir die Anzahl der Anfragen sehen wollten, die pro Controller oder auf aggregierter Ebene verarbeitet werden, würden wir die folgende CloudWatch Logs Insights-Abfrage verwenden.

Gesamtvolumen, das vom KCM verarbeitet wurde

# 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

Die wichtigste Erkenntnis dabei ist, dass bei der Untersuchung von Skalierbarkeitsproblemen jeder Schritt auf dem Pfad (API, Scheduler, KCM usw.) untersucht werden muss, bevor mit der detaillierten Fehlerbehebungsphase begonnen wird. In der Produktion werden Sie häufig feststellen, dass Anpassungen an mehr als einem Teil von Kubernetes erforderlich sind, damit das System optimal arbeitet. Es ist leicht, versehentlich Fehler zu beheben, die lediglich ein Symptom (z. B. ein Node-Timeout) eines viel größeren Engpasses sind.

USW

etcd verwendet eine Datei, die dem Speicher zugeordnet ist, um Schlüsselwertpaare effizient zu speichern. Es gibt einen Schutzmechanismus, um die Größe dieses verfügbaren Speicherplatzes festzulegen, der üblicherweise auf 2, 4 und 8 GB begrenzt wird. Weniger Objekte in der Datenbank bedeuten weniger Aufräumarbeiten, die etcd durchführen muss, wenn Objekte aktualisiert werden und ältere Versionen bereinigt werden müssen. Dieser Vorgang, bei dem alte Versionen eines Objekts entfernt werden, wird als Komprimierung bezeichnet. Nach einer Reihe von Verdichtungsvorgängen folgt ein weiterer Prozess, bei dem nutzbarer Speicherplatz wiederhergestellt wird, der als Defragmentierung bezeichnet wird. Dieser Vorgang erfolgt oberhalb eines bestimmten Schwellenwerts oder nach einem festen Zeitplan.

Es gibt eine Reihe benutzerbezogener Maßnahmen, mit denen wir die Anzahl der Objekte in Kubernetes einschränken und so die Auswirkungen sowohl des Komprimierungs- als auch des Defragmentierungsprozesses verringern können. Zum Beispiel bleibt Helm auf einem hohen Niveau. revisionHistoryLimit Dadurch bleiben ältere Objekte, z. B. ReplicaSets auf dem System, erhalten, um Rollbacks durchführen zu können. Indem wir die Historiengrenzen auf 2 reduzieren, können wir die Anzahl der Objekte (wie ReplicaSets) von zehn auf zwei reduzieren, was wiederum das System weniger belasten würde.

apiVersion: apps/v1 kind: Deployment spec: revisionHistoryLimit: 2

Aus Sicht der Überwachung kann es hilfreich sein, zu überprüfen, ob dieser Defragmentierungsprozess die Ursache ist, wenn Systemlatenzspitzen in einem festgelegten Muster auftreten, das durch Stunden getrennt ist. Wir können das anhand von CloudWatch Logs sehen.

Wenn Sie die start/end Zeiten der Defragmentierung sehen möchten, verwenden Sie die folgende Abfrage:

fields @timestamp, @message
| filter @logStream like /etcd-manager/
| filter @message like /defraging|defraged/
| sort @timestamp asc
Abfrage defragmentieren