Seit vielen Jahren geht der Trend dazu, dass die Anwendungen immer mehr in der Cloud betrieben werden, sei es in der Public oder Private Cloud. Was bedeutet Cloud nun für eine Anwendung, muss etwas anders gemacht werden als bei vielleicht herkömmlichen Anwendungen, die in den 2010er Jahren entwickelt und betrieben wurden?
Jahrelang wurden Java-Anwedungen in Applikation(Servlet)-Servern betrieben. Dafür wurde im Vorwege so genau wie möglich überlegt, wie viele Resourcen (RAM, CPU, Instanzen) eine Anwendung benötigt, um alle Anfragen zuverlässig bedienen zu können. Das war immer eine ziemliche Herausforderung, zumindest bei Anwendungen, die ein sehr wechselndes Anfrageprofil haben und spontan mit höheren Lastanfragen zu tun haben. Geplante Aktionen waren zum Teil zu bewältigen wie z.B. Verkaufsaktion zu Zeitpunkt x. Da wurden dann ein paar mehr Applikationserver gestartet. Ungeplanten Aktionen wie z.B. ein Tweet von Mister X, können zu sehr spontanen Lastspitzen führen, die dann nicht mehr durch manuelle Vorarbeiten bewältigt werden können. Bei spontanen Lastspitzen müssen die Anwendungen einfach und schnell automatisierte Skalierung unterstützen. Außerdem müssen im eigenen Rechenzentrum genügend Resourcen bereitstehen. Bei moderaten Zuwächsen ist ein manuelles oder automatisiertes Skalieren mit vielen Anwendungen möglich. Problematisch wird es bei extremen und spontanen Aktionen. Dort steigt die Zahl an Anfrangen in kürzester Zeit an ohne, dass genügend notwendige Anwendungspower nach skaliert werden kann. [caption id="" align="aligncenter" width="600"]
Request Anzahl bei unterschiedlichen Aktionen[/caption]Unter anderem aus diesen Gründen werden immer mehr Anwendungen in Cloud-Infrastrukturen umgezogen. Dabei gab es über die letzten Jahre einen gewissen Wandel. Es fing zunächst damit an, dass Anwendungen in eigenen Kuberenetes- oder Mesos-Cluster betrieben wurden, bevor dann zum Teil der gesamte Cluster nicht mehr im eigenen Rechenzentrum betrieben wird, sondern in der Public-Cloud. Die Vorteile von moderen Cloud-Infrastrukturen liegen auf der Hand. Eine große Menge an Resourcen, die jeder schnell dazu buchen kann um, die Anwendung möglichst automatisiert zu skalieren. Zusätzlich bieten Cloud-Infrastrukturen eine Vielzahl Services, die einfach verwendet werden können, alles bereitgestellt bzw. angebunden durch IaaS-Tools.Damit das Ganze reibungslos funktionert, sollten die eigenen Anwendungen möglichst ein paar Eigenschaften mitbringen. Was muss eine Cloud Native Anwendung mitbringen?
Die ersten Schritte sehen dabei häufig gleich aus. Typische Anwendungen, seien es Services einer Microservice-Architektur oder ein Monolith, werden von der lokalen Infrastruktur mit minimalen Anpassungen auf Server in der Cloud verlagert. Oder von einen lokal betriebenen Kubernetes-Cluster in die Cloud. Das bietet bereits ein paar Vorteile, da die große Anzahl an Resourcen der Public-Cloud Anbieter für eine mögliche Skalierung bereitstehen. Allerdings sind die Kosten dann häufig höher als im eigenem Rechenzentrum. Die Instanzen der Anwendungen laufen dauerhaft in vorher kalkulierter Anzahl. Die Skalierung dauert häufig recht lang und ist häufig auf manuelle Schritte ausgelegt. Zudem ist der Resourcenverbrauch eventuell doch relativ hoch, was die Kosten unnötig hoch hält.
Betrachten wir die Anforderung an Cloud Native Anwendung speziell für Java Technologien, stellt sich die Frage, ob Java überhaupt eine geeignete Sprache für Cloud Anwendungen ist. Auf den ersten Blick sprechen die Rahmenbedingungen nicht für Java. Der Resourcenverbrauch ist häufig hoch, gerade RAM wird den Anwendungen sehr gerne üppig zur Verfügung gestellt. In klassischen Umgebungen findet in der Regel keine klare Kostenaufschlüsselung statt, so dass es nicht gleich so offensichtlich wird, welche Kosten für welchen Resourcenverbrauch anfallen. Das ist in Public-Clouds komplett anders. Um Kosten zu sparen, die jetzt ziemlich offensichtlich sind und den Anwendungen direkt zugeordnet werden können, werden möglichst wenig Resourcen zugeteilt. Das führt dann leider dazu, dass die Anwendungen lange Startzeiten benötigen. Damit sind wir gleich bei der größten Herausforderung von modernen Java-Anwendungen, die auf Spring(Boot), Quarkus oder Micronaut basieren. Alle benötigen lange bis sehr lange, bis die Anwendung für die erste Aktion wirklich zur Verfügung steht. Typische SpringBoot-Anwendungen haben mit einer größeren Startup-Zeit (> 4 Sekunden) zu kämpfen. Das Ganze wird verstärkt, wenn die Anwendung mit wenig CPU betrieben wird. Ist eine Java-Anwendung erst einmal gestartet und läuft einige Zeit, ist die Performance auch bei geringen Resourcenverbrauch sehr gut und Antwortzeiten im niedrigen Millisekundenbereich gut möglich. Das Problem der längeren Startup-Zeiten liegt zum einen in der Anwendung selber, aber auch in der Natur der JVM. Diese ist nicht dafür konzipiert, den Code möglichst schnell zu starten. Bekanntermaßen wird der Java-Quellcode zunächst in Java ByteCode kompiliert und dann erst zur Ausführung mit Just In Time Compiler in Maschinen Code übersetzt und ausgeführt. All das kostet Zeit, die bei einer sehr schnellen Skalierung in der Cloud nicht vorhanden ist. JVM in der Cloud
In der Cloud werden viele alternative Sprachen zu Java eingesetzt, wie z.B. Typescript auf Node.JS Runtime oder Go, da dort die Vorteile zunächst auf der Hand liegen: sowohl eine sehr schnelle Startup-Zeit als auch geringer Resourcenverbrauch im Allgemeinen. Auch ich bin in den letzten Jahren gerne auf Typescript basierte Anwendungen ausgewichen, um das Problem mit den Java-Anwendungen zu vermeiden. Aber ist das wirklich die Lösung, die Programmiersprache komplett zu wechseln, um eine schnellere Startup-Zeit für eine bessere Skalierung zu erhalten? Das ist bei Unternehmen, die viele Java-Experten haben, doch etwas schwierig. Gerade Spring Boot ist sehr weit verbreitet und bietet sehr viele nützliche Features für Business Anwendungen. Soll das über die Jahre aufgebaute Wissen nun nutzlos sein? Unter anderem die Spring Boot Konkurrenten Quarkus und Micronaut versuchen sich gerade in Punkto Startup-Zeit in Stellung zu bringen. Aber auch diese haben noch nicht genügend niedrige Startup-Zeiten, die für eine sehr schnelle Skalierbarkeit in der Cloud sinnvoll sind. Neben dem eigentlichen Application-Framework (Spring(Boot), Quarkus, Micronaut) hängt für eine schnelle Startup-Zeit auch viel vom schnellen Verbindungsaufbau z.B. zu Datenbanken (Database-Pool) ab.
Das Erfreuliche ist: es gibt aktuell zwei mögliche Lösungsansätze, Java Anwendungen wirklich Cloud native zu machen. Also im Kern die Startup-Zeiten deutlich zu reduzieren und damit eine sehr schnelle Skalierbarkeit zu gewährleisten und das bei möglichst niedrigen Resourcenverbrauch.Der eine Ansatz verwendet die GraalVM, um Native Images zu bauen. Dieser Ansatz wird schon viel in Artikeln/Konferenzen betrachtet. Etwas neuer und noch unbekannter ist der Einsatz von CRaC (Coordinated Restore at Checkpoint).In nachfolgenden Artikeln werde ich beide Lösungsmöglichkeiten einmal näher betrachten, um zu sehen, welche Vor- und Nachteile dabei eventuell entstehen.