View a markdown version of this page

Kubernetes Control Plane - 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 Control Plane

Tipp

Lernen Sie in Amazon EKS-Workshops bewährte Methoden kennen.

Die Kubernetes-Steuerungsebene 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 unterschiedlich, je nachdem, was Sie im Cluster ausführen. Zu den Bereichen mit den größten Auswirkungen auf die Skalierung gehören jedoch die Kubernetes-Version, die Auslastung und die Skalierung einzelner Knoten.

Sie können die Steuerungsebene Ihres Clusters in einem von zwei Modi ausführen, um unterschiedlichen Workload-Anforderungen gerecht zu werden:

  • Standardmodus — Standardmäßig verwenden alle EKS-Cluster den Standardmodus. Die Steuerungsebene skaliert je nach Ihren Workload-Anforderungen automatisch nach oben und unten. Der Standardmodus weist dynamisch ausreichend Kapazität auf der Steuerungsebene zu und ist für die meisten Anwendungsfälle die empfohlene Option.

  • Bereitgestellter Modus — Wenn Ihre Workloads Leistungsschwankungen aufgrund der Skalierung der Steuerungsebene nicht tolerieren können oder wenn sie eine sehr hohe Kapazität der Steuerungsebene benötigen, können Sie den Bereitstellungsmodus verwenden. Im Bereitstellungsmodus weisen Sie vorab Kapazitäten auf der Steuerungsebene zu, die stets bereit ist, anspruchsvolle Anforderungen zu erfüllen. Sie erhalten eine konsistente und vorhersehbare Leistung.

Im EKS-Bereitstellungsmodus wählen Sie aus einer Reihe von Skalierungsstufen (XL, 2XL, 4XL und 8XL). Mit jeder Stufe erhalten Sie eine hohe, vorhersehbare Leistung von der Steuerungsebene des Clusters aus. Der bereitgestellte Modus ist besonders für die folgenden Anwendungsfälle nützlich:

  • Performance-critical Arbeitslasten

  • Large-scale Operationen in den Bereichen KI und maschinelles Lernen

  • Erwartete Veranstaltungen mit hoher Nachfrage

  • Umgebungen, in denen Konsistenz zwischen Staging und Produktion erforderlich ist

Im Bereitstellungsmodus können Sie die Kapazität der Steuerungsebene im Voraus zuweisen und profitieren von einem verbesserten Service Level Agreement (SLA) von 99,99%, das in Intervallen von 1 Minute gemessen wird. Weitere Informationen zum Modus „EKS Provisioned“, einschließlich der Tierspezifikationen und Preise, finden Sie im EKS-Benutzerhandbuch unter EKS Provisioned Control Plane.

Beschränken Sie die Arbeitslast und das Node-Bursting

Wichtig

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

Die EKS-Steuerungsebene skaliert automatisch, wenn Ihr Cluster wächst, aber es gibt Grenzen, wie schnell sie skaliert wird. Wenn Sie zum ersten Mal einen EKS-Cluster erstellen, kann die Control Plane 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.

Die Skalierung großer Anwendungen erfordert, dass sich die Infrastruktur anpassen muss, um vollständig einsatzbereit zu sein (z. B. das Aufwärmen von Load Balancern). 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 sagt Ihre Anwendungseinschränkungen möglicherweise nicht genau voraus. 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. https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics Wenn Sie anspruchsvollere Skalierungsanforderungen haben oder auf der Grundlage externer Quellen (z. B. der AWS SQS-Warteschlange) skalieren müssen, verwenden Sie KEDA für die ereignisbasierte Workload-Skalierung.

Skalieren Sie Knoten und Pods sicher nach unten

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 Betriebszeit auftreten (z. B. langsame Speicherlecks). Der automatische Austausch bietet Ihnen gute Prozesse und Methoden für Node-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 diese 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. Verwenden Sie dazu den Schwellenwert für das Herunterskalieren im Kubernetes Cluster Autoscaler mit der Einstellung --scale-down-utilization-threshold oder in Karpenter können Sie die Provisioner-Einstellung verwenden. ttlSecondsAfterEmpty

Verwenden Sie Budgets für den Ausfall von Pods und das sichere Herunterfahren von Knoten

Das Entfernen von Pods und Nodes aus einem Kubernetes-Cluster erfordert, dass 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, da Änderungen auf die Controller übertragen werden. Budgets für Pod-Unterbrechungen sind eine bewährte Methode, um die Abwanderung zu verlangsamen und so die Verfügbarkeit der Arbeitslast zu gewährleisten, wenn Knoten in einem Cluster entfernt oder neu geplant werden.

Verwenden Sie Client-Side Cache, wenn Sie Kubectl ausführen

Die ineffiziente Verwendung des Befehls kubectl kann den Kubernetes API-Server zusätzlich belasten. 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 Discovery-Informationen 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 von einem Container oder ohne clientseitigen Cache ausführen, können Probleme mit der API-Drosselung auftreten. Es wird empfohlen, Ihren 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 wird die CPU-Last auf dem Client und dem Server bei jeder Anfrage erhöht. Durch das Deaktivieren der Komprimierung können der Overhead und die Latenz reduziert werden, sofern Sie über eine ausreichende Bandbreite verfügen. Um die Komprimierung zu deaktivieren, können Sie das --disable-compression=true Flag oder das Set disable-compression: true in Ihrer kubeconfig-Datei verwenden.

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

Cluster Autoscaler teilen

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 Autoscalers im Shard-Modus auszuführen. Jede Cluster Autoscaler-Instanz ist so konfiguriert, dass sie eine Reihe von Knotengruppen skaliert. Das folgende Beispiel zeigt 2 Cluster-Autoscaling-Konfigurationen, die so konfiguriert sind, dass jeweils 4 Knotengruppen skaliert werden.

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 in Zeiten erhöhter Anfragen vor Überlastung zu schützen, begrenzt der API-Server die Anzahl der Anfragen während des Fluges, die zu einem bestimmten Zeitpunkt noch ausstehen können. Sobald dieses Limit überschritten wird, beginnt der API-Server mit der Ablehnung von Anfragen und sendet einen 429-HTTP-Antwortcode für „Zu viele Anfragen“ an die Kunden 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 zu haben 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, heißt API-Priorität und Fairness. https://kubernetes.io/docs/concepts/cluster-administration/flow-control/ Der API-Server konfiguriert die Gesamtzahl der Inflight-Anfragen, 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 400 und 200 Anfragen für diese Flags, sodass insgesamt 600 Anfragen gleichzeitig versendet werden können. Da es jedoch die Steuerungsebene als Reaktion auf die zunehmende Auslastung und Arbeitslastabwanderung auf größere Größen skaliert, erhöht es entsprechend das Kontingent für Anfragen während des Fluges bis zum Jahr 2000 (Änderungen vorbehalten). APF legt fest, wie diese Kontingente für Anfragen während des Fluges weiter auf die verschiedenen Anfragetypen aufgeteilt werden. Beachten Sie, dass EKS-Steuerebenen hochverfügbar sind und mindestens 2 API-Server in jedem Cluster registriert sind. Das bedeutet, dass die Gesamtzahl der Inflight-Anfragen, die Ihr Cluster verarbeiten kann, doppelt so hoch ist (oder höher, wenn sie horizontal weiter skaliert wird) als die pro Kube-Apiserver festgelegte Inflight-Quote. Dies entspricht in den größten EKS-Clustern mehreren Tausend. 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 automatisch vom API-Server 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 Anfragen, die allen zugewiesen PriorityLevelConfigurations werden, ergibt 600 (oder etwas mehr, da der API-Server aufrundet, wenn einem bestimmten Level nur ein Bruchteil einer Anfrage gewährt wird). Um das PriorityLevelConfigurations in Ihrem Cluster und die Anzahl der jeweils zugewiesenen Anfragen zu überprüfen, können Sie den folgenden Befehl ausführen. Dies sind die Standardwerte 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-Serveranforderungen mit einem bestimmten Satz von Eigenschaften werden derselben Kategorie zugeordnet FlowSchema. Zu diesen Eigenschaften gehören entweder der authentifizierte Benutzer oder Attribute der Anfrage, wie z. B. die API-Gruppe, der Namespace oder die Ressource. A gibt FlowSchema auch an, welcher Art von Anfrage PriorityLevelConfiguration diese Art von Anfrage 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 er jede Anfrage, FlowSchemas bis eine gefunden wird, die allen erforderlichen Eigenschaften entspricht. Wenn mehrere Anfragen einer Anfrage FlowSchemas entsprechen, 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 zu PriorityLevelConfigurations kann mit diesem Befehl angezeigt 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 wird ein Limit für die maximale Anzahl von Anfragen während des Fluges für diese Prioritätsstufe durchgesetzt. Das Verhalten ändert sich jedoch, wenn dieses Limit erreicht wird. Beispielsweise PriorityLevelConfiguration verwendet der Workload-High 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 in der Warteschlange ein Timeout auftritt oder wenn sich bereits zu viele Anfragen in der Warteschlange befinden, hat der API-Server keine andere Wahl, als die Anfrage zu löschen und dem Client eine 429 zurückzugeben. Beachten Sie, dass Warteschlangen zwar verhindern können, dass eine Anfrage eine 429-Nummer erhält, dies jedoch mit dem Nachteil einer erhöhten Ende-zu-Ende-Latenz bei der Anfrage einhergeht.

Betrachten Sie nun das Catch-All FlowSchema , das dem Catch-All mit dem Typ Reject zugeordnet ist. PriorityLevelConfiguration Wenn Kunden das Limit von 13 Anfragen während des Fluges erreichen, verzichtet der API-Server auf die Warteschleife und verwirft die Anfragen sofort mit einem 429-Antwortcode. Und schließlich erhalten Anfragen, die einer PriorityLevelConfiguration Anfrage vom Typ Exempt zugeordnet sind, niemals eine 429-Nummer und werden immer sofort versendet. Dies wird für Anfragen mit hoher Priorität verwendet, wie z. B. Healthz-Anfragen oder Anfragen, die von der system:masters-Gruppe stammen.

Überwachung von APF und verworfenen Anfragen

Um zu bestätigen, ob Anfragen aufgrund von APF verworfen werden, apiserver_flowcontrol_rejected_requests_total können die API-Server-Metriken überwacht werden, um zu überprüfen, welche Anfragen betroffen FlowSchemas sind. PriorityLevelConfigurations Diese Metrik zeigt beispielsweise, dass 100 Anfragen von den Dienstkonten aufgrund eines Timeouts bei Anfragen in Warteschlangen mit geringer Arbeitslast 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 nahe eine bestimmte Person daran PriorityLevelConfiguration ist, 429 Sekunden zu empfangen oder aufgrund von Warteschlangen eine erhöhte Latenz zu verzeichnen, 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 Anforderung zwar Warteschlangen, aber nicht unbedingt gelöschte 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

Zu den weiteren nützlichen Prometheus-Metriken gehören:

  • 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. https://kubernetes.io/docs/concepts/cluster-administration/flow-control/#observability

Verhinderung verworfener Anfragen

Vermeiden Sie 429s, indem Sie Ihre Arbeitslast ändern

Wenn APF Anfragen aufgrund einer bestimmten PriorityLevelConfiguration Überschreitung der maximal zulässigen Anzahl an Anfragen während des Fluges verwirft, FlowSchemas können die betroffenen Clients die Anzahl der Anfragen verringern, die zu einem bestimmten Zeitpunkt ausgeführt werden. Dies kann erreicht werden, indem die Gesamtzahl der Anfragen reduziert wird, die in dem Zeitraum von 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 Anfragen während des Fluges 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), kann dies dazu beitragen, die für die jeweilige Arbeitslast erforderliche Gesamtgleichzeitigkeit zu reduzieren.

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 dazu führen, dass API-Serveranfragen verloren gehen und die Arbeitslast erheblich beeinträchtigt wird.

Ein anderer Ansatz, um verworfene Anfragen zu verhindern, 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 auf ihre Standardwerte zurück, wenn sie geändert werden, es sei denn, die folgende Anmerkung zu den Objekten ist auf false gesetzt:

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

Auf einer höheren 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, bei denen die Kapazität für andere Anfragetypen verloren gehen kann.

Dies kann erreicht werden, indem entweder die Standardeinstellung FlowSchemas geändert PriorityLevelConfigurations und/oder neue Objekte dieser Typen erstellt werden. Operatoren können die Werte für „Zugesichert“ ConcurrencyShares für die entsprechenden PriorityLevelConfigurations Objekte erhöhen, um den Anteil der Anfragen, die ihnen während des Fluges zugewiesen werden, 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 in die Warteschlange gestellt werden, bevor sie versendet werden.

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

Wenn Sie Änderungen an den APF-Standardeinstellungen vornehmen, sollten diese Metriken auf einem Cluster überwacht werden, der nicht zur Produktion gehört, um sicherzustellen, dass Änderungen der Einstellungen nicht zu ungewollten 429s führen:

  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 löschen.

  2. Die Werte für apiserver_flowcontrol_nominal_limit_seats und apiserver_flowcontrol_current_executing_seats sollten verglichen werden, um sicherzustellen, dass bei gleichzeitiger Verwendung kein Risiko besteht, dass der Grenzwert für diese Prioritätsstufe überschritten wird.

Ein häufiger Anwendungsfall für die Definition einer neuen FlowSchema Anzeige ist die Isolierung. PriorityLevelConfiguration Nehmen wir an, wir wollen lang andauernde Listenereignisaufrufen von Pods auf ihren eigenen Anteil an Anfragen isolieren. Dadurch wird verhindert, dass wichtige Anfragen von Pods, die die vorhandenen Dienstkonten FlowSchema verwenden, 429 Anfragen erhalten und ihnen die Anforderungskapazität ausgeht. Denken Sie daran, dass die Gesamtzahl der Anfragen während des Fluges begrenzt ist. Dieses Beispiel zeigt jedoch, dass die APF-Einstellungen geändert werden können, um die Anforderungskapazität für die jeweilige Arbeitslast besser 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
  • Dadurch werden alle Listenereignisaufrufe FlowSchema erfasst, die von Dienstkonten im Standard-Namespace getätigt wurden.

  • Die übereinstimmende Rangfolge 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 PriorityLevelConfiguration den Catch-All, um diese Anfragen zu isolieren. In diesem Bucket können nur 13 Anfragen während des Fluges von diesen lang andauernden Listenereignisaufrufen verwendet werden. Sobald die Pods versuchen, mehr als 13 dieser Anfragen gleichzeitig zu stellen, erhalten sie ab sofort 429 Anfragen.

Ressourcen werden auf dem API-Server abgerufen

Das Abrufen von Informationen vom API-Server ist ein erwartetes Verhalten für Cluster beliebiger 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 Steuerungsebene werden und zu API-Latenz und Langsamkeit führen. Je nach Schweregrad der Latenz kommt es zu unerwarteten Ausfallzeiten, wenn Sie nicht vorsichtig sind.

Die ersten Schritte zur Vermeidung dieser Art von Problemen sind die ersten Schritte, sich darüber im Klaren zu sein, was Sie anfordern und wie oft. Im Folgenden finden Sie eine Anleitung zur Begrenzung des Abfragevolumens auf der Grundlage der Best Practices für die Skalierung. Die Vorschläge in diesem Abschnitt sind der Reihe nach aufgeführt, wobei mit den Optionen begonnen wird, von denen bekannt ist, dass sie am besten skalieren.

Verwenden Sie Shared Informers

Wenn Sie Steuerungen und Automatisierungen erstellen, die in die Kubernetes-API integriert sind, müssen Sie häufig Informationen aus Kubernetes-Ressourcen abrufen. Wenn Sie diese Ressourcen regelmäßig abfragen, 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, anstatt nach Änderungen abzufragen. Informers reduzieren die Last weiter, indem sie einen gemeinsamen Cache für die Ereignisse und Änderungen verwenden, sodass mehrere Controller, die dieselben Ressourcen beobachten, keine zusätzliche Last verursachen.

Controller sollten es vermeiden, clusterweite Ressourcen ohne Bezeichnungen und Feldselektoren abzufragen, insbesondere in großen Clustern. Für jede ungefilterte Umfrage 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 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 besteht das Standardverhalten darin, Objekte aufzulisten. Um watch statt list zu verwenden, können Sie das ?watch=true an das Ende Ihrer API-Anfrage anhängen. Um beispielsweise alle Pods im Standard-Namespace mit einer Uhr abzurufen, verwende:

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

Wenn Sie Objekte auflisten, sollten Sie den Umfang der aufgelisteten Objekte und die Menge der zurückgegebenen Daten einschränken. Sie können die zurückgegebenen Daten einschränken, indem Sie den Anforderungen 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 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-Calls oder aufgelistete Objekte einzuschränken, ist resourceVersions die Verwendung, über die Sie in der Kubernetes-Dokumentation nachlesen können. Ohne Angabe eines resourceVersion Arguments 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 abzufragen versuchen, und kann im Feld gefunden werden. metadata.resourseVersion Dies wird auch empfohlen, wenn Sie Watch-Calls verwenden und nicht nur Listenanrufe

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

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

Es wird empfohlen, watch zu verwenden, wobei ResourceVersion auf den letzten bekannten Wert gesetzt ist, der aus der vorherigen Liste oder Überwachung empfangen wurde. Dies wird in client-go automatisch gehandhabt. 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 dies für den API-Server usw. am ressourcenintensivsten. Dieser Aufruf ruft alle Pods in allen Namespaces ab, ohne Paginierung oder Einschränkung des Gültigkeitsbereichs, und erfordert ein Quorum, das von etcd gelesen wird.

/api/v1/pods

DaemonSet Beugt 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. Wenn Sie einen löschen DaemonSet , werden die von ihm erstellten Pods bereinigt.

Einige typische Anwendungen von a DaemonSet sind:

  • Ausführen eines Cluster-Speicher-Daemons auf jedem Knoten

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

  • Auf jedem Knoten wird ein Node-Monitoring-Daemon ausgeführt

Bei Clustern mit Tausenden von Knoten kann das Erstellen eines neuen DaemonSet, das Aktualisieren oder das Erhöhen der Anzahl von Knoten zu einer hohen Belastung der Steuerungsebene führen. DaemonSet Wenn DaemonSet Pods beim Pod-Start teure API-Serveranfragen stellen, 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 eine 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 auf kontrollierte Weise neue DaemonSet Pods. Während des gesamten Aktualisierungsvorgangs DaemonSet wird auf jedem Knoten höchstens ein Pod des Pods ausgeführt. Sie können einen schrittweisen Rollout durchführen, indem Sie maxUnavailable die Werte maxSurge auf 1, 0 und 60 minReadySeconds setzen. Wenn Sie keine Aktualisierungsstrategie angeben, erstellt Kubernetes standardmäßig eine Datei RollingUpdate mit maxUnavailable den Werten 1, maxSurge 0 und minReadySeconds 0.

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

A RollingUpdate stellt die schrittweise Einführung neuer DaemonSet Pods sicher, 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 werden, kann es zu heftigen Herdenproblemen kommen. RollingUpdate

Beugt donnernden Herden bei der Schöpfung vor DaemonSet

Standardmäßig erstellt der Daemonset-Controller im Kube-Controller-Manager unabhängig von der RollingUpdate Konfiguration Pods für alle passenden Knoten gleichzeitig, wenn Sie einen neuen erstellen. DaemonSet Um eine schrittweise Einführung von Pods zu erzwingen, nachdem Sie einen erstellt haben, können Sie entweder a oder verwenden. DaemonSet NodeSelector NodeAffinity Dadurch wird ein Knoten erstellt DaemonSet , der null Knoten entspricht. Anschließend können Sie die Knoten schrittweise aktualisieren, sodass sie für den Betrieb eines Pods mit kontrollierter Geschwindigkeit DaemonSet in Frage kommen. Du kannst diesem Ansatz folgen:

  • Fügen Sie allen Knoten für eine Bezeichnung hinzurun-daemonset=false.

kubectl label nodes --all run-daemonset=false
  • Erstellen Sie Ihre DaemonSet mit einer NodeAffinity Einstellung, die jedem Knoten ohne run-daemonset=false Label entspricht. 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"
  • Entferne das run-daemonset=false Label mit kontrollierter Geschwindigkeit von deinen Nodes. 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
  • Entfernen Sie optional die NodeAffinity Einstellung aus Ihrem DaemonSet Objekt. Beachten Sie, dass dadurch auch ein Pod ausgelöst RollingUpdate und nach und nach alle vorhandenen DaemonSet Pods ersetzt werden, da sich das DaemonSet Template geändert hat.

Vermeiden Sie donnernde Herden bei Node-Scale-Outs

Ähnlich wie bei der DaemonSet Erstellung kann das schnelle Erstellen 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, sodass der Controller DaemonSet Pods mit derselben Geschwindigkeit erstellt. Wenn dies nicht möglich ist, können Sie die neuen Knoten zunächst für die vorhandenen Knoten ungeeignet machen, DaemonSet indem Sie Folgendes verwendenNodeAffinity. Als Nächstes können Sie den neuen Knoten schrittweise ein Label hinzufügen, sodass der Daemonset-Controller Pods mit einer kontrollierten Geschwindigkeit erstellt. 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, um jedem Knoten ein run-daemonset=true Label zuzuordnen. Beachten Sie, dass dadurch auch ein Pod ausgelöst RollingUpdate und nach und nach alle vorhandenen DaemonSet Pods ersetzt werden, da sich das DaemonSet Template 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 über das run-daemonset=true Label verfügen, sodass DaemonSet sie nicht mit diesen Knoten übereinstimmen.

  • Fügen Sie das run-daemonset=true Label Ihren neuen Knoten (die das run-daemonset Label derzeit nicht haben) 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 das run-daemonset Label von allen Knoten.

Verhindern 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 ein Gerät nur NotReady Pods oder einen großen Prozentsatz an 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 Pods ständig in einer Schleife abstürzen oder keine Images abrufen können.

Um eine schrittweise Einführung von Pods zu erzwingen, wenn Sie einen aktualisieren DaemonSet und es NotReady Pods gibt, können Sie die Aktualisierungsstrategie vorübergehend von zu ändern. DaemonSet RollingUpdate OnDelete MitOnDelete: Nachdem Sie eine DaemonSet Vorlage aktualisiert haben, erstellt der Controller neue Pods, nachdem Sie die alten manuell gelöscht haben, sodass Sie die Einführung neuer Pods steuern können. Sie können diesem Ansatz folgen:

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

  • Falls nein, kannst du die DaemonSet Vorlage problemlos aktualisieren und die RollingUpdate Strategie sorgt für einen schrittweisen Rollout.

  • 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 einer kontrollierten Geschwindigkeit stellen. Sie können dieses Bash-Skript als Beispiel verwenden, bei dem der DaemonSet Name fluentd-elasticsearch im Kube-System-Namespace 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 Ihre Rückkehr zur früheren Strategie aktualisieren. DaemonSet RollingUpdate