Verteilte Anwendungen mit Cloud-nativen Technologien
Softwareentwicklung gewinnt durch Microservices und Container neue Freiheiten: autonome Teams, polyglotte Sprachen und Frameworks sowie mehr Resilienz.
- Matthias Haeussler
- Verteilte Anwendungen ermöglichen heterogene Umgebungen mit verschiedenen Systemen und Architekturen. Vorteile sind Plattformunabhängigkeit, Verfügbarkeit und Skalierbarkeit.
- Der Artikel zeigt die verschiedenen Möglichkeiten der Konfiguration, Architektur und modularen Gestaltung mit verschiedenen Techniken und Frameworks. Im Zentrum steht dabei Kubernetes.
- Über Service Meshs mit oder ohne Sidecar-Modell lässt sich die Kommunikation im modularen Netz überwachen, analysieren und steuern.
- Die von Adam Wiggins postulierten zwölf Grundsätze für gute Cloud-Apps gelten in hohem Maße auch für verteilte Anwendungen.
Verteilte Anwendungen und verteilte Systeme im weiteren Sinne erfreuen sich wachsender Beliebtheit, hauptsächlich dank der Entwicklungen im Bereich von Microservices und Container-Technologie. Der damit einhergehende technische Fortschritt ermöglicht unabhängiges Entwickeln in autonomen Teams, die Freiheit, Sprachen und Frameworks auszuwählen, sowie die Verbesserung der Resilienz durch Skalierung und Lastverteilung.
Dieser Artikel untersucht verschiedene Ansätze zur Implementierung verteilter Anwendungsarchitekturen mithilfe moderner, Cloud-nativer Softwaretechnologien. Dazu zählen einerseits an Programmiersprachen gebundene Frameworks, die insbesondere im Java-Umfeld sehr verbreitet sind. Andererseits gibt es Plattformen wie Kubernetes und Service-Meshes – sowohl im traditionellen Stil als auch neue Varianten ohne Sidecar.
Zunächst gilt es, die Frage zu klären, warum eine verteilte Anwendungsarchitektur überhaupt sinnvoll ist und welche Komponenten für eine erfolgreiche Umsetzung notwendig sind. Zur Orientierung dienen dabei auch die von Adam Wiggins bereits 2011 veröffentlichten Prinzipien der 12-Faktor-App (siehe Kasten "Konzept der 12-Faktor-App").
Im Jahr 2011 veröffentlichte Adam Wiggins das Konzept der 12-Faktor-App. Der Mitgründer des PaaS-Anbieters Heroku beschrieb damit eine bewährte Vorgehensweise (Best Practices) für die Entwicklung von Anwendungen, um sie effizient in der Cloud zu betreiben und deren Möglichkeiten optimal zu nutzen. Die 12-Faktor-App definiert grundlegende Prinzipien, die eine Anwendung plattformunabhängig, skalierbar und granular konfigurierbar machen sollen. Zudem enthalten die Prinzipien nicht nur Cloud-spezifische Aspekte, sondern auch allgemeine Grundsätze guter Softwareentwicklung, etwa im Hinblick auf das Verwenden eines zentralen Versionskontrollsystems pro Komponente und eine saubere Trennung von Code und Abhängigkeiten. Indem Entwickler die zwölf Prinzipien befolgen, stellen sie sicher, dass ihre Anwendungen gut auf die Cloud-Infrastruktur abgestimmt sind und von den Vorteilen der Plattform profitieren.
Besonders hervorzuheben sind folgende Faktoren:
- Faktor 3: "Config" (Trennung von Konfiguration und Code): Die Trennung von Konfiguration und Code ermöglicht einerseits eine flexible Anpassung der Anwendung ohne Neukompilierung und andererseits einen Einsatz in verschiedenen Umgebungen mit unterschiedlichen Konfigurationen.
- Faktor 6: "Processes" (Zustandslosigkeit und Skalierbarkeit): Zustandslose Prozesse erleichtern das Skalieren und Verwalten der Anwendung, da die Prozesse keine Informationen ĂĽber ihren vorherigen Zustand speichern mĂĽssen.
- Faktor 7: "Port binding" (Bindung an Ports und Netzwerkkommunikation): Das Verwenden standardisierter Netzwerkprotokolle vereinfacht die Interaktion zwischen den Komponenten und ermöglicht eine einfache Integration in verschiedene Umgebungen.
- Faktor 11: "Logs" (Logs als Streams): Die Behandlung von Logs als Streams ermöglicht eine effizientere Fehleranalyse und -behebung bei der Verwaltung verteilter Anwendungen. Die Prinzipien der 12-Faktor-App kommen jenseits von Heroku in Systemen wie Cloud Foundry, Spring Boot, Docker und Kubernetes zum Einsatz, um moderne Anwendungen erfolgreich in einer dynamischen und agilen Umgebung zu betreiben.
Warum verteilte Systeme?
Der traditionelle monolithische Ansatz in der Softwarearchitektur gilt vielen als nicht mehr zeitgemäß. Insbesondere Anhänger verteilter Systeme und Microservice-Architekturen sprechen häufig abfällig von "Big Ball of Mud". Doch auch die als modern geltenden verteilten Ansätze sind nicht frei von Problemen, wie Peter Deutsch bereits 1994 in seinen "Fallacies of distributed computing" (Irrtümer der verteilten Datenverarbeitung) zusammengefasst hatte, die bis heute ihre Gültigkeit nicht verloren haben.
Insbesondere führt das Aufteilen einer Anwendung in verschiedene Module zu einer Netzwerkabhängigkeit zwischen den Komponenten, die wiederum Einfluss auf Latenzzeiten, Konfiguration und Fehlerbehandlung hat. Dennoch ist es in bestimmten Szenarien sinnvoll – mitunter sogar unausweichlich.
Im Folgenden geht es darum, die Vorteile und die damit verbundenen Aspekte näher zu beleuchten. Das grundlegende Ziel einer verteilten Anwendung sollte es sein, sowohl Nutzerinnen und Nutzern als auch den Entwicklungsteams Vorteile zu liefern. Die liegen hierbei vor allem in nicht-funktionalen Anforderungen wie Verfügbarkeit, Ausfallsicherheit und Skalierbarkeit.
Ein solches System sollte sich für Nutzer wie eine Einheit anfühlen – ganz im Sinne von Andrew Tanenbaums in seinem Buch "Verteilte Systeme" formulierter Forderung. Wer Google Maps benutzt, den interessiert nicht, wie viele Container dahinterstehen oder welche Programmiersprachen zum Einsatz kommen, es zählen allein Verlässlichkeit und Funktion.
Faktor Heterogenität
In der Theorie verteilter Systeme spielt Heterogenität eine zentrale Rolle, etwa im Hinblick auf Parallelität und Nebenläufigkeit. Durch parallele Verarbeitung heterogener Tasks lässt sich, wie Abbildung 1 zeigt, eine höhere Effizienz erzielen.
Heterogenität spiegelt sich darüber hinaus auch in Abhängigkeiten von Betriebssystemen, Runtimes, Frameworks etc. wider (siehe Abbildung 2). In all diesen Fällen ist es nicht möglich, eine Anwendung in ein monolithisches Artefakt zu bringen, was einen verteilten Ansatz unabdingbar macht.
Abschließend sei noch die Erweiterbarkeit erwähnt. Eine verteilte Architektur bietet den Vorteil, dass sich neue Komponenten als eigenständige Module in ein bestehendes System integrieren lassen, ohne dass damit nennenswerte Auswirkungen auf die bestehenden Module einhergehen. Es ist also kein erneutes Kompilieren oder Paketieren der Komponenten erforderlich.
Faktor Resilienz
Beim Faktor Resilienz geht es hauptsächlich darum, die Anwendung hochverfügbar zu machen und sie gegen unvorhersehbare Ereignisse, wie Schwankungen in der Benutzerzahl oder Ausfälle von Teilsystemen beziehungsweise Netzwerksegmenten, widerstandsfähig zu halten. Der Ausfall einer solchen Komponente sollte kontrollierbar sein und keinesfalls zu einem Ausfall der Gesamtanwendung führen. Das Skalieren individueller Komponenten ermöglicht hierbei Ausfallsicherheit durch Redundanz. Beim Ausfall einer Instanz sollten also noch ausreichend andere Instanzen vorhanden sein, sodass der Service ohne Unterbrechung fortfährt (siehe Abbildung 3). Das dient nicht nur der Redundanz, sondern auch der Lastverteilung, um beispielsweise im Falle steigender Nutzerzahlen die Last gleichmäßig auf die einzelnen Komponenten zu verteilen und so die gewünschte Leistung des Gesamtsystems sicherzustellen (siehe Abbildung 4).
Bei einem plötzlichen Anstieg der Nutzerzahlen oder – schlimmer – einer Denial-of-Service-Attacke kann die Last rasch so stark zunehmen, dass sie sich auch durch Skalierung nicht mehr ausgleichen lässt. Um die Anwendung davor zu schützen, lässt sich eine Netzwerkkomponente platzieren, die den eingehenden Verkehr blockiert oder zumindest drosselt. Zum Einsatz kommen in solchen Fällen meist sogenannte Circuit Breaker oder Bulkheads.
Ein weiterer Aspekt von Resilienz ist die unterbrechungsfreie Verfügbarkeit während eines Updates der Anwendung. Um das sicherzustellen, stehen verschiedene Deployment- und Zero-Downtime-Techniken zur Verfügung – darunter Blue/Green Deployments und Canary Releases.
Beide Varianten funktionieren grundsätzlich nach dem Prinzip, dass eine neue Version der Anwendung deployt wird, während die alte noch läuft. Während bei Blue/Green Deployments der Wechsel in einem einzigen Schritt erfolgt, führt ein Canary Deployment die neue Version schrittweise und selektiv zunächst für eine begrenzte Gruppe von Nutzern ein, bevor das neue Release vollständig in den Produktionsbetrieb übergeht.