Kubernetes-Steuerebene - 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.

Kubernetes-Steuerebene

Die Kubernetes-Steuerebene besteht aus dem Kubernetes API-Server, dem Kubernetes Controller Manager, dem Scheduler und anderen Komponenten, die für das Funktionieren von Kubernetes erforderlich sind. Die Skalierbarkeitsgrenzen dieser Komponenten sind je nachdem, was Sie im Cluster ausführen, unterschiedlich. Zu den Bereichen mit den größten Auswirkungen auf die Skalierung gehören jedoch die Kubernetes-Version, die Auslastung und die Skalierung einzelner Knoten.

Begrenzen Sie Arbeitslast und Node-Bursting

Wichtig

Um zu vermeiden, dass API-Grenzwerte auf der Steuerungsebene erreicht werden, sollten Sie Skalierungsspitzen begrenzen, die die Clustergröße jeweils um zweistellige Prozentsätze erhöhen (z. B. 1000 Knoten auf 1100 Knoten oder 4000 bis 4500 Pods gleichzeitig).

Die EKS-Steuerungsebene wird automatisch skaliert, wenn Ihr Cluster wächst, aber es gibt Grenzen, wie schnell sie skaliert werden kann. Wenn Sie zum ersten Mal einen EKS-Cluster erstellen, kann die Steuerungsebene nicht sofort auf Hunderte von Knoten oder Tausende von Pods skaliert werden. Weitere Informationen darüber, wie EKS die Skalierung verbessert hat, finden Sie in diesem Blogbeitrag.

Für die Skalierung großer Anwendungen muss sich die Infrastruktur anpassen, damit sie vollständig einsatzbereit ist (z. B. Load Balancer, die sich erwärmen). Um die Geschwindigkeit der Skalierung zu kontrollieren, stellen Sie sicher, dass Sie auf der Grundlage der richtigen Metriken für Ihre Anwendung skalieren. Die CPU- und Speicherskalierung kann Ihre Anwendungsbeschränkungen möglicherweise nicht genau vorhersagen, und die Verwendung benutzerdefinierter Metriken (z. B. Anfragen pro Sekunde) in Kubernetes Horizontal Pod Autoscaler (HPA) ist möglicherweise eine bessere Skalierungsoption.

Informationen zur Verwendung einer benutzerdefinierten Metrik finden Sie in den Beispielen in der Kubernetes-Dokumentation. Wenn Sie erweiterte Skalierungsanforderungen haben oder auf der Grundlage externer Quellen (z. B. AWS SQS-Warteschlange) skalieren müssen, verwenden Sie KEDA für die ereignisbasierte Workload-Skalierung.

Skalieren Sie Knoten und Pods sicher herunter

Ersetzen Sie Instanzen mit langer Laufzeit

Durch den regelmäßigen Austausch von Knoten bleibt Ihr Cluster funktionsfähig, da Konfigurationsabweichungen und Probleme vermieden werden, die erst nach längerer Verfügbarkeit auftreten (z. B. langsame Speicherlecks). Durch den automatisierten Austausch erhalten Sie bewährte Verfahren und Verfahren für Knoten-Upgrades und Sicherheitspatches. Wenn jeder Knoten in Ihrem Cluster regelmäßig ausgetauscht wird, ist weniger Aufwand erforderlich, um separate Prozesse für die laufende Wartung aufrechtzuerhalten.

Verwenden Sie die TTL-Einstellungen (Time to Live) von Karpenter, um Instanzen zu ersetzen, nachdem sie eine bestimmte Zeit lang ausgeführt wurden. Selbstverwaltete Knotengruppen können die max-instance-lifetime Einstellung verwenden, um Knoten automatisch zu wechseln. Verwaltete Knotengruppen verfügen derzeit nicht über diese Funktion, aber Sie können die Anfrage hier verfolgen GitHub.

Nicht ausgelastete Knoten entfernen

Sie können Knoten entfernen, wenn sie keine laufenden Workloads haben, indem Sie den Schwellenwert für das Herunterskalieren im Kubernetes Cluster Autoscaler mit --scale-down-utilization-thresholdoder in Karpenter können Sie die Provisioner-Einstellung verwenden. ttlSecondsAfterEmpty

Verwenden Sie Budgets für Pod-Unterbrechungen und das sichere Herunterfahren von Knoten

Um Pods und Knoten aus einem Kubernetes-Cluster zu entfernen, müssen Controller Aktualisierungen an mehreren Ressourcen vornehmen (z. B. EndpointSlices). Wenn Sie dies häufig oder zu schnell tun, kann dies zu einer Drosselung des API-Servers und zu Anwendungsausfällen führen, wenn Änderungen auf die Controller übertragen werden. Budgets für Pod-Unterbrechungen sind eine bewährte Methode, um die Fluktuation zu verlangsamen und so die Verfügbarkeit von Workloads zu schützen, wenn Knoten in einem Cluster entfernt oder neu geplant werden.

Verwenden Sie den clientseitigen Cache, wenn Sie Kubectl ausführen

Wenn Sie den Befehl kubectl ineffizient verwenden, kann dies zu zusätzlicher Belastung des Kubernetes-API-Servers führen. Sie sollten vermeiden, Skripts oder Automatisierungen auszuführen, die kubectl wiederholt verwenden (z. B. in einer For-Schleife), oder Befehle ohne lokalen Cache auszuführen.

kubectlhat einen clientseitigen Cache, der Erkennungsinformationen aus dem Cluster zwischenspeichert, um die Anzahl der erforderlichen API-Aufrufe zu reduzieren. Der Cache ist standardmäßig aktiviert und wird alle 10 Minuten aktualisiert.

Wenn Sie kubectl aus einem Container oder ohne clientseitigen Cache ausführen, können Probleme mit der API-Drosselung auftreten. Es wird empfohlen, den Cluster-Cache beizubehalten, indem Sie ihn mounten, um unnötige API-Aufrufe --cache-dir zu vermeiden.

Deaktivieren Sie die Kubectl-Komprimierung

Das Deaktivieren der kubectl-Komprimierung in Ihrer kubeconfig-Datei kann die API- und Client-CPU-Auslastung reduzieren. Standardmäßig komprimiert der Server die an den Client gesendeten Daten, um die Netzwerkbandbreite zu optimieren. Dadurch erhöht sich die CPU-Last auf dem Client und dem Server bei jeder Anfrage, und wenn Sie über ausreichend Bandbreite verfügen, können durch Deaktivieren der Komprimierung der Overhead und die Latenz reduziert werden. Um die Komprimierung zu deaktivieren, können Sie das --disable-compression=true Flag verwenden oder es disable-compression: true in Ihrer kubeconfig-Datei festlegen.

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

Shard Cluster Autoscaler

Der Kubernetes Cluster Autoscaler wurde für die Skalierung auf bis zu 1000 Knoten getestet. Auf einem großen Cluster mit mehr als 1000 Knoten wird empfohlen, mehrere Instanzen des Cluster Autoscaler im Shard-Modus auszuführen. Jede Cluster Autoscaler-Instanz ist so konfiguriert, dass sie eine Reihe von Knotengruppen skaliert. Das folgende Beispiel zeigt zwei Cluster-Autoscaling-Konfigurationen, die für jeweils 4 Knotengruppen konfiguriert sind.

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

API-Priorität und Fairness

APF

Übersicht

Um sich bei häufigen Anfragen vor Überlastung zu schützen, begrenzt der API-Server die Anzahl der Inflight-Anfragen, die zu einem bestimmten Zeitpunkt noch ausstehen können. Sobald dieses Limit überschritten wird, beginnt der API-Server, Anfragen abzulehnen und gibt einen 429-HTTP-Antwortcode für „Zu viele Anfragen“ an die Clients zurück. Es ist vorzuziehen, dass der Server Anfragen verwirft und die Clients es später erneut versuchen, als keine serverseitigen Beschränkungen für die Anzahl der Anfragen festzulegen und die Steuerungsebene zu überlasten, was zu Leistungseinbußen oder Nichtverfügbarkeit führen könnte.

Der Mechanismus, mit dem Kubernetes konfiguriert, wie diese Inflight-Anfragen auf verschiedene Anfragetypen aufgeteilt werden, wird API-Priorität und Fairness genannt. Der API-Server konfiguriert die Gesamtzahl der Anfragen an Bord, die er annehmen kann, indem er die durch die Flags und angegebenen Werte summiert. --max-requests-inflight --max-mutating-requests-inflight EKS verwendet die Standardwerte von 400 und 200 Anfragen für diese Flags, sodass insgesamt 600 Anfragen zu einem bestimmten Zeitpunkt versendet werden können. Da die Steuerungsebene jedoch als Reaktion auf die zunehmende Auslastung und die Fluktuation der Arbeitslast auf größere Größen skaliert wird, wird die Quote für Anfragen während der Übertragung bis 2000 entsprechend erhöht (Änderungen vorbehalten). APF gibt an, wie diese Quote für Anfragen während des Fluges weiter auf verschiedene Anforderungstypen aufgeteilt wird. Beachten Sie, dass EKS-Kontrollebenen hochverfügbar sind und mindestens 2 API-Server für jeden Cluster registriert sind. Das bedeutet, dass die Gesamtzahl der Inflight-Anfragen, die Ihr Cluster verarbeiten kann, doppelt so hoch ist (oder höher, wenn die horizontale Skalierung weiter ausgebaut wird) wie die pro Kube-Apiserver festgelegte Inflight-Quote. Dies entspricht mehreren Tausend auf den größten EKS-Clustern. requests/second

Zwei Arten von Kubernetes-Objekten, genannt PriorityLevelConfigurations und FlowSchemas, konfigurieren, wie die Gesamtzahl der Anfragen auf verschiedene Anforderungstypen aufgeteilt wird. Diese Objekte werden vom API-Server automatisch verwaltet und EKS verwendet die Standardkonfiguration dieser Objekte für die jeweilige Kubernetes-Nebenversion. PriorityLevelConfigurations stellen einen Bruchteil der Gesamtzahl der zulässigen Anfragen dar. Beispielsweise werden dem Workload-High PriorityLevelConfiguration 98 der insgesamt 600 Anfragen zugewiesen. Die Summe der allen PriorityLevelConfigurations zugewiesenen Anfragen entspricht 600 (oder etwas mehr als 600, da der API-Server aufrundet, wenn einer bestimmten Stufe nur ein Bruchteil einer Anfrage gewährt wird). Um den Wert PriorityLevelConfigurations in Ihrem Cluster und die Anzahl der einzelnen Anfragen zu überprüfen, können Sie den folgenden Befehl ausführen. Dies sind die Standardeinstellungen für 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

Der zweite Objekttyp ist. FlowSchemas API-Serveranfragen mit einem bestimmten Satz von Eigenschaften werden denselben Kategorien zugeordnet FlowSchema. Zu diesen Eigenschaften gehören entweder der authentifizierte Benutzer oder Attribute der Anfrage, z. B. die API-Gruppe, der Namespace oder die Ressource. A gibt FlowSchema auch an, welcher Art von Anfrage PriorityLevelConfiguration dieser Anfragetyp zugeordnet werden soll. Die beiden Objekte zusammen sagen: „Ich möchte, dass diese Art von Anfrage auf diesen Anteil der Anfragen während des Fluges angerechnet wird.“ Wenn eine Anfrage den API-Server erreicht, überprüft dieser jede Anfrage, FlowSchemas bis eine gefunden wird, die allen erforderlichen Eigenschaften entspricht. Wenn mehrere mit einer Anfrage FlowSchemas übereinstimmen, wählt der API-Server die FlowSchema mit der kleinsten übereinstimmenden Priorität aus, die als Eigenschaft im Objekt angegeben ist.

Die Zuordnung von FlowSchemas to PriorityLevelConfigurations kann mit diesem Befehl eingesehen werden:

$ 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 kann den Typ Queue, Reject oder Exempt haben. Bei den Typen Queue und Reject gilt für diese Prioritätsstufe ein Limit für die maximale Anzahl von Inflight-Anfragen. Das Verhalten ist jedoch unterschiedlich, wenn dieses Limit erreicht wird. Der Workload-High PriorityLevelConfiguration verwendet beispielsweise den Typ Queue und verfügt über 98 Anfragen, die vom Controller-Manager, Endpoint-Controller, Scheduler, EKS-bezogenen Controllern und von Pods, die im Kube-System-Namespace ausgeführt werden, verwendet werden können. Da der Typ Queue verwendet wird, versucht der API-Server, Anfragen im Speicher zu belassen und hofft, dass die Anzahl der Inflight-Anfragen unter 98 fällt, bevor diese Anfragen das Timeout überschreiten. Wenn bei einer bestimmten Anfrage ein Timeout in der Warteschlange auftritt oder wenn zu viele Anfragen bereits in der Warteschlange stehen, hat der API-Server keine andere Wahl, als die Anfrage zu verwerfen und dem Client eine 429 zurückzugeben. Beachten Sie, dass eine Warteschlange zwar verhindern kann, dass eine Anfrage eine 429-Nummer erhält, dass sie jedoch mit einer erhöhten end-to-end Latenz bei der Anfrage einhergeht.

Betrachten Sie nun den Catch-All FlowSchema , der dem Catch-All mit dem Typ Reject zugeordnet PriorityLevelConfiguration ist. Wenn Clients das Limit von 13 Inflight-Anfragen erreichen, führt der API-Server keine Warteschlangen durch und verwirft die Anfragen sofort mit dem Antwortcode 429. Und schließlich erhalten Anfragen, die einer PriorityLevelConfiguration vom Typ Exempt zugeordnet sind, niemals eine 429 und werden immer sofort versendet. Dies wird für Anfragen mit hoher Priorität wie Healthz-Anfragen oder Anfragen aus der Gruppe system:masters verwendet.

APF und verworfene Anfragen werden überwacht

Um zu überprüfen, ob Anfragen aufgrund von APF gelöscht wurden, apiserver_flowcontrol_rejected_requests_total können die API-Server-Metriken überwacht werden, um die betroffenen FlowSchemas und zu überprüfen. PriorityLevelConfigurations Diese Metrik zeigt beispielsweise, dass 100 Anfragen von den Service-Accounts aufgrund eines Timeouts bei Anfragen in Warteschlangen mit niedriger Auslastung verworfen FlowSchema wurden:

% 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

Um zu überprüfen, wie nah eine bestimmte Person daran PriorityLevelConfiguration ist, 429 Sekunden zu empfangen oder aufgrund von Warteschlangen eine erhöhte Latenz zu erleben, können Sie den Unterschied zwischen dem Parallelitätslimit und der verwendeten Parallelität vergleichen. In diesem Beispiel haben wir einen Puffer von 100 Anfragen.

% 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

Um zu überprüfen, ob bei einer bestimmten PriorityLevelConfiguration Anfrage Warteschlangen, aber nicht unbedingt verworfene Anfragen auftreten, apiserver_flowcontrol_current_inqueue_requests kann auf die Metrik für verwiesen werden:

% 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

Weitere nützliche Prometheus-Metriken sind:

  • apiserver_flowcontrol_dispatched_requests_total

  • apiserver_flowcontrol_request_execution_seconds

  • apiserver_flowcontrol_request_wait_duration_seconds

Eine vollständige Liste der APF-Metriken finden Sie in der Upstream-Dokumentation.

Verhinderung verworfener Anfragen

Vermeiden Sie 429s, indem Sie Ihren Workload ändern

Wenn APF Anfragen aufgrund einer bestimmten PriorityLevelConfiguration Überschreitung der maximal zulässigen Anzahl von Inflight-Anfragen verwirft, FlowSchemas können die betroffenen Clients die Anzahl der Anfragen, die zu einem bestimmten Zeitpunkt ausgeführt werden, verringern. Dies kann erreicht werden, indem die Gesamtzahl der Anfragen reduziert wird, die in dem Zeitraum gestellt wurden, in dem 429 Anfragen gestellt wurden. Beachten Sie, dass Anfragen mit langer Laufzeit, wie z. B. teure Listenanrufe, besonders problematisch sind, da sie für die gesamte Dauer ihrer Ausführung als Inflight-Anfragen gelten. Wenn Sie die Anzahl dieser teuren Anfragen reduzieren oder die Latenz dieser Listenaufrufen optimieren (z. B. indem Sie die Anzahl der pro Anfrage abgerufenen Objekte reduzieren oder zur Verwendung einer Überwachungsanforderung wechseln), können Sie dazu beitragen, die Gesamtparallelität zu reduzieren, die für die jeweilige Arbeitslast erforderlich ist.

Vermeiden Sie 429s, indem Sie Ihre APF-Einstellungen ändern

Warnung

Ändern Sie die APF-Standardeinstellungen nur, wenn Sie wissen, was Sie tun. Falsch konfigurierte APF-Einstellungen können zu verworfenen API-Serveranfragen und erheblichen Arbeitsauslastungsunterbrechungen führen.

Ein weiterer Ansatz zur Verhinderung verworfener Anfragen besteht darin, die Standardeinstellung FlowSchemas oder die PriorityLevelConfigurations Installation auf EKS-Clustern zu ändern. EKS installiert die Upstream-Standardeinstellungen für FlowSchemas und PriorityLevelConfigurations für die angegebene Kubernetes-Nebenversion. Der API-Server gleicht diese Objekte automatisch mit ihren Standardeinstellungen ab, sofern sie nicht geändert werden, es sei denn, die folgende Anmerkung zu den Objekten ist auf „Falsch“ gesetzt:

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

Auf hoher Ebene können die APF-Einstellungen wie folgt geändert werden:

  • Weisen Sie Anfragen, die Ihnen wichtig sind, mehr Kapazität an Bord zu.

  • Isolieren Sie unwichtige oder teure Anfragen, die zu Kapazitätsverlusten für andere Anforderungstypen führen können.

Dies kann erreicht werden, indem entweder die Standardeinstellung FlowSchemas geändert PriorityLevelConfigurations oder neue Objekte dieser Typen erstellt werden. Betreiber können die Werte assuredConcurrencyShares für die entsprechenden PriorityLevelConfigurations Objekte erhöhen, um den Anteil der ihnen zugewiesenen Anfragen während des Fluges zu erhöhen. Darüber hinaus kann die Anzahl der Anfragen, die zu einem bestimmten Zeitpunkt in die Warteschlange gestellt werden können, erhöht werden, wenn die Anwendung die zusätzliche Latenz bewältigen kann, die dadurch entsteht, dass Anfragen vor dem Versand in die Warteschlange gestellt werden.

Alternativ können neue FlowSchema PriorityLevelConfigurations Objekte erstellt werden, die speziell auf die Arbeitslast des Kunden zugeschnitten sind. Beachten Sie, dass die Anzahl der Anfragen, die von anderen Buckets bearbeitet PriorityLevelConfigurations werden können, reduziert wird, wenn Sie entweder vorhandenen PriorityLevelConfigurations oder neuen mehr assuredConcurrencyShares Anfragen zuweisen, da das Gesamtlimit bei 600 Inflight pro API-Server bleibt.

Wenn Sie Änderungen an den APF-Standardeinstellungen vornehmen, sollten diese Metriken auf einem Cluster außerhalb der Produktion überwacht werden, um sicherzustellen, dass eine Änderung der Einstellungen nicht zu unbeabsichtigten 429s führt:

  1. Die Metrik für apiserver_flowcontrol_rejected_requests_total sollte für alle überwacht werden, FlowSchemas um sicherzustellen, dass keine Buckets anfangen, Anfragen zu verwerfen.

  2. Die Werte für apiserver_flowcontrol_nominal_limit_seats und apiserver_flowcontrol_current_executing_seats sollten miteinander verglichen werden, um sicherzustellen, dass bei der Verwendung von Parallelität kein Risiko besteht, dass das Limit für diese Prioritätsstufe überschritten wird.

Ein häufiger Anwendungsfall für die Definition eines neuen FlowSchema und PriorityLevelConfiguration ist die Isolierung. Nehmen wir an, wir möchten lang andauernde Listenereignisaufrufe von Pods auf ihren eigenen Anteil an Anfragen isolieren. Dadurch wird verhindert, dass wichtige Anfragen von Pods, die die vorhandenen Dienstkonten verwenden, 429 Anfragen FlowSchema erhalten und ihnen die Anforderungskapazität entzogen wird. Denken Sie daran, dass die Gesamtzahl der Anfragen während der Übertragung begrenzt ist. Dieses Beispiel zeigt jedoch, dass die APF-Einstellungen geändert werden können, um die Anforderungskapazität besser auf die jeweilige Arbeitslast aufzuteilen:

FlowSchema Beispielobjekt zur Isolierung von Listenereignisanfragen:

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
  • Es FlowSchema erfasst alle Listenereignisaufrufen, die von Dienstkonten im Standard-Namespace getätigt wurden.

  • Die Vergleichspriorität 8000 ist niedriger als der Wert 9000, der von den vorhandenen Dienstkonten verwendet wird, FlowSchema sodass diese Listenereignisaufrufe eher mit list-events-default-service -accounts als mit service-accounts übereinstimmen.

  • Wir verwenden den Catch-All, um diese Anfragen PriorityLevelConfiguration zu isolieren. In diesem Bucket können nur 13 eingehende Anfragen für diese lang andauernden List-Event-Aufrufe verwendet werden. Pods erhalten ab sofort 429 Anfragen, sobald sie versuchen, mehr als 13 dieser Anfragen gleichzeitig zu stellen.

Ressourcen auf dem API-Server werden abgerufen

Das Abrufen von Informationen vom API-Server ist ein erwartetes Verhalten für Cluster jeder Größe. Wenn Sie die Anzahl der Ressourcen im Cluster skalieren, können die Häufigkeit der Anfragen und das Datenvolumen schnell zu einem Engpass für die Kontrollebene werden und zu Latenz und Langsamkeit der API führen. Je nach Schweregrad der Latenz kann es zu unerwarteten Ausfallzeiten kommen, wenn Sie nicht vorsichtig sind.

Zu wissen, was Sie anfordern und wie oft, sind die ersten Schritte, um diese Art von Problemen zu vermeiden. Im Folgenden finden Sie Anleitungen zur Begrenzung des Abfragevolumens auf der Grundlage der bewährten Skalierungsmethoden. In diesem Abschnitt werden die Vorschläge in der Reihenfolge gegeben, mit den Optionen zu beginnen, von denen bekannt ist, dass sie sich am besten skalieren lassen.

Verwenden Sie Shared Informers

Bei der Entwicklung von Controllern und Automatisierungen, die in die Kubernetes-API integriert sind, müssen Sie häufig Informationen aus Kubernetes-Ressourcen abrufen. Wenn Sie regelmäßig nach diesen Ressourcen fragen, kann dies zu einer erheblichen Belastung des API-Servers führen.

Wenn Sie einen Informer aus der Client-Go-Bibliothek verwenden, haben Sie den Vorteil, dass Sie anhand von Ereignissen nach Änderungen an den Ressourcen Ausschau halten können, anstatt nach Änderungen zu suchen. Informer reduzieren die Auslastung weiter, indem sie einen gemeinsamen Cache für die Ereignisse und Änderungen verwenden, sodass mehrere Controller, die dieselben Ressourcen überwachen, keine zusätzliche Last verursachen.

Controller sollten es vermeiden, clusterweite Ressourcen ohne Labels und Feldselektoren abzufragen, insbesondere in großen Clustern. Bei jeder ungefilterten Abfrage müssen viele unnötige Daten von etcd über den API-Server gesendet werden, um vom Client gefiltert zu werden. Durch Filtern auf der Grundlage von Labels und Namespaces können Sie den Arbeitsaufwand reduzieren, den der API-Server ausführen muss, um die Anfrage und die an den Client gesendeten Daten zu erfüllen.

Optimieren Sie die Nutzung der Kubernetes-API

Wenn Sie die Kubernetes-API mit benutzerdefinierten Controllern oder Automatisierung aufrufen, ist es wichtig, dass Sie die Aufrufe nur auf die Ressourcen beschränken, die Sie benötigen. Ohne Einschränkungen können Sie den API-Server usw. unnötig belasten.

Es wird empfohlen, wann immer möglich das Watch-Argument zu verwenden. Ohne Argumente ist das Standardverhalten das Auflisten von Objekten. Um watch statt list zu verwenden, können Sie es ?watch=true an das Ende Ihrer API-Anfrage anhängen. Um beispielsweise alle Pods im Standard-Namespace mit einer Uhr abzurufen, verwenden Sie:

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

Wenn Sie Objekte auflisten, sollten Sie den Umfang dessen, was Sie auflisten, und die Menge der zurückgegebenen Daten einschränken. Sie können die zurückgegebenen Daten einschränken, indem Sie den Anfragen limit=500 Argumente hinzufügen. Das fieldSelector Argument und der /namespace/ Pfad können nützlich sein, um sicherzustellen, dass Ihre Listen so eng wie nötig abgegrenzt sind. Um beispielsweise nur laufende Pods im Standard-Namespace aufzulisten, verwenden Sie den folgenden API-Pfad und die folgenden Argumente.

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

Oder listen Sie alle Pods auf, die ausgeführt werden mit:

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

Eine weitere Option, um Watch-Aufrufe oder aufgelistete Objekte einzuschränken, ist resourceVersionsdie Verwendung dieser Option. Weitere Informationen finden Sie in der Kubernetes-Dokumentation. Ohne resourceVersion Argument erhalten Sie die neueste verfügbare Version, für die ein etcd-Quorum-Read erforderlich ist. Dies ist der teuerste und langsamste Lesevorgang für die Datenbank. Die ResourceVersion hängt davon ab, welche Ressourcen Sie abfragen möchten, und kann im Feld gefunden werden. metadata.resourseVersion Dies wird auch empfohlen, wenn Sie Watch-Anrufe und nicht nur Listenanrufe verwenden

Es ist ein spezielles Programm resourceVersion=0 verfügbar, das Ergebnisse aus dem API-Server-Cache zurückgibt. Dies kann die Belastung von etcd reduzieren, unterstützt aber keine Paginierung.

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

Es wird empfohlen, watch mit einer ResourceVersion zu verwenden, die auf den neuesten bekannten Wert gesetzt ist, der aus der vorherigen Liste oder Überwachung empfangen wurde. Dies wird automatisch in Client-Go abgewickelt. Es wird jedoch empfohlen, dies noch einmal zu überprüfen, wenn Sie einen k8s-Client in anderen Sprachen verwenden.

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

Wenn Sie die API ohne Argumente aufrufen, ist sie für den API-Server usw. am ressourcenintensivsten. Dieser Aufruf ruft alle Pods in allen Namespaces ab, ohne Seitennummerierung oder Einschränkung des Gültigkeitsbereichs und erfordert ein Quorum, das aus etcd gelesen wird.

/api/v1/pods

DaemonSet Beugen Sie donnernden Herden vor

A DaemonSet stellt sicher, dass auf allen (oder einigen) Knoten eine Kopie eines Pods ausgeführt wird. Wenn Knoten dem Cluster beitreten, erstellt der Daemonset-Controller Pods für diese Knoten. Wenn Knoten den Cluster verlassen, werden diese Pods im Müll gesammelt. Durch das Löschen eines DaemonSet werden die erstellten Pods bereinigt.

Einige typische Verwendungen von a DaemonSet sind:

  • Auf jedem Knoten wird ein Cluster-Speicher-Daemon ausgeführt

  • Auf jedem Knoten wird ein Daemon zur Erfassung von Protokollen ausgeführt

  • Auf jedem Knoten wird ein Daemon zur Knotenüberwachung ausgeführt

In Clustern mit Tausenden von Knoten kann das Erstellen eines neuen DaemonSet, das Aktualisieren eines DaemonSet oder das Erhöhen der Anzahl von Knoten zu einer hohen Belastung der Steuerungsebene führen. Wenn DaemonSet Pods beim Start des Pods teure API-Serveranfragen ausgeben, können sie aufgrund einer großen Anzahl gleichzeitiger Anfragen zu einem hohen Ressourcenverbrauch auf der Steuerungsebene führen.

Im Normalbetrieb können Sie a verwenden, RollingUpdate um die schrittweise Einführung neuer DaemonSet Pods sicherzustellen. Bei einer RollingUpdate Aktualisierungsstrategie beendet der Controller nach dem Aktualisieren einer DaemonSet Vorlage alte DaemonSet Pods und erstellt automatisch und kontrolliert neue DaemonSet Pods. Während des gesamten Aktualisierungsvorgangs DaemonSet wird auf jedem Knoten höchstens ein Pod von ausgeführt. Sie können einen schrittweisen Rollout durchführen, indem Sie maxUnavailable die Werte maxSurge auf 1, 0 und 60 minReadySeconds einstellen. Wenn Sie keine Aktualisierungsstrategie angeben, erstellt Kubernetes standardmäßig eine RollingUpdate mit maxUnavailable den Werten 1, maxSurge 0 und minReadySeconds 0.

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

A RollingUpdate gewährleistet die schrittweise Einführung neuer DaemonSet Pods, sofern der DaemonSet bereits erstellt wurde und die erwartete Anzahl von Ready Pods auf allen Knoten aufweist. Unter bestimmten Bedingungen, die nicht durch Strategien abgedeckt sind, kann es zu Problemen mit donnernden Herden RollingUpdate kommen.

Vermeiden Sie donnernde Herden auf der Erde DaemonSet

Standardmäßig erstellt der Daemonset-Controller im unabhängig von der RollingUpdate Konfiguration Pods für alle passenden Knoten gleichzeitig, wenn kube-controller-manager Sie einen neuen erstellen. DaemonSet Um einen schrittweisen Rollout von Pods zu erzwingen, nachdem Sie einen erstellt haben DaemonSet, können Sie entweder ein oder verwenden. NodeSelector NodeAffinity Dadurch wird ein Knoten erstellt DaemonSet , der Null entspricht. Anschließend können Sie die Knoten schrittweise aktualisieren, sodass sie für die Ausführung eines Pods mit einer kontrollierten Geschwindigkeit DaemonSet in Frage kommen. Sie können diesem Ansatz folgen:

  • Fügen Sie allen Knoten für ein Label hinzurun-daemonset=false.

kubectl label nodes --all run-daemonset=false
  • Erstellen Sie Ihre DaemonSet mit einer NodeAffinity Einstellung, die zu jedem Knoten ohne run-daemonset=false Label passt. Dies führt zunächst dazu, DaemonSet dass Sie keine entsprechenden Pods haben.

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: run-daemonset
          operator: NotIn
          values:
          - "false"
  • Entfernen Sie das run-daemonset=false Etikett mit einer kontrollierten Geschwindigkeit von Ihren Knoten. Sie können dieses Bash-Skript als Beispiel verwenden:

#!/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
  • Optional können Sie die NodeAffinity Einstellung aus Ihrem DaemonSet Objekt entfernen. Beachten Sie, dass dadurch auch ein Pod ausgelöst RollingUpdate und nach und nach alle vorhandenen DaemonSet Pods ersetzt werden, da sich die DaemonSet Vorlage geändert hat.

Vermeiden Sie donnernde Herden bei Node-Scale-Outs

Ähnlich wie bei der DaemonSet Erstellung kann die schnelle Erstellung neuer Knoten dazu führen, dass eine große Anzahl von DaemonSet Pods gleichzeitig gestartet wird. Sie sollten neue Knoten mit einer kontrollierten Geschwindigkeit erstellen, damit der Controller DaemonSet Pods mit derselben Geschwindigkeit erstellt. Falls dies nicht möglich ist, können Sie dafür sorgen, dass die neuen Knoten zunächst nicht für die vorhandenen Knoten in Frage kommen, DaemonSet indem Sie NodeAffinity Als Nächstes können Sie den neuen Knoten schrittweise ein Label hinzufügen, sodass der Daemonset-Controller Pods mit einer kontrollierten Geschwindigkeit erzeugt. Sie können diesem Ansatz folgen:

  • Fügen Sie allen vorhandenen Knoten ein Label hinzu für run-daemonset=true

kubectl label nodes --all run-daemonset=true
  • Aktualisieren Sie Ihre DaemonSet mit einer NodeAffinity Einstellung, die jedem Knoten mit einer run-daemonset=true Bezeichnung entspricht. Beachten Sie, dass dadurch auch ein Pod ausgelöst RollingUpdate und nach und nach alle vorhandenen DaemonSet Pods ersetzt werden, da sich die DaemonSet Vorlage geändert hat. Sie sollten warten, RollingUpdate bis der Vorgang abgeschlossen ist, bevor Sie mit dem nächsten Schritt fortfahren.

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: run-daemonset
          operator: In
          values:
          - "true"
  • Erstellen Sie neue Knoten in Ihrem Cluster. Beachten Sie, dass diese Knoten nicht mit der run-daemonset=true Bezeichnung versehen DaemonSet werden, sodass sie nicht mit diesen Knoten übereinstimmen.

  • Fügen Sie das run-daemonset=true Label Ihren neuen Knoten (die derzeit nicht über das run-daemonset Label verfügen) in kontrollierter Geschwindigkeit hinzu. Sie können dieses Bash-Skript als Beispiel verwenden:

#!/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
  • Entfernen Sie optional die NodeAffinity Einstellung von Ihrem DaemonSet Objekt und entfernen Sie die run-daemonset Bezeichnung von allen Knoten.

Vermeiden Sie donnernde Herden bei Updates DaemonSet

Eine RollingUpdate Richtlinie berücksichtigt nur die maxUnavailable Einstellung für DaemonSet Pods, bei denen dies der Fall ist. Ready Wenn a nur NotReady Pods oder einen großen Prozentsatz von NotReady Pods DaemonSet hat und Sie seine Vorlage aktualisieren, erstellt der Daemonset-Controller gleichzeitig neue Pods für alle Pods. NotReady Dies kann bei einer großen Anzahl von NotReady Pods zu Problemen mit donnernden Herden führen, z. B. wenn die Pods ständig abstürzen oder keine Bilder abrufen können.

Um einen schrittweisen Rollout von Pods zu erzwingen, wenn Sie einen aktualisieren DaemonSet und es NotReady Pods gibt, können Sie die Aktualisierungsstrategie auf der DaemonSet Seite Von bis vorübergehend ändern. RollingUpdate OnDelete Wenn Sie eine DaemonSet Vorlage aktualisiert haben, erstellt der Controller neue Pods, nachdem Sie die alten manuell gelöscht haben, sodass Sie den Rollout neuer Pods steuern können. OnDelete Sie können diesem Ansatz folgen:

  • Überprüfen Sie, ob Sie irgendwelche NotReady Pods in Ihrem haben DaemonSet.

  • Falls nein, können Sie die DaemonSet Vorlage problemlos aktualisieren und die RollingUpdate Strategie gewährleistet eine schrittweise Einführung.

  • Falls ja, sollten Sie zuerst Ihre aktualisieren, DaemonSet um die OnDelete Strategie verwenden zu können.

updateStrategy:
  type: OnDelete
  • Aktualisieren Sie als Nächstes Ihre DaemonSet Vorlage mit den erforderlichen Änderungen.

  • Nach diesem Update können Sie die alten DaemonSet Pods löschen, indem Sie Anfragen zum Löschen von Pods mit kontrollierter Geschwindigkeit stellen. Sie können dieses Bash-Skript als Beispiel verwenden, bei dem der DaemonSet Name im Kube-System-Namespace fluentd-elasticsearch lautet:

#!/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
  • Schließlich können Sie Ihren Rücken zur früheren Strategie aktualisieren. DaemonSet RollingUpdate