CO2 einsparen mit kubernetes & container-basierten anwendungsarchitekturen.

Durch die steigende Digitalisierung und den immer stärker werdenden Einsatz von Cloud-Technologien rückt der Anwendungsbetrieb stärker in den Fokus zur Reduktion von CO₂ Emissionen in Unternehmen. Da die Cloud, sei sie private oder public, durchgehend das state-of-the-art Betriebsmodell darstellt, ist es wichtig von vornherein sicherzustellen, dass der effiziente Betrieb der Software durch eine optimale Nutzung der Ressourcen sichergestellt ist.

Auf Infrastruktur- und Anwendungsebene ergeben sich in Cloud-Anwendungen viele Stellschrauben, um einen energieeffizienten Betrieb zu gewährleisten. Dieser Beitrag soll Ideen und Lösungsansätze liefern, wie man mit einfachen Mitteln dazu beitragen kann, den Ausstoß klimaschädlicher Gase zu verringern und den Stromverbrauch zu reduzieren. Die vorgestellten Maßnahmen sind im Grunde für jede Anwendungsarchitektur geeignet, bieten aber gerade bei Microservice-Anwendungen das größte Einsparpotential. Voraussetzung ist lediglich, dass Container zum Einsatz kommen.

scheduling rechenintensiver prozesse in tageszeiten mit umweltfreundlicher stromerzeugung.

Die Grundidee ist simpel: Energieintensive, lange andauernde Prozesse dann ausführen, wenn der Strommix besonders "grün" ist, d.h. der Anteil regenerativ erzeugter Energie möglichst hoch.

Hierfür eignen sich zeitunkritische, automatisierbare Prozesse. Wichtig ist, bei der Wahl der Ausführungszeit (Tageszeit, Wochentag) so wenig wie möglich eingeschränkt zu sein. Dadurch sind die größten Einsparungen möglich.

Einige nicht anwendungsspezifische Beispiele hierfür sind:

  • Berichte erstellen
  • Backups durchführen
  • Aufräumen/Optimierung in der Datenbank

die benötigten daten: smard.

Woher aber bekommt man Daten über die Stromerzeugung in Deutschland? Glücklicherweise veröffentlicht die Bundesnetzagentur auf der Website Smard (https://www.smard.de) sehr detaillierte Informationen über die Stromerzeugung – für jeden zugreifbar, laufend aktualisiert und bis auf eine Stunde genau. Hier wird genau aufgeschlüsselt, welcher Energieträger wieviel Strom zu welcher Zeit bereitstellt.

Die Daten hinter dem ansehnlichen Diagramm kann man auch per API abrufen. Für unsere Zwecke benötigen wir die Gesamtmenge des mit erneuerbaren Energieträgern sowie des mit fossilen Energieträgern erzeugten Stroms. So können wir leicht bestimmen, wann eine gute Zeit für CO2-sparende Datenverarbeitung ist.

Die Schnittstelle hat allerdings auch ein paar Unzulänglichkeiten: So ist sie zum einen nicht vollständig dokumentiert, es fehlen einige URLs. Erst durch Analyse der Website mit den Chrome-Netzwerktools erhält man die benötigten API-Codes zur Abfrage der Gesamtmenge an konventionell/erneuerbar erzeugtem Strom.

Auch ist die Datenqualität hin und wieder suboptimal. Vereinzelte Datenpunkte der letzten Stunden fehlen manchmal und werden später nachgemeldet, wie man am lückenhaften Chart im Screenshot erkennt. Jede Farbe stellt dabei eine bestimmte Stromerzeugungsart dar.

Diagramm - Green Cloud
Diagramm - Green Cloud
Screenshot der visualisierten Daten von der Smard-Website.

Für Analysen in Echtzeit ist die Datenqualität also nicht gut genug, aber wie man im Bild sieht, gibt es bei der Energieerzeugung eindeutige Zyklen, die sich wiederholen. Zur Evaluation ziehen wir daher die jeweils gesamte letzte Woche heran, also von Montag bis Sonntag. Dadurch fallen einzelne fehlende Datensätze nicht so sehr ins Gewicht. Der Einfachheit halber nehmen wir an, dass die Stromerzeugung in der kommenden Woche ähnlichen Mustern folgt wie in der vergangenen. So können wir brauchbare Vorhersagen treffen.

Zusätzlich arbeiten wir mit einem Prozentwert, der steuert, welcher Anteil der Erneuerbaren an der gesamten Energieerzeugung gewünscht ist. Schaut man sich die Daten an, ist 80% ein ambitionierter, aber dennoch realistischer Wert. Braucht man mehr Flexibilität bei der Zeiteinteilung, kann man hier großzügigere Werte angeben – auf Kosten der CO2-Einsparungen, versteht sich.

die umsetzung: container-orchestrierung und scripting.

Im Cloud-Native Umfeld hat sich Kubernetes zur Standardlösung entwickelt, um Software in Containern ausliefern und verwalten zu können. Jedoch bringt Kubernetes wenig überraschend keine Funktion mit, einen Job gemäß der Stromdaten der Bundesnetzagentur einzureihen. Aber das stellt für uns kein Hindernis dar: Wir implementieren ein solches Feature einfach selbst. Kubernetes ist sehr vielseitig integrier- und steuerbar, sei es über kubectl, eine REST-API oder per Operator.

Zu Demonstrationszwecken nutzen wir die einfachste Variante: Ein Python-Skript und kubectl. So verbinden wir Kubernetes mit der Schnittstelle der Bundesnetzagentur. Mit Python hat man die Möglichkeit, prägnanten, gut lesbaren Code zu schreiben. Nicht zuletzt bietet die Sprache mächtige Features zur Verarbeitung von Daten. Das Skript kann man entweder manuell ausführen oder unter Unix-Systemen per crontab periodisch laufen lassen.

Script-1
Script-1
Script-2
Script-2
Python-Skript für das automatische Scheduling energieintensiver Workloads.

Das Skript registriert einen CronJob bei Kubernetes. Hiermit wird unser energieintensiver Prozess als Container in einem Kubernetes Pod ausgeführt. Das bedeutet also, zur angegebenen Zeit wird Kubernetes den Container selbstständig in einem Pod starten und auf dem Cluster platzieren. Nach Beendigung des Prozesses wird der Pod gelöscht und alle benötigten Ressourcen wieder freigegeben.

Eine Anmerkung hierbei zu Zeitzonen: Smard gibt Zeiten in mitteleuropäischer Zeitzone an, die meisten Kubernetes-Cluster „ticken“ aber in UTC. Damit das mit einer Standard-Installation von Kubernetes zusammenpasst, muss man entweder die Zeitzone im kube-controller-manager umstellen, sie im CronJob explizit angeben (ab Kubernetes 1.27) oder die Daten vorher nach UTC konvertieren. Letztendlich führt das zum selben Ergebnis.

wenn die prozesse nicht flexibel sind: lastadaptives scaling von ständig laufenden prozessen.

Nicht jeder Arbeitsvorgang kann aber zu beliebigen Zeiten ausgeführt werden. Häufig müssen Anwendungen sogar jederzeit verfügbar sein und Anfragen in Echtzeit beantworten. Ein typisches Beispiel ist ein Web-Dienst, der über eine API Daten ausliefert oder eine Webanwendung, die über eine grafische Benutzeroberfläche bedient wird.

So ein Dienst sollte naturgemäß möglichst hochverfügbar sein, also anstreben an 24 Stunden, 7 Tagen die Woche zu laufen. Aber auch in diesem Fall kann zumindest die Energieeffizienz optimiert werden, was wiederum den Ausstoß von klimaschädlichem Kohlenstoffdioxid verringert. In jedem Fall sollte man versuchen, Überkapazitäten zu vermeiden und die eingesetzten Ressourcen auf das wirklich Nötige zu beschränken.
Als nicht zu unterschätzenden Nebeneffekt profitiert man dann auch von geringeren Betriebskosten, ganz besonders in der Cloud mit Pay-as-you-go Preismodellen.

Umsetzen kann man das Konzept in Kubernetes mit dem sogenannten Horizontal Pod Autoscaler. Der Name ist Programm: Das ist ein Feature, das vollautomatisch die Anwendung horizontal hochskaliert, also die anfallende Last auf mehr Pods derselben Anwendung verteilt.
Wir implementieren das im Folgenden beispielhaft. Zuerst erstellen wir ein Deployment:

deployment
deployment
Simple Deployment-Konfiguration für eine hochverfügbare Beispielanwendung.

Zu Demonstrationszwecken setzen wir als Anwendung den Nginx-Webserver ein. So können wir mit geringem Aufwand Lastspitzen simulieren, indem eine große Menge an Requests generiert und an den Server gesendet wird. Dazu eignen sich Tools wie Apache Benchmark oder hey.
Um die simulierte Last automatisch abzufangen, setzen wir den angesprochenen Horizontal Pod Autoscaler ein:

autoscaler
autoscaler
Ein einfacher, auf Energieersparnis optimierter Autoscaler.

Übersteigt die durchschnittliche CPU-Auslastung über alle Pods hinweg nun die angestrebten 30 Prozent, wird hochskaliert. Fällt sie dagegen fortlaufend, aber mindestens für 30 Sekunden unter 30 Prozent, skaliert Kubernetes die Anwendung so lange runter, bis das angegebene Minimum erreicht ist. 30 Sekunden sind dabei ein kurzer Zeitraum, aber wir arbeiten mit der Annahme, dass nach dieser Zeit ohne besondere Last auch keine weiteren Lastspitzen mehr auftreten. So werden keine Ressourcen verschwendet und das Energiesparpotential maximiert.

Möchte man wie im Beispiel Performance-Metriken (CPU-, RAM-Auslastung) für das Autoscaling nutzen, muss dafür der Kubernetes Metrics Server im Cluster installiert sein. Standardmäßig werden die Metriken alle 60 Sekunden abgefragt, das Intervall lässt sich auf bis zu 15 Sekunden herabsenken. Damit wird das Autoscaling auch wesentlich schneller getriggert, was je nach Use Case mit Vor- oder Nachteilen verbunden ist. Da wir in unserem Fall möglichst schnell und viel Energie einsparen wollen, überwiegen die Vorteile.

So oder so: Kubernetes als verteiltes System lässt sich auch im Idealfall eine Bedenkzeit von einigen Sekunden, bis Anpassungen an der Skalierung vorgenommen werden. Um die Lastanpassung nicht unnötig noch weiter in die Länge zu ziehen, sollten die Startzeiten der Anwendungscontainer also möglichst kurz ausfallen. Bei der Anwendungsentwicklung helfen hierbei moderne Programmiersprachen wie Go oder in der Java-Welt AoT compiler wie GraalVM. Damit während der Zeit der Lastanpassung keine Anfragen verloren gehen, kann man sie z.B. in einen Cache speichern.

scale-to-zero.

Das größte Einsparpotential bietet naturgemäß das komplette Abschalten einzelner ungenutzter Dienste, auch genannt Scale-to-Zero. Möglich wird das, wenn die Funktionalitäten der Anwendung kleinteilig auf Microservices verteilt und lose gekoppelt sind – so kann man gerade nicht benötigte Funktionalitäten abschalten und andere weiterlaufen lassen. Der Kubernetes HPA kann das allerdings nicht von sich aus, es existieren aber Add-Ons, die das können. Ob sich der Zusatzaufwand lohnt, ist im Einzelfall abzuwägen.

Autor

Dominique Dorscheid

softwareingenieur