Wie man mit Autoscalern Ressourcen verschwendet – eine Kubernetes-Geschichte
"Wir verwenden Autoscaler, unsere (Kubernetes-)Umgebung ist ausreichend optimiert."
Wem kommt das auch bekannt vor? Jeder, der sich mit dem Thema Kosteneffizienz in der Cloud beschäftigt, hat diese Aussage schon einmal gehört. Cloud-Kosten sind ein unbeliebtes Thema in IT-Teams – nicht zuletzt, weil in den meisten Unternehmen das Cloud-Kostenmanagement und alles, was dazu gehört, in weit entfernten Abteilungen angesiedelt sind. Das Problem an dieser Aussage ist, dass sie falsch ist.
Cloud-Kostenoptimierung ist vor allem die Kombination aus einer gut durchdachten Architektur sowie der Ausnutzung der Elastizität der Cloud. Einmal konfigurierte Autoscaler und Loadbalancer, einmal gesetzte Alerts, einmal definierte Ressourcenanforderungen werden obsolet, sobald sich Workloads verändern. Und damit ist das Kostenmanagement ein wesentlicher Bestandteil der richtigen Konfiguration von Workloads und des technischen Betriebs – und zwar kontinuierlich. Wir erklären in diesem Artikel, warum Autoscaling allein nicht ausreicht.
Von Anfang an: Warum es so wichtig ist, die Cloud-Architektur mit einer Kostenperspektive zu betrachten
Um ein klares Bild der Cloud-Rechnung zu erhalten, benötigen Projektmanager, Product Owner und Finanzabteilungen den Input von DevOps-Teams, welche den Workload aufbauen und betreiben. Diese Zusammenarbeit ist entscheidend, bevor kostenbezogene Entscheidungen getroffen werden können.
Wir möchten diese technischen Teams dazu ermutigen, ihre Workloads und Architekturen mit Blick auf die Kosten zu betrachten. Durch den Einsatz von FinOps als Ansatz in der Entwicklungsphase können Cloud-Ressourcen effizienter genutzt werden, ohne dass zusätzlicher großer Aufwand entsteht, insbesondere wenn dieser Ansatz kontinuierlich eingesetzt wird.
Kubernetes abstrahiert das Verständnis der Cloud-Ressourcennutzung noch mehr und macht es dadurch schwieriger zu verstehen, wie und wo Kosten entstehen. Dennoch kann eine Kostenperspektive bei der Gestaltung der Workload-Architektur dazu beitragen, die Cloud effizient zu nutzen. Auf diese Weise helfen DevOps-Teams dabei, Ressourcenverschwendung in freie Ressourcen umzuwandeln, die dann zur Verbesserung ihres Arbeitsalltags re-investiert werden können.
Exkurs: Was ist FinOps?
FinOps ist eine Erweiterung von DevOps und eine kulturelle Praktik des Cloudkosten- und Effizienzmanagements. Als Rahmenwerk soll es IT- und Nicht-IT-Teams in Organisationen helfen, methodisch dem neuen Pay-as-you-go-Modell der Kostenentstehung gerecht zu werden.
Schon wieder eine neue Methode? Ja, denn die Cloud verändert, wie wir IT-Ressourcen beziehen. Im Datencenter-Zeitalter mussten IT-Ingenieure Hardware beziehungsweise Ressourcen zentral anfragen. Genehmigt durch zentrale Bereiche und in der Regel über den Einkauf abgewickelt, konnten z.B. neue Server bestellt und (bestenfalls) ein paar Wochen später für die Entwicklung provisioniert werden. Ein und dasselbe Team hat also die Kosten genehmigt und verwaltet. Mit der Entwicklung der Cloud hat sich Ressourcen-Provisionierung radikal geändert. Heute können IT-Ingenieure mit nur einem Knopfdruck Server (und alles, was dazu gehört) einkaufen – und die Kosten entstehen in dem Moment, wo die Ressourcen eingesetzt werden über die Dauer, die sie aktiv sind. Kosten entstehen also sofort und verursachungsgerecht.
Dennoch werden diese Kosten von nicht-technischen Teams verwaltet, die in der Regel sowohl über Budgetkürzungen als auch über Investitionen entscheiden. Da diese Teams oft nur über ein begrenztes Verständnis vom variablen Kostenmodell der Cloud verfügen, gestalten sich die Ausgabeentscheidungen entsprechend schwierig. Daher ist es von entscheidender Bedeutung, alle beteiligten Teams an einen Tisch zu holen.
In diesem Kontext kommt FinOps ins Spiel. FinOps zielt darauf ab, eine sinnvolle Zusammenarbeit zwischen den verschiedenen Teams – IT, Management und Finance – zu fördern, um Cloud-Kostendaten transparent und zuweisbar zu machen. Dadurch können Ineffizienzen identifiziert und Optimierungspotentiale aufgedeckt werden, um den geschäftlichen Wert der Cloud zu maximieren.
Warum es mit Autoscalern nicht getan ist
Einer der großen Vorteile der Cloud ist die Elastizität. Wer "lose gekoppelt" baut, kann die von den Public Cloud Providern angebotenen Services verursachungsgerecht nutzen. Sogenannte Autoscaler ermöglichen zusätzlich eine bedarfsgerechte Nutzung automatisiert, indem sie die Anzahl der bereitgestellten Ressourcen entsprechend dem aktuellen Bedarf dynamisch anpassen. Dadurch können Cloud-Ressourcen optimal genutzt werden.
In der Welt von Kubernetes, der Container-Orchestrierung, werden Autoscaler zu einem unverzichtbaren Werkzeug. Da Cloud-native Lösungen und die Nutzung von Containern die Entwicklung und den Betrieb von Ressourcen erheblich schneller und skalierbarer machen, wird jedoch zugleich das Management von Bedarfen komplexer. Hier ermöglichen Autoscaler eine dynamische und fortlaufende Anpassung der Ressourcenzuweisung an die aktuellen Arbeitslasten, was die Effizienz und Leistung der Anwendungen erheblich steigert.
Aber: Autoscaler erfordern – so wie alle anderen Ressourcen in der Cloud – eine korrekte Konfiguration, um effizient zu laufen. Und hier steckt der Teufel im Detail. Oftmals übersehen oder vernachlässigt, entsteht so Ressourcenverschwendung und damit einhergehend unnötige Kostenverursachung – und das, obwohl die Umgebung ja eigentlich auto-skaliert ist.
Doch bevor wir uns näher damit befassen, warum es mit Autoscalern nicht getan ist, müssen wir uns genauer anschauen, welche Arten es gibt und wie diese funktionieren. Dieser Artikel durchleuchtet die im Kubernetes-Bereich eingesetzten Autoscaler, da es hier noch filigranere Skalierungsmöglichkeiten gibt und wir am häufigsten von unseren Kubernetes-Engineers zu hören bekommen, dass ihre Autoscaler für Kostenoptimierung völlig ausreichen. Die hier beschriebenen Prinzipien – und später auch die Lücken in der Effizienz – lassen sich auf klassische Scaler (z. B. EC2 Autoscaler) und Load-Balancer übertragen.
Zurück zu den Autoscalern: Wer mit Kubernetes arbeitet, hat grundsätzlich drei Autoskalierungsmöglichkeiten: Den Horizontalen Pod Autoscaler, den Vertikalen Pod Autoscaler und den Cluster Autoscaler. Auch wenn unterschiedlich einsetzbar, haben alle gemein, die Nutzung der Cloud-Ressourcen bedarfsgerecht hoch- und herunterzufahren, um so eine Unter- oder Überprovisionierung zu vermeiden.
Der Horizontale Pod Autoscaler
Der Horizontale Pod Autoscaler passt die Anzahl der Pods je nach Auslastung an. Er überprüft die tatsächliche Nutzung der Pods im Vergleich zu dem angegebenen Schwellenwert. Sobald der Schwellenwert überschritten wird, wird automatisch ein neuer Pod erstellt, damit die Nutzung ausgeglichen ist.
Der Vertikale Pod Autoscaler
Der Vertikale Pod Autoscaler vergrößert oder reduziert Ressourcen nach Bedarf. Er überprüft, ebenso wie der Horizontale Autoscaler, auch die tatsächliche Nutzung der Pods, aber statt der Erstellung eines neues Pods fügt dieser mehr Ressourcen zum Pod hinzu, wodurch sich die Größe des Pods verändert.
Der Cluster Autoscaler
Der Cluster Autoscaler überprüft nicht die tatsächliche Nutzung der Pods, sondern überwacht die Ressourcenanfragen und skaliert demnach die Nodes. Er sieht sich die geplanten Pods an und kontrolliert im Nachhinein, ob es auf den Nodes ausreichend Platz für diese gibt. Wenn dies nicht der Fall ist, dann erstellt der Cluster Autoscaler einen neuen Node, damit die geplanten Pods platziert werden können.
Zusammengefasst lässt sich also feststellen, dass sich Cloud-native Lösungen dynamisch an die aktuellen Bedürfnisse anpassen können. Wie kann es also sein, dass sogenannte nicht ausgelastete Ressourcen in autoskalierten Umgebungen existieren? Wie ist es möglich, dass wir Verschwendung erzeugen, wenn Verschwendung durch die autoskalierte, bedarfsgerechte Nutzung vermieden werden sollte? Kurzum: Jede Optimierung, hier in diesem Fall die Autoscaler, ist nur so gut wie ihre Konfiguration.
Aus dem Sysdig 2023 Cloud-Native Security and Container Usage Report geht hervor, dass bei gut 50% der Autoscaler die notwendigen Grenzwerte nicht richtig gesetzt wurden – das bedeutet, dass sie schlichtweg nicht korrekt skalieren. Die Konfiguration liegt bei Autoscalern im Pod Template. Hier werden die Ressourcenanforderungen und -beschränkungen angegeben. Alle Autoscaler greifen auf diese Informationen zurück.
Die Requests sind Mindestanforderungen und geben an, wie viel CPU und Speicher ein Container benötigt, während Limits die maximale Menge definieren, die ein Container verbrauchen kann. Diese Parameter sind für die automatische Skalierung entscheidend, da sich die Autoscaler daran orientieren. Wenn die angeforderten Ressourcen die verfügbare Kapazität überschreiten, kann der Autoscaler zusätzliche Nodes hinzufügen. Das Festlegen von Limits ist wichtig, um Ressourcenkonflikte zu vermeiden und eine genaue Einschätzung der Ressourcennutzung zu ermöglichen. Wenn ein Container wiederholt seine Grenzen erreicht, kann der Autoscaler die Anzahl der Replikate erhöhen, um die Auslastung zu bewältigen.
In der Praxis besteht oft das Risiko, dass die Requests zu hoch gesetzt werden. In folgendem Beispiel sieht man, dass unsere tatsächliche Nutzung unter dem gesetzten Minimum liegt.
Warum ist das Request-Limit dann so viel höher als nötig?
Die Request-Limits werden von den Ressourcen-provisionierenden Teams gesetzt. Oftmals fehlt vorher die Zeit für sorgfältige Load-Tests. Oder es gibt kein Monitoring, das die Unterauslastung in den Kontext der entstehenden Kosten setzt. Oder es fehlt an Wissen oder Prozessen oder... oder... oder.
Da die Sicherstellung der Performanz das Wichtigste ist – insbesondere im laufenden Betrieb – wird deshalb lieber kein Risiko eingegangen. Anders ausgedrückt: Dieses Verhalten ist typisch menschlich oder einfach eine Gewohnheit. Wir möchten Engpässe vermeiden, da das Scheitern der Anwendung das Schlimmste wäre. Die Konsequenz: Lieber ein bisschen höher setzen.
Wenn wir erneut unser Beispiel betrachten und annehmen, dass wir hier 30 Millicores Ressourcenverschwendung haben, hört sich das erst einmal nicht viel an. Diese Verschwendung entsteht aber nicht nur in einem Pod, sondern in allen, die auf dem Node platziert sind. Wenn es zum Beispiel 50 Pods auf dem Node gibt, kommen wir auf 1.500 Millicores Ressourcenverschwendung. Diese große Menge kann durch die effiziente Nutzung von Autoscalern vermieden werden.
Wie Autoscaler effizient laufen können
Um eine effiziente Nutzung von Autoscalern sicherzustellen, bedarf es nur weniger Schritte. Zunächst sollten Ingenieure über den Einfluss ihres Verhaltens auf die Kosten aufgeklärt werden, ein Konzept, das als FinOps Enablement bekannt ist. Durch dieses Verständnis können fundiertere Entscheidungen getroffen werden.
Des Weiteren ist es wichtig, das Monitoring nicht nur auf die Performance zu fokussieren, sondern auch mit Kosteninformationen zu verknüpfen. Dies kann durch Eigenintegration oder den Einsatz von Out-of-the-Box-Tools erreicht werden.
Zusätzlich sollte die Kostenüberprüfung nicht nur monatlich erfolgen, sondern fest in die Sprintplanung integriert werden. So bleibt das Team kontinuierlich über die Kosten informiert und kann bei Bedarf Anpassungen vornehmen.
Schließlich ist es wichtig, gemeinsam mit dem Team Lösungen zu entwickeln, um die Skalierung sinnvoll anzupassen. Durch kontinuierliche Iteration können Verbesserungen implementiert und die Effizienz des Autoscalings optimiert werden.