9 Punkte von outsideris 2022-08-21 | 2 Kommentare | Auf WhatsApp teilen

Crossplane von Upbound stellt in Kubernetes eine Cloud-Control-Plane bereit. Deshalb gibt es Hunderte von CRDs (Custom Resource Definitions), um Cloud-Ressourcen wie AWS, Azure und GCP abzubilden. In Crossplane werden sie MRs (Managed Resources) genannt.

Selbst fortgeschrittene Kubernetes-Nutzer betrieben bislang in der Regel nur einige Dutzend CRs, aber bei Crossplane müssen Hunderte von MRs verwendet werden. Deshalb begann man, die Grenzen zu untersuchen, wie viele CRDs Kubernetes verarbeiten kann.

Das lässt sich in clientseitige und serverseitige Probleme unterteilen.

Clientseitige Probleme

  • Auf Client-Seite ist der Discovery-Prozess das Problem.
  • Clients wie kubectl führen Discovery aus, um herauszufinden, welche APIs der Server bereitstellt. Dazu müssen sie alle API-Endpunkte einmal durchlaufen.
  • CRs werden als API-Endpunkte bereitgestellt.
  • Um ein MR wie https://example.org/apis/rds.aws.upbound.io/v1/instances/cool-db abzufragen, muss man die unterstützten API-Gruppen unter https://example.org/apis/ finden, die unterstützten Versionen unter https://example.org/apis/rds.aws.upbound.io ermitteln und unter https://example.org/apis/rds.aws.upbound.io/v1 die unterstützten CRs finden.
  • Die Crossplane-MRs für die Cloud-Provider AWS, Azure und GCP umfassen etwa 2.000 Ressourcen und sind auf 300 API-Gruppen und -Versionen verteilt.
  • Der Client sendet für Discovery also 300 HTTP-Anfragen.
  • In heutigen Netzwerken ist das kein großes Problem, aber die entdeckten Probleme betrafen Rate Limiting und Caching.

Clientseitiges Rate Limiting

  • Es gibt ein Rate Limit von durchschnittlich 5 Anfragen pro Sekunde (mit Bursts bis 100), und alle 10 Minuten wird der Discovery-Cache invalidiert.
  • Das lässt sich beheben, indem man das Rate Limit anhebt; es liegt weiterhin bei 5 Anfragen pro Sekunde, kann nun aber bis auf 300 erhöht werden.
  • In kubectl v1.22 wurde ein Issue eröffnet, um dieses Limit anzuheben, und auch der Discovery-Cache wurde von 10 Minuten auf 5 Stunden angepasst, sodass in Kubernetes v1.25 das erhöhte Client-Limit genutzt werden kann.

Client-Cache

  • Selbst bei deaktiviertem Rate Limiting dauerte das Abfragen von 300 API-Gruppen fast 20 Sekunden.
  • Zunächst wirkte es wie ein Netzwerkproblem, doch bei der Untersuchung stellte sich heraus, dass die Ursache im Zugriff auf Cache-Dateien lag.
  • Das wurde in Kubernetes 1.25 behoben, wodurch es auf macOS 25-mal schneller und auf Linux 2-mal schneller wurde.

Künftige Client-Verbesserungen

  • Rate Limiting auf Client-Seite ist zwar sinnvoll, schützt den Server in Wahrheit aber nicht wirklich.
  • API Priority and Fairness (AP&F), eingeführt in Kubernetes 1.20, bietet serverseitig Queues und Traffic-Shaping, um den API-Server zu schützen.
  • Ein einzelner aggregierter HTTP-Endpunkt für Discovery wurde im KEP genehmigt und soll in 1.26 als Alpha unterstützt werden.

Serverseitige Probleme

Berechnung des OpenAPI-Schemas

  • Nach dem Registrieren von Hunderten von CRDs wurde beobachtet, dass API-Anfragen über fast eine Stunde hinweg sehr langsam waren.
  • Durch Profiling wurde festgestellt, dass die Ursache in der Logik zur Berechnung des OpenAPI-v2-Schemas lag.
  • Wenn ein CRD hinzugefügt oder aktualisiert wird, baut der OpenAPI-Controller die Swagger-Spezifikation des CR, führt sie mit den Swagger-Spezifikationen aller CRs zu einer großen Spezifikation zusammen und serialisiert diese dann als JSON, das unter /openapi/v2 bereitgestellt wird.
  • Dies wurde so geändert, dass /openapi/v2 verzögert berechnet wird und erst dann, wenn tatsächlich ein Endpunkt für einen CR angefragt wird.
  • Diese Änderung wurde in v1.24.0 aufgenommen und auf 1.20.13, 1.21.7 und 1.22.4 zurückportiert.

etcd-Client

  • Das ist ein neuer Engpass, der nach der Behebung des OpenAPI-Problems entdeckt wurde.
  • Es stellte sich heraus, dass der API-Server pro CRD 4 MiB Speicher verwendet.
  • Das ist insbesondere bei Managed Kubernetes wie GKE und EKS problematisch, weil dort CPU und Speicher des API-Servers begrenzt sind. Wenn mehr Ressourcen benötigt werden, wird der API-Server zwar automatisch skaliert, aber leider ist das Hinzufügen von CRDs kein Faktor bei der Entscheidung zur Skalierung. Deshalb wird nicht skaliert, solange der API-Server nicht wiederholt per OOM-Kill beendet wird.
  • Bei Tests auf GKE, AKE und EKS funktionierte Self-Healing zwar, aber der API-Server war zwischen 5 Sekunden und 1 Stunde lang nicht verfügbar. Der Cluster stand nicht vollständig still, aber alle Reconciliations stoppten.
  • Durch Profiling wurde festgestellt, dass die Logging-Bibliothek Zap 20 % des Speichers belegte.
  • Der API-Server erstellt für jede Version eines CR einen etcd-Client, und jeder etcd-Client erzeugt einen Zap-Logger.
  • Dadurch stieg nicht nur der Speicherverbrauch durch doppelte Logger, sondern es entstanden auch unnötige TCP-Verbindungen zwischen API-Server und etcd.
  • Die Maintainer stimmten zu, dass ein einzelner etcd-Client für alle CR-Endpunkte richtig wäre. Da der Release von Kubernetes 1.25 jedoch unmittelbar bevorstand, war es schwierig, alles vollständig zu beheben. Deshalb wurde zunächst eine kleinere Änderung umgesetzt, bei der alle etcd-Clients einen gemeinsamen Logger nutzen.
    -Dies soll in 1.25 enthalten sein und auf 1.22, 1.23 und 1.24 zurückportiert werden. Der Speicherverbrauch soll dadurch um 20 % sinken.

Künftige serverseitige Verbesserungen

  • Die bisher pro CR-Version erstellten etcd-Clients sollen künftig pro Transport einer erstellt werden, also einer pro etcd-Cluster.
  • Gemeinsam mit den Engineering-Teams von GKE, EKS und AKE wird zudem daran gearbeitet, die Installation einer großen Zahl von Crossplane-CRDs besser zu unterstützen.

2 Kommentare

 
roxie 2022-08-21

Freigabe kostenlos -> Ungültigmachung

 
roxie 2022-08-21

Klaiyent -> Client