Eine Anwendungsfamilie auf zwei Produktivsystemen Cloud Native und Rechenzentrum im Vergleich
Anbieter zum Thema
Worin unterscheidet sich das Deployment über die Cloud von dem in einer klassischen Server-Infrastruktur? Und müssen Cloud-native Lösungen zwangsläufig auf Microservices setzen? Antworten liefert ein Praxisbeispiel, bei dem beide Architekturansätze parallel im Einsatz sind.

Flexibilität, Skalierbarkeit und Geschwindigkeit sind nur drei von vielen Aspekten, warum die Cloud für das Deployment neuer Anwendungen sowohl bei Unternehmen als auch Entwicklern beliebt ist. Oft wird dabei jedoch übersehen, dass mit der Cloud auch neue Schwierigkeiten und Anforderungen verbunden sind.
Neben neu zu lernenden Konzepten und Skills seitens des Entwicklungsteams, muss auch das Konzept des Betriebs überdacht werden. Außerdem stellen sich aufgrund der Dezentralisierung einer Cloud-Lösung neue Fragen bei Sicherheit, Architektur der Applikation, Netzwerklatenz oder der Handhabung bei Fehlern. Werden diese nicht ausreichend berücksichtigt, drohen Mehraufwände und zusätzliche Kosten.
Um die spezifischen Vorteile, aber auch die Herausforderungen einer Cloud-Infrastruktur besser beurteilen zu können, empfiehlt sich daher ein systematischer Vergleich mit einer technisch und fachlich ähnlich aufgebauten Anwendung, die auf einer klassischen Server-Infrastruktur deployed wird. So können die plattformspezifischen Besonderheiten herausgearbeitet und Kriterien für eine erfolgreiche Umsetzung abgeleitet werden.
Genau hier setzt der Beitrag an: Am Beispiel von zwei Generationen einer Anwendungsfamilie werden zunächst die allgemeinen Unterschiede zwischen dem Betrieb im Rechenzentrum und bei Cloud Foundry analysiert, um anschließend anhand der Kriterien aus dem 12-Factor App Manifest Empfehlungen für die Implementierung abzuleiten
Eine Anwendungsfamilie – zwei Plattformen
Bei der betrachteten Lösung handelt es sich um eine Familie von Anwendungen eines Fertigungsunternehmens, die entwickelt wird, um die Beschreibung und Pflege von Bauteilen zu automatisieren und den Freigabeprozess zu vereinfachen. Sie ersetzt bestehende Geschäftsprozesse, die zuvor durch Office Tools wie Excel abgebildet wurden, und umfasst zum jetzigen Stand zwei von perspektivisch mindestens drei Anwendungen.
Während die initial entwickelte Applikation (im Folgenden APP1 genannt) auf einer klassischen Server-Infrastruktur der Firma läuft, setzt die zweite Anwendung (APP2), auf eine firmeninterne Instanz von Cloud Foundry auf. Technologisch basieren beide Anwendungen allerdings auf einem ähnlichen Stack, indem ein Spring Boot Backend von einem Angular Frontend angesprochen wird.
Ausgeliefert wird jeweils ein Artefakt, das Frontend und Backend bündelt. Im Falle von APP1 handelt es sich hierbei um eine war-Datei, bei APP2 um ein Java Archive (jar). Zusätzlich gibt es eine gemeinsame Angular Frontend Bibliothek, die wiederkehrende Komponenten wie Tabellen oder Dialoge bereitstellt.
Die Anwendungen folgen nicht dem vorherrschenden Trend der Microservice-Architektur, sondern sind jeweils als modulare Monolithen aufgebaut. Dennoch sind die individuellen Anwendungen aufgrund ihrer fachlichen Abgeschlossenheit und der internen Modularisierung domänenspezifisch geschnitten.
Zum Persistieren von Daten werden jeweils folgende Technologien verwendet:
APP1 | APP2 | |
---|---|---|
Persistenz (Anwendung) | Hibernate, Spring Data, Flyway | Hibernate, Spring Data, Flyway |
Datenbank | Oracle | Amazon Aurora MySQL |
In-Memory Datastore | Hazelcast (embedded) | Redis (standalone) |
APP1 läuft produktiv auf zwei Server-Knoten, wobei die Last über einen Loadbalancer verteilt wird. APP2 hingegen kann flexibel skaliert werden und läuft produktiv mit vier Instanzen mit jeweils 3 GB Arbeitsspeicher.
Vergleich zwischen den Welten
Um die Unterschiede zwischen Rechenzentrums- und Cloud-basierter Applikationsumgebungen und daraus resultierende, plattformspezifische Vor- und Nachteile aufzuzeigen, vergleichen wir zunächst die beiden Applikationen in Bezug auf Deployment-Praxis, Architektur, Caching sowie Logging und Monitoring:
- Deployment
Das Vorgehen ist in beiden Fällen agil nach SCRUM und verläuft in Sprints mit Bereitstellung eines integrierten Inkrements und Deployments geplanter Releases alle drei Wochen. Während das Deployment bei APP1 jedoch strikt geregelten Prozessen folgt, die eine Trennung von Entwicklung und Betrieb vorsehen, ist diese bei APP2 vollständig aufgehoben.
Vielmehr, und ganz im Sinne des DevOps-Ansatzes, haben die Entwickler dort die Hoheit über alle Systeme, von der lokalen bis hin zur produktiven Instanz sowie über weitere angebundene Dienste. Im Falle von APP1 können die Entwickler nach Bedarf zwar auf die Testumgebung deployen, für die QA- und produktiven Instanzen ist dies allerdings nicht möglich.
Zudem sind dort die Jenkins Pipelines, welche die Artefakte bauen und deployen, nicht durch die Entwickler konfigurierbar, sondern werden vom Betrieb bereitgestellt. Das führt zu einer erschwerten Fehleranalyse, falls einer der Jobs fehlschlagen sollte. Denn statt direkt und selbstständig das Jenkins File anzupassen, sind E-Mail- oder Telefonkommunikation notwendig, um den Fehler zu lokalisieren und zu beheben. In der Folge können Features regelmäßig später als geplant deployed werden.
In der Cloud Foundry hingegen können die Entwickler wesentlich schneller auf vorgelagerte Stages deployen, bei Bedarf die Anzahl an Instanzen ändern, neue Dienste selbstständig anbinden und kleine Änderungen oder Fehlerbehebungen schnell live setzen. Dies funktioniert einfach mithilfe des Cloud Foundry Command Line Interfaces (CLI).
- Architektur
Aus der oben beschriebenen Struktur der Cloud Foundry resultiert eine auf den ersten Blick komplexere Applikations-Landschaft, als es bei APP1 der Fall zu sein scheint. Doch der erste Eindruck täuscht. Bei APP1 bzw. im Rechenzentrum werden diverse Infrastruktur-Konfigurationen durch den Betrieb vorgenommen und stellen somit eine Blackbox für die Entwickler dar.
Im Falle von APP2 hingegen wird das Management der individuellen Bausteine und deren Betrieb durch das Entwicklungsteam und ganz im Sinne eines DevOps Ansatzes durchgeführt. Berücksichtigt man die Aufgaben des Betriebs und die notwendige Kommunikation zwischen Entwicklung und Betrieb, übersteigt die Komplexität im APP1-Kontext diejenige von APP2 bei Weitem.
- Logging
Die Analyse von Bugs und die Früherkennung potenzieller Fehler unterscheidet sich in beiden Anwendungen ebenfalls erheblich. Während APP2, einem modernen Ansatz folgend, Log-Events mittels eines ELK-Stacks sammelt, aggregiert und in Kibana grafisch darstellt, setzt APP1, wie die meisten klassischen Web-Anwendungen, auf Logfiles. Dabei wird pro Tag eine Datei pro Server erzeugt und auf diesem abgelegt. Eine schnelle Analyse oder Früherkennung von Fehlern ist so nicht möglich.
Eine nachträgliche Anbindung von Splunk mit integriertem Alerting per E-Mail ermöglicht zwar ebenfalls eine Event-basierte Analyse der Logs – allerdings verzögerte sich die Umsetzung prozessbedingt um mehrere Monate. In Cloud Foundry hingegen konnte der Dienst einfach über die CLI erstellt und an die Anwendung angebunden werden.
- Monitoring
Die Trennung zwischen Entwicklung und Betrieb in der APP1-Anwendung schlägt sich auch im Anwendungs-Monitoring nieder. Die dort verwendete Lösung Grafana bietet zwar diverse Dashboards, die dem Entwicklungsteam helfen, verschiedene Aspekte der Applikation wie Request-Zeiten oder Heap schnell zu analysieren und Trends zu beobachten.
Allerdings haben auch hier die Entwickler nicht die Hoheit über die Anbindung an die Applikation. Somit ist es nicht möglich, bei Bedarf schnell und eigenständig neue Dashboards zu erstellen. Stattdessen muss dies über den Umweg der IT an einen externen Dienstleister weitergegeben werden. Dadurch können Anpassungen erst nach einigen Tagen oder sogar Wochen vorgenommen werden.
Im Vergleich dazu wurde bei APP2 die Überwachung sowohl in der Testphase als auch im produktiven Betrieb durch die Möglichkeit, eigenständig Monitoring-Lösungen anzubinden, stark vereinfacht. Die Analyse der Dashboards sowie das flexible Hinzufügen von neuen Metriken und Alerts bei Schwellwertüberschreitungen, ermöglichen dem Entwicklungsteam frühzeitig Fehler zu erkennen oder diese zielgerichtet zu lokalisieren.
Herausforderungen der Cloud-Umgebung
Während die Anbindung von Diensten in der Cloud im Allgemeinen einfach funktioniert und über die CLI erfolgt, sind bei der Umsetzung dennoch verschiedene Herausforderungen zu berücksichtigen. Der folgende Überblick zeigt mögliche Probleme bei einem Cloud-nativen Ansatz, aber auch bei einer möglichen Migration aus dem Rechenzentrum in die Cloud auf Basis der Erfahrungen mit der APP2-Anwendung auf:
- Probleme mit Datenbank-Dumps
Eine Schwierigkeit der Cloud-nativen Lösung besteht in der Erstellung, Aufbewahrung und dem Import von Datenbank-Dumps zu beliebigen Zeitpunkten. So war es aufgrund fehlender Rechte zunächst nicht möglich, direkt und bei Bedarf Datenbank-Dumps aus AWS heraus zu erstellen. Eine möglichst flexible und robuste Lösung für dieses Problem, die zusätzlich die Datenkonsistenz beim Erstellen eines Dumps garantiert, liefert eine selbstentwickelte Mini-Anwendung, der sog. Database-Dumper-Importer, pro Space.
Für APP2 ist ein Space im Wesentlichen eine Stage. Dieser kann sich über ssh mit der Datenbank verbinden, erstellt einen Dump mittels mysqldump und legt diesen dann in einem S3-Bucket ab. Von dort aus kann dann bei Bedarf ebenfalls über den Dumper ein Import stattfinden. Da ein solches Vorgehen typischerweise verschiedene Stages involviert, wird der Bucket als shared service eingebunden.
- Langläufer
Neben den Herausforderungen bei der Anbindung von Cloud-Diensten, sind auch Anpassungen innerhalb der anwendungsinternen Architektur notwendig. So wurden zunächst Funktionalitäten, die langlaufende Excel-Imports oder -Exports anstoßen, durch ein Request Timeout von 60 Sekunden in der Cloud Foundry blockiert. Um das zu umgehen, werden die von Spring bereitgestellte @EnableAsync-Annotation eingebunden und langlaufende Methoden mit @Async in gesonderten Threads gestartet.
- Automatisierung
Im Bestreben einer konsequenten Umsetzung von DevOps Prinzipien ist Automatisierung unerlässlich, um einen sicheren und fehlerfreien Betrieb in der Cloud Foundry zu gewährleisten. Wiederkehrende Aufgaben wie die oben beschriebene Erstellung von Dumps oder das Aufsetzen gesamter Spaces, sollten, um Zeit zu sparen und Fehleranfälligkeit zu reduzieren, auf Dauer nicht händisch ausgeführt werden.
Reibungsloser kann die Anwendung betrieben werden, wenn solche Aufgaben in Shell-Skipte oder kleine Programme ausgelagert werden. So kann beispielsweise ein gesamter Space, also die Anwendung an sich sowie alle angebundenen Dienste, mithilfe eines einzigen Skriptes erzeugt werden. Mehr noch, statt direkt die Skripte auszuführen, lassen sich über ein eigenentwickeltes, auf Python basierendes, Command Line Interface, alle Interaktionen mit Cloud Foundry steuern.
Einen weiteren Schritt in Richtung Automatisierung und insbesondere in Richtung CI/CD bildet die programmative Implementierung von Build- und Deployment Pipelines. Die Konfiguration des Jenkins Buildserver mit Jenkinsfiles stellt einen großen Unterschied zum Build-and-Deployment im Rechenzentrum dar, wo jegliche Konfigurationen durch den Betrieb vorgenommen werden müssen und somit keine DevOps-Kultur möglich ist.
Weiterführender Vergleich auf Basis der 12 Factor App
Aus den vorangegangenen Abschnitten ist bereits deutlich hervorgegangen, dass es fundamentale Unterschiede zwischen APP1 und APP2 im Hinblick auf Themen wie Betrieb, Infrastruktur und Deployment gibt. Um hieraus konkrete Hinweise für die Implementierung auf beiden Plattformen abzuleiten, können die Kriterien der 12-Factor App herangezogen werden.
Dabei handelt es sich um eine Ansammlung von Grundsätzen und Best Practices für die Entwicklung Cloud-nativer Applikationen. Sie wurden initial von Entwicklern bei Heroku herausgearbeitet, sind aber generisch und werden auch von anderen Plattformen wie Cloud Foundry adaptiert. Außerdem kann die Umsetzung einiger Faktoren auch den Betrieb im Rechenzentrum vereinfachen Zum Vergleich werden fünf der insgesamt zwölf Faktoren herangezogen:
Factor III: Config
Mit Config ist gemeint, dass alle Konfigurationen, die von Stage zu Stage variieren, nicht im Code, sondern über Umgebungsvariablen gesetzt werden. Das betrifft unter anderem URLs oder Schlüssel, die benötigt werden, um die Kommunikationen zwischen Anwendung und angebundenen Diensten zu ermöglichen. Ein naheliegendes und generisches Beispiel ist die URL der Datenbank.
APP1 verletzt dieses Prinzip bedingt durch die Betriebsumgebung an mehreren Stellen: Im Rechenzentrum mussten wir bei der Einbindung von Hazelcast auf eine spezielle Variante (TCP/IP) der Synchronisation zurückgreifen. Dazu müssen die IP-Adressen der involvierten Server in die Konfiguration von Hazelcast eingetragen werden. Nun sind diese Parameter offensichtlich abhängig von der Stage. Die 12-Factor App empfiehlt nun die Werte über Umgebungsvariablen zu setzen.
Die Prozesse im Rechenzentrum erwarten hingegen eine Stage-abhängige application-<STAGE_NAME>.yml Datei. Dort werden jeweils händisch die Werte eingetragen. Somit ist pro Stage eine eigene Datei, damit verbunden ein eigener Commit und natürlich ein eigener Build-Prozess nötig.
Cloud Foundry hingegen setzt viele relevante Parameter direkt bei der Erstellung von Diensten über die Umgebung. Die von APP2 verwendete In-Memory Redis-Lösung stellt zum Beispiel Umgebungsvariablen bereit, die mithilfe einer Spring Boot Konfiguration in die Anwendung injiziert werden. Eine Stage-abhängige Pflege der entsprechenden Parameter in verschiedenen Konfigurationsdateien ist folglich nicht mehr vonnöten.
Factor IV: Backing services
Ein weiterer Faktor besagt, dass die von der Applikation verwendeten Dienste als angebundene Ressourcen behandelt werden sollen. Dabei spielt es keine Rolle, ob es sich um plattformspezifische oder extern angebundene Services handelt. Aus Sicht der Anwendung stellen alle Dienste also eine Blackbox dar, die über eine lose gekoppelte Schnittstelle verfügbar gemacht wird. Die Kommunikation kann dann zum Beispiel über HTTPS, ssh oder andere Netzwerkprotokolle erfolgen. Auch dieses Prinzip wird bei der eingebundenen Variante von Hazelcast im Rahmen von APP1 verletzt. Da Hazelcast Teil der Anwendung ist, besteht eine starke Kopplung zwischen beiden.
In APP2 ist die Situation diesbezüglich deutlich komfortabler. Praktisch per Definition stellt Cloud Foundry Dienste über den bind-service Befehl als attached resources bereit. Aus Anwendungssicht überlegt man sich also, welche Dienste benutzt werden sollen und erzeugt und bindet diese mittels Kommandozeile. Konfigurationen, die zur Kommunikation zwischen Anwendung und Dienst nötig sind, können wie oben beschrieben über Umgebungsvariablen gesetzt werden.
Factor XI: Logs
Dieser Faktor besagt, dass Logs nicht in Dateien geschrieben, sondern als aggregierte, zeitgeordnete Events aller Prozesse – einschließlich angebundener Dienste – betrachtet werden sollen. Somit können sie durch einen angebundenen Dienst gesammelt und grafisch aufbereitet werden, womit die Analyse erheblich vereinfacht wird. Hier setzen mittlerweile beide Anwendungen auf moderne Aggregations-, Indexing- und Visualisierungstechnologien wie Splunk (APP1) oder den ELK-Stack (APP2).
Während die Anbindung in APP2 mittels eines simplen service-bindings umgesetzt wird, war für die Umsetzung der Splunk-Lösung im Rechenzentrum erheblicher Kommunikationsaufwand mit dem Betrieb notwendig. Außerdem werden parallel noch immer Logs in Dateien geschrieben. Während Cloud Foundry den Faktor praktisch per Konstruktion umsetzt, weil Instanzen ad-hoc vernichtet werden können und Log-Dateien dabei verloren gingen, ist im Rechenzentrum noch immer die Annahme, dass standardmäßig Logs in Dateien geschrieben werden.
Factor VII: Port binding
„Port binding“ bedeutet, dass eine Anwendung „self-contained“ sein soll, also insbesondere keinen externen Container wie Tomcat benötigt, um laufen zu können und erreichbar zu sein. Erreicht wird dies, indem die Applikation Endpunkte anbietet, die über ein spezifiziertes Protokoll – typischerweise HTTP(S) – an einen Port gebunden sind.
Im Spring-Boot-Umfeld ist dies praktisch der „out-of-the-box“ Mechanismus. Ein integrierter Tomcat impliziert, dass die Anwendung self-contained als jar-Datei ausgeliefert werden kann. Dennoch sind auf den virtuellen Maschinen, auf denen APP1 deloyed ist, Tomcat-Instanzen installiert, in denen die Anwendung ausgeführt werden muss. Diese müssen dediziert konfiguriert und gewartet werden. Bei APP1 ist es folglich nötig, statt einer jar- eine war-Datei auszuliefern, die dann im Tomcat ausgeführt wird.
Cloud Foundry benötigt lediglich die Information, welche Applikation mit welcher runtime ausgeführt werden soll. Beim Deployment der App kann dies über die Kommandozeile als Parameter mitgegeben werden. Das buildpack wird von Cloud Foundry bereitgestellt und stellt die nötige runtime bereit. Für eine Java-Applikation wäre hier zum Beispiel das java-buildpack einzutragen.
Zusammen mit dem zuerst betrachteten Faktor „Config“ wird erreicht, dass ein gleichbleibendes Artefakt einmal getestet und auf allen Stages deployed werden kann. Weiterhin können dadurch einfach zusätzliche Stages eingeführt werden, was Abstimmungen und Tests deutlich vereinfacht.
Factor X: Dev/prod parity
Nach dem Prinzip der „Dev/prod parity“ sollen die verschiedenen Stages so ähnlich wie möglich sein. Folgt man diesem Prinzip, reduziert das die Gefahr, erst spät im Entwicklungs- bzw. Deployment-Prozess auf Probleme zu stoßen. Unter Cloud Foundry bedeutet das beispielsweise, dass auf jeder Stage die gleichen Dienste an die Anwendung angebunden werden.
Hierbei hilft Automatisierung: In der APP2-Anwendung wird die eigens geschriebene CLI beim Aufsetzen und Deployment einer Stage zum Beispiel nur mit dem Namen des Space parametrisiert. Intern wird dann sichergestellt, dass die gleichen Dienste erstellt und an die Anwendung gebunden werden. Das Management der benötigten Ressourcen pro Stage dann über Umgebungsvariablen. In APP1 ist erheblicher Abstimmungsaufwand vonnöten, um Ähnliches zu erreichen. Die Entwickler definieren, welche Ressourcen benötigt werden, die Umsetzung erfolgt hingegen durch den Betrieb.
Der Abgleich mit den Prinzipien der 12-Factor App zeigt, dass viele der Kriterien in APP2 als Cloud-basierter Applikation bereits von Beginn an berücksichtigt sind, während APP1 die zentralen Gestaltungs-Prinzipien an etlichen Stellen verletzt. Dennoch kann auch der Betrieb im Rechenzentrum von den Prinzipien der 12-Factor App profitieren. Daneben wird deutlich, dass eine Microservice-Architektur – obwohl diese oft in einem Atemzug mit Cloud genannt werden – nicht zwingend notwendig ist, um eine Cloud-native Applikation zu entwickeln.
Fazit
Am Beispiel von zwei fachlich vergleichbaren Applikationen, die auf einem ähnlichen Technologiestack aufsetzen, zeigt sich wie drastisch sich Entwicklung und Betrieb unterscheiden können, wenn eine der Anwendungen im Rechenzentrum läuft, wohingegen die andere auf einer Cloud Plattform deployed wird: Die Unterschiede liegen zum einen in der bereitgestellten Infrastruktur, aber auch im vorherrschenden Mindset einerseits im Rechenzentrum und andererseits in der Cloud.
Adaptiert man eine Cloud-native Sichtweise, wie zum Beispiel in der 12-Factor App vorgeschlagen, entstehen Applikationen, die skalierbar, flexibel und schnell erweiterbar sind. Gleichzeitig wird damit die Fehleranalyse vereinfacht und sichergestellt, dass Probleme durch fehlerhafte Konfigurationen nicht erst produktiv, sondern schon wesentlich früher erkannt werden.
* Dr. Tehseen Rug ist Senior Software Engineer bei der iteratec GmbH.
(ID:47281972)