Blog

So reduzieren Sie Cloud-Abfall

Wenn Sie heute sagen, dass Sie die meisten Anwendungen in Ihrem eigenen Rechenzentrum hosten, ist das so, als würden Sie Ihren eigenen Strom erzeugen. Warum sollten Sie so viel Kapital ausgeben, um genügend Hardware bereitzustellen, um Ihre Spitzenlasten abzudecken, die möglicherweise nur einmal im Jahr auftreten, und dann noch die Betriebskosten aufwenden, um diese Maschinen mit Strom zu versorgen, zu kühlen und zu warten? Lassen Sie das einfach jemand wie Amazon, Microsoft oder Google für Sie erledigen und zahlen Sie nur für das, was Sie verbrauchen, wie bei Ihrer Stromrechnung.

Dies ist das Versprechen des Cloud Computing: ein nutzungsbasiertes Preismodell mit niedrigeren Rechnungen für die Ausführung Ihrer unternehmenskritischen Anwendungen.

Leider sieht die Realität oft anders aus und die Leute stellen fest, dass der Wechsel in die Cloud sie mehr kostet als das Hosting vor Ort. Wie kann das sein und wie können wir das Problem lösen?

Schauen wir uns speziell JVM-basierte Anwendungen mit Java an. Allerdings können auch viele andere Sprachen wie Kotlin, Scala und Clojure für die JVM kompiliert werden.

Der moderne Ansatz zur Architektur von Cloud-basierten Anwendungen besteht in der Verwendung von Microservices. Anstatt eine einzelne, monolithische Anwendung zu entwickeln, teilen wir die Anwendung in einzelne Dienste auf, die lose gekoppelt, aber dennoch hochgradig zusammenhängend sein können. Wenn ein Dienst auf diese Weise zu einem Leistungsengpass wird, können wir neue Instanzen dieses Dienstes hochfahren, die Last ausgleichen und den Engpass beseitigen, ohne andere Teile des Systems ändern zu müssen.

Hier kann eine der Kernfunktionalitäten der JVM zur Verschwendung von Cloud-Ressourcen führen.

Um das Versprechen „Einmal schreiben, überall ausführen“ einzulösen”, Java-Anwendungen werden in Bytecodes kompiliert, die Anweisungen einer virtuellen Maschine und nicht eines bestimmten Prozessors. Wenn eine Java-Anwendung gestartet wird, erstellt die JVM ein Profil davon und identifiziert häufig verwendete Code-Hotspots, die in nativen Code kompiliert werden können. Diese Just-in-Time-Kompilierung (JIT) bietet eine hervorragende Leistung, da die JVM genau weiß, wie der Code verwendet wird, und ihn entsprechend optimieren kann.

Allerdings kann die Zeit, die zum Identifizieren und Kompilieren aller häufig verwendeten Codeabschnitte benötigt wird (was eigentlich ein komplexerer, mehrstufiger Prozess ist), länger als gewünscht sein. Diese sogenannte Aufwärmzeit ist bei lang laufenden Prozessen wie Web- oder Anwendungsservern im Allgemeinen kein Problem. Microservices können häufig gestartet und gestoppt werden, um dynamisch auf Lastschwankungen zu reagieren. Wenn man darauf wartet, dass ein Microservice aufgewärmt wird, bevor er seine volle Kapazität bereitstellen kann, verringert sich der Nutzen dieses Ansatzes.

Eine häufig verwendete Lösung besteht darin, mehrere Instanzen eines Dienstes zu starten und laufen zu lassen, damit sie bei Bedarf sofort die volle Leistung erbringen können. Dies ist offensichtlich sehr verschwenderisch und verursacht unnötige Kosten für die Cloud-Infrastruktur.

Wie können wir dieses Problem lösen?

Ein Ansatz besteht darin, die Ahead-of-Time-Kompilierung (AOT) zu verwenden. Anstatt die JIT-Kompilierung zu verwenden, wird der gesamte Code direkt in native Anweisungen kompiliert. Dadurch entfällt die Aufwärmphase vollständig und die Anwendung startet mit der vollen verfügbaren Leistung.

Obwohl dies wie die ideale Lösung klingt, ist sie mit Kosten und Einschränkungen verbunden.

AOT kompiliert Code, ohne zu wissen, wie er tatsächlich verwendet wird, wodurch das Optimierungspotenzial begrenzt wird. Die JIT-Kompilierung verfügt über Profilinformationen, die Optimierungen ermöglichen, die genau auf die Art und Weise zugeschnitten sind, wie die Anwendung verwendet wird. Dies führt normalerweise zu einer geringfügig besseren Gesamtleistung.

Für kurzlebige Microservices, sogenanntes Serverless Computing, bietet AOT eindeutige Vorteile. Für jeden Service, der mindestens ein paar Minuten lang läuft, führt JIT zu einer besseren Leistung und damit zu niedrigeren Cloud-Computing-Kosten.

Eine alternative Lösung hat Azul als Teil seiner leistungsstarken Java-Runtime-Umgebung Platform Prime implementiert. Dazu gehört die ReadyNow-Technologie zur Aufwärmbeseitigung.

Das Problem besteht, wie wir gesehen haben, darin, dass die JVM jedes Mal, wenn wir eine Instanz eines Microservice starten, dieselbe Analyse durchführen muss, um Hotspots zu identifizieren, Profilinformationen zu sammeln und sie in nativen Code zu kompilieren. Dies geschieht sogar, wenn wir diesen Microservice schon viele Male zuvor auf dieselbe Weise verwendet haben. Mit ReadyNow wird der Service gestartet und kann in der Produktion mit realen, nicht simulierten Anfragen aufgewärmt werden. Wenn der Service vollständig aufgewärmt ist (sein optimales Leistungsniveau erreicht hat), wird ein Profil erstellt. Dieses Profil enthält alle Informationen, die zum Erreichen dieses Leistungsniveaus erforderlich sind: eine Liste von Hotspots, Profildaten und sogar kompilierten Code.

Wenn der Dienst erneut gestartet werden muss, wird das Profil als Teil der Ausführungsparameter bereitgestellt. Die JVM verwendet das Profil, um sicherzustellen, dass die Leistung bei der Verarbeitung der ersten Transaktion fast auf dem Niveau ist, das sie hatte, als das Profil erstellt wurde (sie liegt bei etwa 98 %, da einige technische Einschränkungen bei der Funktionsweise der JVM die Bereitstellung von 100 % verhindern).

Das Ergebnis ist, dass fast die gesamte Aufwärmzeit entfällt, während alle Leistungsvorteile der JIT-Kompilierung erhalten bleiben. Dieses System bietet vollständige Flexibilität, da für denselben Dienst unterschiedliche Profile verwendet werden können, je nachdem, wann und wo der Dienst verwendet wird. Beispielsweise kann das Arbeitslastprofil an einem Montagmorgen ganz anders sein als an einem Freitagnachmittag. Es können mehrere Profile gespeichert und bei Bedarf das entsprechende ausgewählt werden.

Da JVM-basierte Microservices nun nur noch eine minimale Aufwärmzeit benötigen, ist es nicht mehr nötig, einen Pool von Diensten zu verwalten, die untätig im Hintergrund herumliegen. Dadurch lässt sich die Cloud-Verschwendung erheblich reduzieren.

Eine leistungsoptimierte JVM, die auch ein alternatives Speicherverwaltungssystem enthält und die damit normalerweise verbundene Latenz für Transaktionen eliminiert, ist eine großartige Option. Das JIT-Kompilierungssystem wurde ebenfalls verbessert, um einen höheren Durchsatz zu erzielen. Anstatt den Cloud-Verschwendungsaufwand zu reduzieren, reduzieren diese einfach die Cloud-Ressourcen, die erforderlich sind, um die gleiche Übertragungskapazität bereitzustellen. Der Effekt besteht darin, die Cloud-Kosten noch weiter zu senken.

Sehen wir uns ein Beispiel an, wie dies bei einem echten Kunden funktioniert hat. Supercell ist ein Unternehmen, das einige der größten Online-Multiplayer-Spiele der Welt betreibt. Bei der jüngsten Veröffentlichung von Brawl Stars kam es beim Hochfahren neuer Server zu Verzögerungen, da die JVMs den erforderlichen Code kompilierten. Durch die Umstellung auf Azul Platform Prime und die Nutzung von ReadyNow konnte eine viel konsistentere Lastkapazität bereitgestellt, die Spielverzögerung verringert und die CPU-Auslastung bei gleicher Arbeitslast um 20 bis 25 % gesenkt werden.

Eine JVM, die schnelleren Code ausführt, bedeutet eindeutig, dass weniger Cloud-Ressourcen benötigt werden.

Gruppe Erstellt mit Sketch.