Der Leitfaden des Data Engineers zur Optimierung von Kubernetes

18.04.2025

Niels Claeys

Bei Conveyor haben wir über fünf Jahre daran gearbeitet, eine Batch-Datenplattform auf Basis von Kubernetes aufzubauen und zu betreiben.

Während Kubernetes eine solide Grundlage bietet, ist die Standardkonfiguration nicht gut geeignet, um Batch-Workloads zu handhaben.

In diesem Blogbeitrag werde ich verschiedene Anpassungen erläutern, die wir vorgenommen haben, um die Leistung zu verbessern und die Kosten unserer Kubernetes-Cluster zu senken. Diese Techniken gelten für jede Skalierung, aber ihre Auswirkungen wachsen mit der Anzahl der Jobs. Zum Kontext unterstützen wir derzeit Kunden, die mehr als 500.000 Kernstunden pro Monat nutzen. Dies entspricht dem Betrieb von mehr als 170 unserer Standardknoten ohne Unterbrechung für einen Monat. Wenn Sie Batch-Workloads in ähnlichem Maßstab betreiben oder planen zu unterstützen, ist dieser Blogbeitrag für Sie.

The Data Engineer’s guide to optimizing Kubernetes

Was macht Batch-Workloads anders?

Batch-Workloads unterscheiden sich grundlegend von langfristig laufenden Jobs, für die Kubernetes historisch entwickelt wurde. Die Hauptmerkmale, die Batch-Workloads auszeichnen, sind:

  • Ein hohes Volumen an kurzlebigen Jobs (z.B. > 50000 Jobs/Tag), die von weniger als einer Minute bis zu mehreren Stunden dauern. Dies führt zu einer Vielzahl von Scheduling-Entscheidungen.

  • Die Ressourcenanforderungen von Batch-Jobs variieren erheblich. Wir beobachten Jobs, die zwischen 200 Mb und 500 Gb RAM bei unseren Kunden laufen.

  • Batch-Jobs werden nach einem Zeitplan ausgelöst und laufen in den meisten Organisationen hauptsächlich nachts (z.B. von 12 bis 7 Uhr), während tagsüber eine geringere Auslastung besteht.

Diese Unterschiede machen es notwendig, die Konfiguration von Kubernetes anzupassen. In den folgenden Abschnitten werde ich drei Bereiche behandeln, in denen eine Feinabstimmung vorteilhaft ist, und erklären, wie wir diese Herausforderungen angegangen sind.

Maximierung der Knoten-Effizienz in Ihrem Cluster

In Kubernetes zahlen Sie für gesamte Knoten und nicht nur für die Workloads, die sie hosten (im Gegensatz zu Diensten wie ECS oder Fargate). Wenn Ihre Plattform wächst und Sie beginnen, die Effizienz des Clusters zu verfolgen, wird schnell deutlich, dass ein großer Teil der verfügbaren Rechenressourcen verschwendet wird. Unsere ersten Ergebnisse zeigten, dass etwa 50 % der Ressourcen nicht genutzt wurden. Bevor wir diese Effizienz verbessern, müssen wir verstehen, wie die Ressourcen eines Knotens in Kubernetes aufgeteilt sind:

  • Knotenüberkopf: Dazu gehören OS-Ressourcen, Kubelet-Ressourcen sowie Evakuierungsgrenzen.

  • Daemonset-Überkopf: die Ressourcen, die für die Kubernetes-Komponenten benötigt werden, die auf jedem Knoten laufen müssen (z.B. CNI, CSI, Überwachungs-Komponenten).

  • Tatsächliche Workload: die Ressourcen, die für die Ausführung Ihres Jobs verfügbar sind.

Maximizing node efficiency in your cluster

Der Kubernetes-Überkopf ist die Summe aus dem Knotenüberkopf und dem Daemonset-Überkopf. Sie sollten diesen Überkopf so weit wie möglich minimieren, damit Ihnen mehr Platz für die Workloads auf Ihrem Knoten zur Verfügung steht. Bei einigen Cloud-Anbietern kann der Knotenüberkopf angepasst werden (z.B. AWS), wenn Sie sich für selbstverwaltete Knoten entscheiden. Stellen Sie sicher, dass Sie alle Konfigurationsänderungen unter hoher Last testen, um sicherzustellen, dass Ihr Cluster weiterhin wie erwartet funktioniert.

Der Daemonset-Überkopf kann minimiert werden, indem die Anzahl der Komponenten, die auf jedem Knoten installiert sind, sowie die Ressourcen, die sie benötigen, eingeschränkt werden. Dies erfordert, dass Ihr Plattform-Team entscheidet, was wichtig ist, und nicht blind jedes Anfrage von den Anwendungsfall-Teams implementiert. Zweitens passen wir die Ressourcen, die für jedes Daemonset erforderlich sind, gründlich an, was dazu führte, dass wir von Fluentd zu Fluentbit für das Logging wechselten und anstelle des (veralteten) OMS-Agenten einen benutzerdefinierten Überwachungsagenten auf Azure betrieben haben.

Die richtige Knotengröße wählen

Bei der Ausführung mehrerer Jobs auf einem Knoten müssen Sie die richtige Größe für Ihre Knoten wählen:

  • Kleine Knoten haben ein höheres Verhältnis von Überkopf zu Workload-Ressourcen, was ihre Effizienz verringert. Daher sind Kubernetes-Knoten mit weniger als 8 Gb RAM und 2 vCPU nicht nützlich.

  • Große Knoten erhöhen die Wahrscheinlichkeit von Ressourcenverschwendung und verringern damit ebenfalls die Effizienz.

Aufgrund beider Probleme gibt es keine perfekte Knotengröße für jede Workload, und Sie müssen herausfinden, was am besten für Ihre Workloads funktioniert. Die meisten Unternehmen enden in einem gemischten Knotenszenario: Sie wählen eine Standardgröße (z.B. 4 vCPU und 16 Gb RAM), die für die meisten Ihrer Workloads geeignet ist, fügen jedoch auch einige Nodepools mit größeren Knoten für die Workloads hinzu, die dies benötigen.

Die verfügbaren Ressourcen effizient nutzen

Der vorherige Abschnitt behandelte die theoretisch optimale Konfiguration für Ihre Knoten. Sobald die geeigneten NodePools eingerichtet sind, besteht der nächste Schritt darin, zu messen, wie effizient sie mit realen Workloads umgehen. In unserem Fall haben wir festgestellt, dass unsere Standard-NodePools nur 40–50 % Effizienz erzielten, was fast die Hälfte der Knotenressourcen verschwendet. Weitere Untersuchungen ergaben, dass der Standard-Kubernetes-Scheduler die Hauptursache war, da er nicht gut für Batch-Verarbeitungs-Workloads geeignet ist.

Standardmäßig bevorzugt der Kubernetes-Scheduler, Pods auf Knoten zu planen, die die meiste Kapazität verfügbar haben und verteilt die Workloads so weit wie möglich über die Knoten. Dies ist problematisch, wenn Sie es mit schwankenden Workloads kombinieren, da es das schnelle Scalieren des Clusters verhindert. Nachdem 50 Knoten für einen großen Job hinzugefügt wurden, wird der Scheduler neue Jobs gleichmäßig auf die 50 Knoten verteilen, wie in der Abbildung unten dargestellt. Dies führt dazu, dass alle Knoten (teilweise) genutzt werden und somit viele Ressourcen verschwendet werden.

Use the available resources efficiently

Für Batch-Workloads ist es optimal, Jobs auf einem Knoten zu planen, bis die Kapazität des Knotens erreicht ist, und dann zum nächsten Knoten zu wechseln. Kubernetes unterstützt dies, indem die Bewertungsstrategie von LeastAllocated auf MostAllocated geändert wird. Leider unterstützen viele Cloud-Anbieter (z.B. AWS, Azure) nicht die Änderung der Bewertungsstrategie. Die Lösung besteht darin, einen zweiten Kubernetes-Scheduler zu betreiben, der neben dem Standard-Scheduler im Datenbereich läuft. Sie können angeben, welchen Scheduler Sie für eine gegebene Workload verwenden möchten, indem Sie dessen Namen in der Scheduler-Eigenschaft Ihres Jobs, Pods oder Deployment-Objekts angeben.

Illustration of MostAllocated strategy in the Kubernetes scheduler. Illustration found here

Nach der Migration zum neuen Scheduler haben wir die Effizienz um 15–20 % erhöht, wie im folgenden Bild angezeigt. Dies entspricht einer Reduzierung der Anzahl der Knoten, die für diese Nodepools laufen, um 15–20 %.

Illustration of the increase in node efficiency after turning on Binpacking.

Darstellung der Steigerung der Knoten-Effizienz nach Aktivierung von Binpacking.


Strategien zum Skalieren von schwankenden Workloads

Batch-Workloads schwanken natürlich im Verlauf des Tages. Bei vielen unserer Kunden beobachten wir, dass Produktions-Workloads hauptsächlich nachts laufen, während die Nutzung tagsüber auf Entwickler beschränkt ist, die ihre Arbeit testen. Dies führt zu einem Unterschied von 10x zwischen der maximalen und minimalen Auslastung des Clusters, was in der Grafik unten visualisiert ist.

Strategies for scaling spiky workloads

Um mit diesen Schwankungen umzugehen, benötigen Sie eine Komponente, die die Knoten Ihres Clusters vergrößert/verkleinert. Die beiden beliebtesten Komponenten dafür sind:

  • Cluster-Autoscaler: der seit Jahren existiert und fast von allen großen Cloud-Anbietern unterstützt wird. Sein Hauptnachteil ist, dass das Hoch- und Herunterskalieren langsam ist.

  • Karpenter: das vor 2 Jahren von AWS veröffentlicht wurde und die Geschwindigkeit des Hoch- und Herunterskalierens drastisch erhöht. Im Moment ist es nur produktionsbereit auf AWS, aber sie machen Fortschritte bei einer Azure-Implementierung.

Wir verwenden den Cluster-Autoscaler auf Azure-Clustern und konfigurieren ihn wie folgt:

  • Wir möchten schnell hoch- und herunterskalieren, weshalb wir die scale-down-unneeded-time auf 5 Minuten senken und die scale-down-delay-after-… auf 0 Minuten setzen.

  • Wir konfigurieren StatusTaints für die von Azure geplanten Ereignisse (z.B. Freeze, Preempt,…), sodass das Hochskalieren eines Nodepools von 0 Knoten funktioniert.

  • Wir verwalten unseren eigenen cluster-autoscaler, da Azure nicht unterstützt, StartUpTaints anzugeben. Wir benötigen sie, da wir Cilium als unser CNI verwenden und Cilium einen Knoten taintet, bis er bereit ist.

Für AWS-Cluster betreiben wir Karpenter und verwenden die folgende Konfiguration:

  • Da wir laufende Workloads nicht unterbrechen möchten, da dies den Job fehlschlägt oder eine kostspielige Neuberechnung erfordert, konsolidieren wir Knoten nur, wenn sie leer sind.

  • Wir setzen die Konsolidierung auf 5 Minuten, um schnell herunterskalieren zu können.

  • Unsere Nodepools verwenden ein benutzerdefiniertes AMI, um das schnelle Hochfahren neuer Knoten zu beschleunigen. Diese AMIs bündeln benutzerdefinierte Komponenten, die erforderlich sind, um unsere Workloads auszuführen.

Während diese Einstellungen in unserer Konfiguration gut funktionieren, ist es wichtig, ihre Auswirkungen auf Ihre Workloads zu messen, bevor Sie sie in der Produktion übernehmen.

Zuverlässige Workloads auf unzuverlässigen Instanzen erstellen

Ein dritter Faktor, den man für Batch-Workloads berücksichtigen sollte, ist die Nutzung von Spot-Instanzen. Diese Instanzen bieten Kosteneinsparungen von bis zu 70 % im Vergleich zu On-Demand-Knoten, können jedoch jederzeit vom Cloud-Anbieter zurückgefordert werden. Angesichts der großen Anzahl an Batch-Jobs und ihrer typischerweise kurzen Laufzeiten stellen Spot-Instanzen eine praktische und kosteneffektive Lösung dar. Der einzige Haken ist, dass Sie in der Lage sein müssen, mit unzuverlässigen Instanzen umzugehen.

Wir beobachten, dass unsere Kunden in über 97 % der Fälle auf Spot-Instanzen angewiesen sind. Ich glaube, dass diese hohe Akzeptanzrate in großem Maße auf die Verbesserungen zurückzuführen ist, die wir im Umgang mit Spot-Unterbrechungen erzielt haben. Dies führt dazu, dass nur 0,01–0,2 % der Jobs von Spot-Kündigungen betroffen sind. Um dies zu erreichen, verfolgen wir einen dreistufigen Ansatz:

Die Wahrscheinlichkeit von Spot-Unterbrechungen verringern

Der erste Schritt besteht darin, die Anzahl der Spot-Unterbrechungen zu minimieren. Aus unserer Erfahrung gibt es drei Merkmale Ihres Jobs/Umgebung, die dies beeinflussen:

  • Die Jobdauer: Kurze Jobs haben eine geringere Wahrscheinlichkeit, unterbrochen zu werden, als lange Jobs. Wenn Ihr Job weniger als eine Stunde dauert, sehen wir eine geringe Wahrscheinlichkeit, dass er unterbrochen wird.

  • Die Ressourcenanforderungen: Je höher die Ressourcenanforderungen für einen Job und damit auch für den Knoten, auf dem er läuft, desto wahrscheinlicher wird er unterbrochen. Auf diese Weise ist es besser, einen Job mit 4 vCPU und 16 Gb RAM anzufordern als einen mit 32 vCPU und 128 Gb RAM.

  • Regionale Beliebtheit: Beliebte Regionen haben mehr Spot-Kapazität und damit eine geringere Wahrscheinlichkeit für Unterbrechungen. In einer Nischenregion beobachten wir 10–15 Mal mehr Spot-Unterbrechungen im Vergleich zu einer beliebten Region.

Darüber hinaus stellt AWS die Spot Placement Score API zur Verfügung, die die Wahrscheinlichkeit von Spot-Unterbrechungen für eine gegebene Ressourcenanforderung schätzt. Nach unserer Erfahrung treten Unterbrechungen häufig auf, wenn der Score unter 6 liegt. Wir verwenden diese API, um die Wahrscheinlichkeit von Spot-Unterbrechungen in unseren Spark-Jobs zu verringern. Jeder Spark-Job auf Conveyor wird in einer der drei möglichen Verfügbarkeitszonen (AZ) des Kubernetes-Clusters geplant. Um die geeignetste Zone auszuwählen, befragen wir die Spot Placement Score API und weisen den Treiber und alle Executor für diesen Job der ausgewählten AZ zu.

Die Auswirkungen einer Unterbrechung minimieren

Der zweite Schritt beim Umgang mit Spot-Unterbrechungen besteht darin, ihre Auswirkungen zu minimieren, wenn sie auftreten. Eine Möglichkeit, dies zu erreichen, besteht darin, den Spark-Treiber auf On-Demand auszuführen und Spot-Instanzen für die Executor zu verwenden. Solange der Treiber läuft, kann Spark mit fehlschlagenden Executor umgehen, da er sie automatisch neu erstellt. Darüber hinaus erfordert der Treiber typischerweise deutlich weniger Ressourcen als die Executor, sodass wir die meisten Kostenvorteile für die Nutzung von Spot-Instanzen beibehalten können.

Eine weitere Möglichkeit, die Auswirkungen von Spot-Unterbrechungen auf Spark-Jobs zu verringern, besteht darin, die Spark-Decommissioning-Funktion zu aktivieren. Diese Funktion überträgt den Shuffle-Zustand von einem Executor, der kurz vor der Beendigung steht, an einen anderen aktiven Executor, wodurch die Notwendigkeit entfällt, Zwischenresultate neu zu berechnen.

Spot-Unterbrechungen für Benutzer sichtbar machen

Wenn beide vorherigen Ansätze fehlschlagen, ist Ihre letzte Möglichkeit, Spot-Unterbrechungen für Ihre Benutzer sichtbar zu machen. Dies ermöglicht es ihnen, diese Jobs (automatisch) erneut auszuführen, da der Job beim nächsten Mal wahrscheinlich erfolgreich sein wird.

Dies kann mithilfe des AWS- oder Azure-Knotenterminierungsmanagers implementiert werden. Diese Komponente läuft auf jedem Knoten und wendet einen Taint an, wenn eine Benachrichtigung über eine Spot-Unterbrechung vom Cloud-Anbieter empfangen wird. Diese Benachrichtigungen werden typischerweise 1-2 Minuten vor der Beendigung der Instanz gesendet, sodass Zeit für ein sauberes Herunterfahren bleibt. Wenn ein Job mit einem SIGKILL fehlschlägt, überprüfen wir, ob der Knoten getaint wurde, und wenn ja, klassifizieren wir den Jobfehler als durch eine Spot-Unterbrechung verursacht.

Fazit

In diesem Blogbeitrag habe ich 3 wichtige Aspekte bei der Verwendung von Kubernetes für Batch-Workloads hervorgehoben, nämlich:

  • Optimieren Sie Ihre Knoten-Effizienz, um Ihre Rechenkosten zu minimieren.

  • Skalieren Sie Ihren Cluster schnell hoch und runter, um die Leistung zu steigern und die Rechenkosten zu senken. Dies kann durch das Anpassen der Karpenter- und Cluster-Autoscaler-Einstellungen erreicht werden.

  • Spot-Instanzen können eine großartige Möglichkeit sein, die Kosten Ihrer Workloads zu reduzieren, wenn Sie wissen, wie man mit Spot-Unterbrechungen umgeht. Ich habe 3 Möglichkeiten aufgezeigt, dies zu tun: Verringerung der Wahrscheinlichkeiten ihres Auftretens, Begrenzung ihrer Auswirkungen und sichtbar machen für Benutzer.

Probieren Sie diese Verbesserungen an Ihren eigenen Workloads aus und lassen Sie mich wissen, wie es läuft. Klatschen Sie, wenn Sie diesen Beitrag aufschlussreich fanden, und hinterlassen Sie einen Kommentar, wenn Sie an anderen Verbesserungen denken.

Latest

Portable by design: Rethinking data platforms in the age of digital sovereignty
Portable by design: Rethinking data platforms in the age of digital sovereignty
Portable by design: Rethinking data platforms in the age of digital sovereignty

Portable by design: Rethinking data platforms in the age of digital sovereignty

Build a portable, EU-compliant data platform and avoid vendor lock-in—discover our cloud-neutral stack in this deep-dive blog.

Cloud-Unabhängigkeit: Test eines europäischen Cloud-Anbieters gegen die Giganten
Cloud-Unabhängigkeit: Test eines europäischen Cloud-Anbieters gegen die Giganten
Cloud-Unabhängigkeit: Test eines europäischen Cloud-Anbieters gegen die Giganten

Cloud-Unabhängigkeit: Test eines europäischen Cloud-Anbieters gegen die Giganten

Kann ein europäischer Cloud-Anbieter wie Ionos AWS oder Azure ersetzen? Wir testen es – und finden überraschende Vorteile in Bezug auf Kosten, Kontrolle und Unabhängigkeit.

Hören Sie auf, schlechte Qualitätsdaten zu laden
Hören Sie auf, schlechte Qualitätsdaten zu laden
Hören Sie auf, schlechte Qualitätsdaten zu laden

Vermeide schlechte Daten von Anfang an

Das Erfassen aller Daten ohne Qualitätsprüfungen führt zu wiederkehrenden Problemen. Priorisieren Sie die Datenqualität von Anfang an, um nachgelagerte Probleme zu vermeiden.

Hinterlasse deine E-Mail-Adresse, um den Dataminded-Newsletter zu abonnieren.

Hinterlasse deine E-Mail-Adresse, um den Dataminded-Newsletter zu abonnieren.

Hinterlasse deine E-Mail-Adresse, um den Dataminded-Newsletter zu abonnieren.

Belgien

Vismarkt 17, 3000 Leuven - HQ
Borsbeeksebrug 34, 2600 Antwerpen


USt-IdNr. DE.0667.976.246

Deutschland

Spaces Kennedydamm,
Kaiserswerther Strasse 135, 40474 Düsseldorf, Deutschland


© 2025 Dataminded. Alle Rechte vorbehalten.


Vismarkt 17, 3000 Leuven - HQ
Borsbeeksebrug 34, 2600 Antwerpen

USt-IdNr. DE.0667.976.246

Deutschland

Spaces Kennedydamm, Kaiserswerther Strasse 135, 40474 Düsseldorf, Deutschland

© 2025 Dataminded. Alle Rechte vorbehalten.


Vismarkt 17, 3000 Leuven - HQ
Borsbeeksebrug 34, 2600 Antwerpen

USt-IdNr. DE.0667.976.246

Deutschland

Spaces Kennedydamm, Kaiserswerther Strasse 135, 40474 Düsseldorf, Deutschland

© 2025 Dataminded. Alle Rechte vorbehalten.