Das Ziel bestimmt den Weg in die Cloud
Bevor man seine Cloud-Reise plant, sollte man sich über seine Ziele und Möglichkeiten klar sein. Um die zu migrierende Anwendung gut zu verstehen, ist ein bestehendes Systemkontext- und Deployment-Diagramm neben einer Aufstellung der wichtigsten verwendeten Hauptprodukte und -technologien hilfreich. Leider sind die vorhandenen Architekturdokumentationen entweder veraltet oder hierfür ungeeignet, sodass man an einem Architekturanalyseworkshop mit den entscheidenden Stakeholdern nicht vorbeikommt. Wenn man noch tiefer in die Fachlichkeit einsteigen möchte, haben sich eventorientierte Domain Storytelling Workshops bewährt, um grob zu verstehen, wie die einzelnen Teile zusammenhängen.
Die Ergebnisse können gut mit den strategischen Domain-Driven-Design-Methoden in ein grobes Systemarchitekturmodell übergeführt werden. Wo weder die Architekturdokumentation noch der Sourcecode des Systems vorhanden ist, macht ein Reverse Engineering der Verteilungsarchitektur über einen Netzwerkscan oder ein Monitoring-Werkzeug Sinn. Hat man den Quellcode und die CI-Pipeline im Zugriff, so kann man weitere Analysen laufen lassen, um die Code- und Testqualität, aber auch das Sicherheitsniveau der eingesetzten Fremdbibliotheken einschätzen zu können. Damit hat man eine gute Ausgangsbasis, um die grobe Komplexität und Risiken einer Migration in die Cloud einzuschätzen.
Abb. 1: Deployment-Möglichkeiten von Java-Anwendungen auf Azure
Wenn es keine vorgegebene Option für die zu verwendenden Azure-Dienste gibt, kann man sich grob an den in Abbildung 1 dargestellten Optionen entlang hangeln. Oft wird es doch auf eine PaaS-Variante von Kubernetes herauslaufen, entweder Azure Kubernetes Service (AKS) oder Azure Red Hat OpenShift. Gerade Letzteres bietet sich an, wenn man IBM- oder Red Hat-Produkte im Einsatz hat oder OpenShift-Zusatzfunktionen benötigt, da diese dort besser unterstützt werden. AKS unterstützt mindestens 12 Monate die installierte Kubernetes-Version (aktuell 1.24-1.27). Bei OpenShift (aktuell 4.11 basierend auf K8s 1.25) sind das mindestens 6 bis 24 Monate. Auf jeden Fall sollte man Kubernetes-Updates regelmäßig einplanen, um nicht zu große Test- und Anpassungsaufwände zu haben, da immer alle Updatezwischenversionen eingespielt werden müssen.
Auch wenn sich der Provider um das Update der Umgebung und das Upgrade der Kubernetes Control Plane kümmert, muss man Upgrades oder Updates auf die freigegebenen Versionen für die Kubernetes-Arbeitsknoten selbst durchführen und einplanen. Bei OpenShift gibt es dafür einen eigenen Managed-Upgrade-Operator (MUO). Wenn man hier eher auf die Funktionen von neueren Kubernetes-Versionen angewiesen ist, sollte man eher AKS als OpenShift einsetzen, was immer ein wenig länger braucht, um auf diese umzustellen. Das liegt auch daran, dass OpenShift immer auch einige Kubernetes-Zusatzprodukte und eigene Erweiterungen mitbringt. Um sich nicht um das Managen von Kubernetes kümmern zu müssen oder keinen Zugriff auf die Kubernetes APIs zu brauchen, gibt es Azure Container Apps, die es erleichtern, serverlose Microservices als Container mit einem gemanagten Kubernetes zu verwenden.
Für kleinere Webapps [AZJAVAOPTS] bieten sich die FaaS-Azure App Services (für Java SE, Apache Tomcat und JBoss EAP) an, die sich in die Azure-Dienste Azure Container Apps oder Azure Functions integrieren. Eine spezielle Variante ist Azure Spring Apps (ehemals Azure Spring Cloud Service) [AZSPR-TRAIN, SPRINGAZDG], die für Java Spring Boot und für asp.net Core Steeltoe verwendet. Gegenüber AKS haben diese den Vorteil, dass bei App Service, Azure Functions und Azure Spring Apps die Java-Laufzeitumgebung und – wenn verwendet – auch der Applikationsserver automatisch auf neuere Versionen aktualisiert wird, wodurch Sicherheitslücken schneller geschlossen werden.
Eine interessante Option ist die Azure Spring Apps Enterprise Tier [SPRINGAZENT], worüber man statt 12 Monate (Basic, Standard Tier [SPRINGAZPR]) mindestens 24 Monate erweiterten LTS-Support von Tanzu erhält. Außerdem gibt es die Möglichkeit zur nutzungsbasierten Bezahlung von Azure Spring Apps (Konsumptionsplan), was es Anwendungen vergünstigt, die kurz starten und schnell enden. [SPRINGAZCON].
Die Azure Container Instances (ACI) sind ein einzelner Pod von Hyper-V-On-demand-Containern, die als virtuelle Knoten (basierend auf Virtual Kubelet) in ein AKS-Cluster aufgenommen werden können. Diese bieten sich eher für Windows basierende Anwendungen an. Meist wird man bei AKS oder Azure App Service Linux vor Windows bevorzugen, da diese sich besser mit Containern vertragen. Im Preview für Linux gibt es den neuen On-premise-Dienst Azure Arc-enabled Kubernetes, um AKS-Cluster in einer hybriden Cloud zu managen oder die Entwicklung On-premise zu ermöglichen. Neben den Kubernetes Diensten auf GCP, AWS sollen auch die auf VMware vSphere oder Azure Stack HCI unterstützt werden.
Abb. 2: Entscheidungshilfe für mögliche Cloud-Anwendungsmigrationsoptionen [AZCONT]
Migration der Plattform
Bei einem Wechsel in die Cloud bietet sich die Nutzung eines PaaS-Angebots an. Meist läuft es darauf hinaus, dass man kommerzielle Produkte, wie Java-Laufzeitumgebung, Applikationsserver oder Datenbanken, durch gemanagte Dienste oder durch ähnliche Open-Source-Produkte ersetzt. Für relationale Datenbanken bietet Azure für MS SQL, MySQL, MariaDB und PostgreSQL gemanagte Dienste an. Diese haben den Vorteil, dass sie sich in andere Azure-Dienste gut integrieren lassen und dass man sich um das Verwalten, Überwachen, Sichern und Aktualisieren nicht kümmern muss.
Wenn man eine gemanagte NoSQL-Datenbank verwenden möchte, wird man bei Azure Cosmos DB, Azure Cache for Redis oder Apache Cassandra fündig. Für die Migration der Daten und die Konvertierung der Schemata kann der Azure Database Migration Service genutzt werden. Für die Datenbanken MS Access, MS SQL SERVER, MySQL, ORACLE, DB2, SAP-ASE, MongoDB, DynamoDB, CouchBase oder HBase werden entsprechende Leitfäden zur Azure-Datenbankmigration [AZMIGDB] angeboten. Die für PostgreSQL und MySQL vorhandene Option und ein neuer flexibler Server (s. Abb. 3) sind den für MariaDB immer noch vorhandenen starren Pakten (Basic, General Purpose, Memory Optimized) vorzuziehen. Für neuere Datenbankversionen wird nur noch der flexible Server angeboten und bietet mit automatischem Failover und synchroner Replikation in mehreren Verfügbarkeitszonen eine höhere Ausfallsicherheit. Bestehende Single-Server-Instanzen haben noch bis September 2024 Zeit, dorthin zu migrieren.
Abb. 3: Azure Database for MySQL flexible Server [AZMYFLEX]
Bei der Migration des Java-EE-Applikationsservers wird für WildFly/JBoss AS, Red Hat JBoss EAP, Oracle WebLogic Server oder IBM WebSphere die Migration zu AKS oder App Service JBoss EAP empfohlen. Bei Apache Tomcat wird App Service Tomcat, Azure Container Apps, Azure Spring Apps oder AKS empfohlen, was auch in dieser Reihenfolge den Anpassungs- und Lernaufwand wiedergibt. Eine Motivation kann auch der Wechsel der Java-Ablaufumgebung von Oracle JDK zu OpenJDK sein. Microsoft bietet hier für die aktuellen Versionen zusammen mit Azul ein auf OpenJDK basierendes Java (das aktuelle ist OpenJDK 17.0.6 LTS) für die Prozessorarchitekturen AArch64/ ARM64 und x64 Windows, MacOS und Linux an. Oft wird bei der Migration in die Cloud die Gelegenheit genutzt, ein Versionsupdate vorzunehmen, wenn man beim selben Produkt bleibt. Solche Updates kann man in seiner CI-Pipeline schon mal in der Testumgebung vorziehen.
Migration der Anwendung
Unabhängig davon, welchen Azure-Dienst man später nutzt, sollte klar werden, ob zum Beispiel nur ein Wechsel [JAVA2AZ, MO-DJEIS21] vom Oracle JDK 8 zu OpenJDK 17.0.6 LTS, ein Wechsel des Applikationsservers oder des Datenbankprodukts in Azure angedacht ist, oder ob es die Möglichkeit gibt, die Anwendung für Jakarta EE (Microprofile), Cloud-native (Spring Native, Quarkus mit Containern) oder Microservices (Spring Boot, Quarkus mit Kubernetes [KUBQUA22]) umzubauen. Das hat Einfluss auf den Umfang der Umbaumaßnahmen und damit einhergehende Kosten und Risiken.
Um sich vorher ein gutes Überblickbild über die Umstellungsaufwände zu machen, lohnt es sich, eine Analyse mit OpenRewrite, Red Hat Migration Toolkit for Applications, Tanzu Cloud Suitability Analyzer [CLSUITANA] oder dem Azure App Service Migration Tools [AZMIGTOOL] laufen zu lassen. Dann sollte die Zielplattform und das dabei eingesetzte Hauptframework klar sein. Wo möglich sollte man sich auch an den Best Practices einer Azure-Referenzarchitektur (s. Abb. 4) orientieren. Wir gehen davon aus, dass wir eine Tomcat basierende Java-EE-Webanwendung mit einer MYSQL-Datenbank haben, die wir nach Spring Boot und später in einem Container auf Kubernetes migrieren wollen. Das Erste, was geändert werden muss, ist das Auslieferungsformat. Statt einer WAR-Datei muss eine JAR-Datei erzeugt werden, die bereits alles und damit auch den Apache Tomcat embededed enthält. Wenn man Maven als Build-Werkzeug einsetzt, kann man das relativ einfach anpassen. Azure bietet mehrere Maven-Plug-ins an, die einem, je nach verwendetem Dienst, einiges an Arbeit abnehmen. Es gibt Maven-Plug-ins [AZMAVEN] für Azure App Service, Azure Web Apps, Azure Functions und Azure Spring Apps. Um ein neues Java-Projekt für Azure zu erstellen, kann man mit:
mvn com.microsoft.azure:azure-webapp-maven-plugin:2.10.0:config
einen Wizard aufrufen, der das Azure SDK [AZSDKJAVA] mit den passenden Java-Bibliotheken für Web Apps installiert. Alternativ geht das auch über den Archetyp:
mvn archetype:generate -DarchetypeGroupId=com.azure.tools
-DarchetypeArtifactId=azure-sdk-archetype
Der erstellt ein neues Projekt, indem man das bisherige pom.xml hineinkopiert.
Abb. 4: Azure Spring Apps Referenzarchitektur [SPRINGAZARC]
Später wird man sicher noch weitere Java-Bibliotheken, die als Maven-Bibliotheken zur Verfügung gestellt werden, verwenden, um die Anwendung noch besser mit den Azure-Diensten zu verheiraten. So gibt es zum Beispiel einen Logback Appender, der die Protokolle in Microsoft Application Insights zur Verfügung stellt, und eine Bibliothek für Azure Authentication und Identity, die es vereinfacht, die Authentifizierung über Azure-Dienste oder das Azure Active Directory (Azure AD) durchführen zu lassen. So bietet es sich an, für die von Azure gemanagten Datenbanken kein User-Passwort zu verwenden, sondern ein tokenbasiertes Verfahren, um die Credentials (im Azure Key Vault) getrennt von der Anwendung pflegen und schützen zu können.
Ein paar kleine Einschränkungen gibt es, die man bei der Migration zu Azure Spring Apps berücksichtigen sollte. Für die Zertifikatsverwaltung sollte man den Azure KeyVault statt dem JRE Keystore verwenden, da darauf kein Zugriff möglich ist und auch das Management in Azure einfacher ist. Ebenso sollten das Monitoring und Tracing angepasst werden. Hier gibt es zu verschiedenen Application Performance Management (APM)-Werkzeugen Integrationen. Am besten funktioniert das mit dem Application Insights Java In-Process Agent und Azure Monitor Log Analytics. Statt einer LogStash-ELK-Stack-Log-Aggregation sollte man das LogStash EventHub Plug-in verwenden, um die Diagnose im Azure Event Hub durchführen zu können. Alterativ geht das auch über den Elastic APM Java Agent. Hier muss man sich jedoch um die ELK-Infrastruktur selbst kümmern. Statt Zipkin wird dann auf Azure das OpenTelemetry Tracing Plug-in für Java [AZTELE] verwendet und zur Anzeige Azure Monitor Log Analytics.
Zum Arbeiten für IDEs gibt es Erweiterungen, die das Arbeiten mit Azure vereinfachen. Hier bieten sich die Azure-Spring-Apps-Erweiterungen für VS Code an. Für Spring Boot 3 gibt es mit Spring Cloud Azure 6.0 [SPRINGAZ60] eine erste Beta, die bereits Java 17 unterstützt und auch mit dem Spring Initialzr verwendet werden kann, um eine neues Spring-Cloud-Azure-Projekt für Maven oder Gradle zu erstellen. Für Spring Cloud Azure [SPRINGAZ] gibt es eine umfangreiche und aktuelle Dokumentation mit Beispielen, an denen man sich orientieren kann. Für die Nutzung der meisten Azure-Dienste gibt es passende Spring Cloud Azure Starter, die man in Maven verwenden kann. Bei der umzubauenden Tomcat-Webanwendung sollte man zuerst die Tomcat DataSources durch eine Spring DataSource ersetzen und dort die jeweilige Konfiguration der Datenbankverbindung vornehmen. Ähnlich ist es bei JNDI-Konfigurationen. Ebenso sollten Tomcat Realms durch Spring Security-Konfigurationen ersetzt werden. Wo möglich sollten Servlets durch Spring Rest oder MVC ersetzt werden.
Wie kann die Cloud-Reise weitergehen
Es sollte auch an das Monitoring [AZMONJ] gedacht werden, hier sollte in der Azure Spring App auf die Diagnoseeinstellung gesetzt werden, um die Ereignisse im Azure Monitor Log Analytics angezeigt zu bekommen. Alternativ kann auch LogStash verwendet werden, um die Konsolenausgaben an den Azure Event Hub umzuleiten. Für Performanceüberwachung sollte der Application Insights Java Inprocess Agent mitinstalliert werden. Wenn der Resilience4J Circuit Breaker verwendet wird, können dessen Metriken ebenso vom Application Insights Java in-process Agenten verarbeitet und damit im Dashboard angezeigt werden. Damit ist es möglich, ohne zusätzliche Server alle Informationen an einer Stelle in Azure anzuzeigen und auszuwerten. Damit wird eine Spring Boot-Anwendung nativ in Azure integriert.
Spring Cloud Azure unterstützt bereits eine Vielzahl von Azure-Diensten. Angefangen bei der App-Konfiguration über Zugangsdaten- und Zertifikatsverwaltung mit dem Azure Key Vault oder Single Sign-on über Facebook oder Google mit Azure Active Directory B2C. Es werden aber auch asynchrone Nachrichten mit Azure Event Hubs, Azure Service Bus oder Azure Storage Queue unterstützt. Das Speichern in der Azure Cosmos DB, Azure Blob Storage oder Azure Files geht wie andere gemanagte Dienste auch. Diese Funktionen sind in Abbildung 5 dargestellt. Auch für Quarkus existieren, vor allem über Apache Camel, für einige Azure-Dienste (Functions, EventHub, StorageBlob/Queue) Erweiterungen. Diese gehen jedoch nicht so weit wie die Azure-Erweiterungen für Spring Boot.
Mit dem JDBC Authentication Plug-in für MS SQL, MySQL oder PostgreSQL kann die Authentifizierung der JDBC-Anmeldung über Azure AD erfolgen mit der azure-identity-extensions-Bibliothek [AZJDBCAUTHAD]. Für MS SQL und Azure SQL kann die Authentifizierung über Azure Active Directory und die Nutzung des Azure KeyVaults in der JDBC-Treiberversion 12.2 ebenfalls über die Bibliotheken Azure-Identity Version 1.7.0 und KeyVault-Keys Version 4.5.3 genutzt werden, wenn in den Azure Ressourcen der authentication=ActiveDirectoryManagedIdentity-Modus aktiviert ist.
Abb. 5: Spring Cloud Azure, Überblick über Funktionen [SPRINGAZ]
Für Last- und Performacetests [AZLOAD] unterstützt der Dienst Azure Load Testing das beliebte Werkzeug Apache JMeter. Um den Entwicklungsprozess zu automatisieren und den Betrieb zu optimieren, gibt es für Spring-native und Cloud-native Buildpacks die Möglichkeit, GraalVM mit Container-Images und Spring Boot als multiple layer build zu verwenden. Dadurch können Container-Images schneller aktualisiert und ausgeführt werden. In Spring Cloud Function gibt es Adapter für Azure Functions, um serverlose Anwendungen einfacher zu erstellen. Mit Spring Cloud Kubernetes, was den Fabric8 Kubernetes Java Client nutzt, verbessert sich die Integration mit dem Kubernetes-Ökosystem und die Nutzung zum Beispiel von Pod Health Indicator, Open Cluster Management, Istio ServeMesh, Tekton oder ChaosMesh.
Möchte man schnell seine Anwendung in einen Container packen, zum Beispiel für einen PoT, hilft Azure Migrate [AZCTMIG] mit seinem App Containerization Tool für Java weiter, um Web Apps mit Apache Tomcat zu inventarisieren, containerisieren und später auf AKS zu deployen, wenn die Voraussetzungen dazu gegeben sind.
Für containerisierte Anwendungen mit Spring Boot ist Azure gut aufgestellt und bietet eine breite Unterstützung. Serverlose Anwendungen (FaaS) sind möglich, wobei hier leichtgewichtigere Frameworks mit GraalVM-Unterstützung sicher geeigneter sind als aktuell Spring Boot. Beim lokalen Test von Azure-Diensten mit Testcontainern [TCAZ] ist man aktuell für Storage auf Azurite und CosmosDB eingeschränkt, sodass es vermutlich einfacher ist, direkt in der Azure Cloud zu entwickeln.
Fazit
Wie man sieht, gibt es für Java-Anwendungen auf Azure viele Möglichkeiten. Je nach Ziel und eigenem Wissen bietet es sich hier an, auf Cloud-native Frameworks, wie Spring Boot oder Quarkus, zu setzen. Bei Spring hat man den Vorteil, dass dieses viele Azure-Dienste direkt unterstützt und mit Azure Spring Apps auch als gemanagter Dienst angeboten wird.
Der andere Weg geht über Container und Kubernetes. Hierfür muss man jedoch mehr Wissen mitbringen und seine bestehenden Werkzeuge und DevOps-Prozesse umstellen. Ähnlich wie Linux ist Java inzwischen bei Azure eine weitverbreitete Option, sodass sich Microsoft hier auch an deren Open-Source-Weiterentwicklung beteiligt. Bei aller Begeisterung für die neuen Möglichkeiten sollte man die Kosten [AZPRICE] immer im Auge haben, um nicht zu viel für gar nicht benötigte Dienste zu zahlen oder um durch eine Reservierung auf 3 Jahre zusätzliche Rabatte zu erhalten. Ein Cloud-Migrationsprojekt gleicht mehr einer Reise in eine neue Welt als einem Tagesausflug. Deswegen wird man auf der Spring-Azure-Reise auch in Zukunft noch mehr entdecken und lernen. Viel Erfolg dabei! Be Cloud-native!
Weitere Informationen
[AZCONT] Comparing Container Apps with other Azure container options,
https://learn.microsoft.com/en-us/azure/container-apps/compare-options
[AZJAVAOPTS] Compare Java application hosting options on Azure,
https://learn.microsoft.com/en-us/azure/architecture/guide/technology-choices/service-for-java-comparison
[AZJDBCAUTHAD] Azure Active Directory for authenticating with MySQL or PostgreSQL,
https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-azure-ad-authentication, https://learn.microsoft.com/en-us/azure/postgresql/single-server/connect-java?tabs=passwordless,
Connect Azure SQL Database using Azure Active Directory authentication,
https://learn.microsoft.com/en-us/sql/connect/jdbc/connecting-using-azure-active-directory-authentication?source=recommen-dations&view=sql-server-ver16
[AZCTMIG] Java web app containerization and migration to Azure Kubernetes Service,
https://learn.microsoft.com/azure/migrate/tutorial-app-containerization-java-kubernetes
[AZLOAD] Azure Load Testing support for JMeter 5.5,
https://azure.microsoft.com/en-us/updates/azure-load-testing-support-for-jmeter-55
[AZMAVEN] Maven plugins for Azure,
https://github.com/microsoft/azure-maven-plugins
[AZMIGDB] Leitfäden zur Azure-Datenbankmigration,
https://learn.microsoft.com/data-migration
[AZMIGTOOL] Azure App Service migration tools,
https://azure.microsoft.com/en-us/products/app-service/migration-tools
[AZMONJ] Azure Monitor Ingestion client library for Java azuresdk-for-java/README.md at main · Azure/azure-sdk-for-java · GitHub
[AZMYFLEX] Azure Database for MySQL - Flexible Server,
https://learn.microsoft.com/en-us/azure/mysql/flexible-server
[AZPRICE] Azure, Preisrechner,
https://azure.microsoft.com/pricing/calculator
[AZSDKJAVA] Azure SDK for Java,
https://azure.github.io/azure-sdk/releases/2023-06/java.html, https://azure.github.io/azure-sdk/#java
[AZSPRMIG] Migrate Spring Cloud applications to Azure Spring Apps,
https://learn.microsoft.com/en-us/azure/developer/java/migration/migrate-spring-cloud-to-azure-spring-apps
[AZSPRTRAIN] Azure Spring Apps training,
https://github.com/microsoft/azure-spring-apps-training
[AZTELE] Azure OpenTelemetry Tracing plugin library for Java,
https://learn.microsoft.com/java/api/overview/azure/core-tracing-opentelemetry-readme
[CLSUITANA] Cloud Suitability Analyzer,
https://learn.microsoft.com/en-us/azure/developer/java/migration/cloud-suitability-analyzer
[JAVA2AZ] Java to Azure migration strategy documentation,
https://learn.microsoft.com/azure/developer/java/migration
[KUBQUA22] J, Clingan, K. Finnigan, Kubernetes Native Microservices with Quarkus and MicroProfile, Manning, 2022
[MODJEIS21] M. Eisele, N. Vinto, Modernizing Enterprise Java, 2021,
https://developers.redhat.com/e-books/modernizing-enterprise-java
[MSJAVA] Microsoft Build of OpenJDK,
https://www.microsoft.com/openjdk
[SPRINGAZ] Spring Cloud Azure,
https://spring.io/projects/spring-cloud-azure
[SPRINGAZ60] Spring Cloud Azure 6.0,
https://devblogs.microsoft.com/azure-sdk/announcing-the-beta-release-ofspring-cloud-azure-versions-6-0-0-beta-3-and-beta-4/
[SPRINGAZARC] Azure Spring Apps reference architecture,
https://learn.microsoft.com/en-us/azure/spring-apps/reference-architecture?tabs=azure-spring-enterprise
[SPRINGAZDG] Spring Cloud Azure developer guide,
https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/
[SPRINGAZENT] Azure Spring Apps Enterprise Tier,
https://techcommunity.microsoft.com/t5/apps-on-azure-blog/azure-spring-apps-enterprise-tier-is-now-generally-available/ba-p/3418245
[SPRINGAZCON] Azure Spring Apps consumption plan,
https://spring.io/blog/2023/03/22/start-from-zero-and-scale-to-zero-azure-spring-apps-consumption-plan
[SPRINGAZPR] Azure Spring Apps – Preise,
https://azure.microsoft.com/pricing/details/spring-apps/
[SPRINGAZTC] Migrate a Tomcat application to Azure Spring Apps,
https://learn.microsoft.com/en-us/azure/developer/java/migration/migrate-tomcat-to-azure-spring-apps
[TCAZ] Testcontainers for Java Azure Module,
https://www.testcontainers.org/modules/azure