Container-Referenzarchitektur für Microservices in der Cloud
Cloud-native Architekturen
von Dr. Eldar Sultanow und Daniel Friedmann
Das enorme Interesse an Cloud-nativen Architekturen und damit der zunehmende Einsatz von Containern und Microservices zieht sich wie ein roter Faden durch unterschiedlichste Industrien – von Automotive über Pharma, bis hin zum öffentlichen Sektor. Die Anzahl an Frameworks und Plattformen nimmt zugleich kontinuierlich zu, sie „sprießen geradezu wie Pilze aus dem Boden“. Die Folge: Es wird immer schwieriger, den Überblick zu wahren und einzuordnen, ob Plattformen wie Mesos oder Kubernetes miteinander konkurrieren oder sich doch eher gegenseitig ergänzen. Vor diesem Hintergrund werden die Rufe nach einer containerbasierten Serverless-Referenzarchitektur, die den Einsatz von Microservices und FaaS-Funktionen ermöglicht, zunehmend lauter. Eine solche Architektur stellen wir in diesem Artikel vor und ordnen verschiedene Tools und Frameworks ein.
Nachfolgend stellen wir unsere Referenzarchitektur vor und erläutern die einzelnen Bausteine. Dabei zeigen wir insbesondere auf, wie Microservices mithilfe von Containerplattformen bereitgestellt werden können. Zudem beleuchten wir darauf aufbauende FaaS-Lösungen und deren unterschiedliche Funktionsweisen.
Aufbau der Referenzarchitektur im Überblick
Abbildung 1 zeigt die Referenzarchitektur mit ihren einzelnen Bausteinmengen. Der fließende Übergang verdeutlicht, wie eng Entwicklung und Produktion zusammenwirken. Die oberste Ebene ist die Entwicklungsplattform. Hier finden sich typische Kernelemente einer Entwicklungsphase sowie des Testens. Eine funktionierende Kollaboration unter und innerhalb der Entwicklerteams ist hierbei ein wesentlicher Faktor, Gleiches gilt für das Serverless-Framework zur Entwicklung von Microservices und FaaS-Funktionen.
Das Bindeglied zwischen Entwicklung und Produktion sind DevOps-Praktiken. Neben Möglichkeiten der automatischen Integration und des Bereitstellens neuer Softwareeinheiten findet hier auch das Management des zugrunde liegenden Clusters statt. Die FaaS-Entwicklung reiht sich ebenfalls in dieser Ebene ein. Mithilfe des Serverless-Frameworks werden FaaS-Funktionen zunächst implementiert und anschließend auf einer FaaS-Plattform bereitgestellt.
Die Containerplattform stellt den Hauptteil der Architektur dar und besteht aus einem Management-Interface zur Verwaltung der Plattform, einem mehrfach vorliegenden Master sowie darunter liegenden Cluster-Nodes (CNs). Der Master organisiert die CNs und verteilt Anfragen und Ressourcen auf die jeweiligen Services. Das Zusammenspiel des Masters mit den CNs wird später im Detail erläutert. Die CNs bestehen aus Serverinstanzen, die gemeinsam das Cluster bilden. Auf diesen CNs werden Microservices oder auch FaaS-Funktionen in Form von Containern bereitgestellt. Damit gemeinsame Daten über CNs hinweg verwendet werden können, finden sich in der Containerplattform Lösungen zur gemeinsamen Speicherung.
Innerhalb der gesamten Produktion ist die Sicherheit zentral, da sowohl Plattformen als auch darauf laufende Services abzusichern sind. Über die Programmierschnittstelle des Masters können FaaS-Plattformen auf der bestehenden Container-as-a-Service-Plattform (CaaS-Plattform) aufgesetzt werden und so auf Ressourcen des Masters zurückgreifen. Ein Einsatz externer FaaS-Lösungen ist gleichfalls denkbar. Um im gesamten Cluster einen einfachen gegenseitigen Datenaustausch gewährleisten zu können, wird ein übergeordnetes softwarebasiertes Netzwerk – das sogenannte Overlay-Netzwerk – eingesetzt. Dieses verbindet unterschiedliche physische Netzwerke zu einem großen Netzwerk.
Die Referenzarchitektur baut letztlich auf einer Infrastruktur auf, die entweder eigenständig verwaltet und bereitgestellt wird oder in Form von „Infrastructure as a Service“ (IaaS) vorliegt. Letzteres meint die Bereitstellung von Infrastruktur durch einen externen Cloud-Provider wie Amazon AWS oder Microsoft Azure, der dann auch für die Sicherheit auf Ebene der Infrastruktur zuständig ist.
Wichtige Begriffserläuterungen und -abgrenzungen
Für den weiteren Verlauf des Beitrags ist es zentral, folgende notwendige Begriffe einzuordnen und abzugrenzen:
- Container ≠ Microservice: Ein Container wird definiert als standardisierte Softwareeinheit, die den Code und all dessen Abhängigkeiten bündelt [Doc]. Microservices sind ein architektonischer Ansatz, bei dem Verantwortlichkeiten des Systems in unterschiedliche und einzeln bereitstellbare Services aufgeteilt werden. Die Containerisierung von Microservices hilft, das Ziel einer unabhängigen Bereitstellung zu erreichen. Durch die Cloud sind Microservices nahezu beliebig skalierbar.
- Serverless ≠ FaaS: „Function as a Service“ meint das Bereitstellen von skalierbaren Funktionen in einer Cloud. Serverless hingegen bedeutet, dass man sich nicht selbst um die Verwaltung der Server kümmern muss, da grundlegende Infrastrukturaufgaben auf einen Cloud-Provider übertragen werden.
- PaaS ≠ Referenzarchitektur: Während Platform as a Service (PaaS) eine vom Cloud-Provider bereitgestellte Dienstleistung bezeichnet, ist eine Referenzarchitektur eine Blaupause für die Architektur-Umsetzung in einem bestimmten Anwendungsfeld. Sie fungiert also als Designprinzip, Richtlinie, bewährte Referenz und definiert Bausteine sowie Beziehungen zwischen diesen.
Die Referenzarchitektur im Detail
Im Folgenden werden die einzelnen Bereiche der Referenzarchitektur näher erläutert. Abbildung 2 zeigt die identifizierten Bausteinmengen mit ihren zugehörigen Bausteinen.
Generelle Entwicklung
Im Bereich der generellen Entwicklung befinden sich Frameworks, die die Realisierung der gewünschten Geschäftslogik erleichtern. Hier sind auch die einzusetzenden Sprachen und die zugehörige Laufzeitumgebung zu klären. Ein automatisiertes Management von Abhängigkeiten definiert einen Katalog einsetzbarer Drittlösungen und sorgt für eine einheitliche Verwendung der erprobten und somit freigegebenen Versionen.
Test
Im Bereich Test finden sich Komponenten- sowie Integrationstests, welche im Idealfall automatisiert ablaufen. Bestehende Tests sollten in Form von Regressionstests fortlaufend stattfinden, um die Entstehung bereits behobener Fehler zu vermeiden. Elastizitätstests stellen die Stabilität und Skalierungsmöglichkeiten der Microservices sicher. Eine weitere Testvariante für Microservices und Faas-Funktionen ist das Canary-Testing, das fertige Softwareartefakte direkt in der Produktion testet. Hierbei wird die neue Version parallel zur alten, noch aktiven Version eingesetzt und ein geringer Teil der Benutzer auf die neue Version umgeleitet. Treten über einen definierten Zeitraum keine Komplikationen auf, wird komplett auf die neue Version umgestellt. Die Integration in ein bestehendes System erfolgt somit schrittweise und ohne die Anwendung abzuschalten. Canary-Testing erlaubt Rückschlüsse über die anfängliche Stabilität der Microservices und FaaS-Funktionen und ermöglicht es, die Softwarequalität insgesamt auszuwerten.
Neben dem Testen in Produktion ist auch das Testen der Schnittstellen für verteilte Anwendungen relevant. Mithilfe von Schnittstellenverträgen lassen sich die durch Microservices zu erbringenden Leistungen dokumentieren. In der Testphase können anhand des Vertrags Schnittstellentests auf Seite des Aufrufers und des Microservice implementiert werden.
Kollaboration
Ein passendes Projektvorgehensmodell fördert den Erfolg einer funktionierenden Kollaboration unter den Entwicklerteams. Eine gute Wahl für die Entwicklung von Microservices sind agile Modelle. Sie zeichnen sich in Bezug auf Microservices durch ähnliche Zielsetzungen wie eine schnelle Time-to-Market aus, während traditionelle und relativ starre Modelle langsamer sind, da beispielsweise Releases erst am Ende eines langen Prozesses erfolgen.
Weiterhin ist die gemeinschaftliche Verwaltung der Codebasis relevant: Jedes Teammitglied sollte jederzeit Zugriff auf den gesamten Quellcode besitzen, um stets über eine Gesamtsicht zu verfügen. Sind Teams überlastet oder fällt Personal aus, kann die Arbeit einfach von einem anderen Team angenommen werden. Die gemeinsame Codebasis ist auch hinsichtlich wiederkehrender Codereviews bedeutsam, um gemeinsam eine hohe Softwarequalität durch frühzeitige Fehlererkennung zu erzielen und Folgekosten zu vermeiden.
Darüber hinaus helfen Codereviews, die Wartungsmöglichkeiten des Quellcodes, die Programmierkenntnisse der Entwickler sowie die Sicherheit und die Portabilität der Anwendung zu verbessern [Siy01], indem getroffene Stil- und Dokumentationsempfehlungen umgesetzt werden. Ebenfalls werden Redundanzen minimiert und die Qualität der Dokumentation gesteigert [Siy01]. Onlineplattformen unterstützen die Dokumentationsarbeit und tragen zum nachhaltigen Wissensmanagement bei.
Serverless-Framework
Serverless-Frameworks, wie das aktuelle Quarkus-Framework [Qua] für Java, erleichtern die Entwicklung von Microservices. Für das Entwickeln, Erstellen und Bereitstellen von FaaS-Funktionen kommen zudem Kommandozeilen-Tools der jeweiligen FaaS-Plattformen zum Einsatz.
DevOps
Im Bereich DevOps sind zunächst die Verwaltungsaufgaben des Clusters angesiedelt, wobei das Monitoring einen wichtigen Bestandteil für eine funktionierende Anwendung darstellt. Durch zusätzliche Lösungen wie beispielsweise Prometheus für das Monitoring beziehungsweise Fluentd und OpenTracing für das Logging und Tracing können zu langsame Ausführungszeiten und weitere Flaschenhälse innerhalb der verteilten Anwendung identifiziert werden.
Neben diesen Aufgaben wird eine Pipeline zur kontinuierlichen Integration und Bereitstellung (CI/CD-Pipeline) geschaffen. Am Ende der Pipeline gelangen die fertigen Artefakte – Microservices und FaaS-Funktionen – in ein öffentliches oder privates Image-Register und können darüber jederzeit in Produktion bereitgestellt werden. Es ist sinnvoll, eine solche Pipeline in mehrfacher Ausführung zu besitzen, um einen unabhängigen Release der Services zu garantieren. Im Idealfall wird hier eine Instanz der Pipeline je Release hochgefahren, sodass die Vorteile der modularen Architektur voll ausgeschöpft werden.
Containerplattform
Wie Microservices und FaaS-Funktionen mittels Container über eine Cloud bereitgestellt werden können, zeigt sich im Rahmen der Produktion: Sie werden auf Containerplattformen wie Kubernetes oder Mesos mittels „Container as a Service“ (CaaS) bereitgestellt. Diese Plattformen bieten Lösungen für bekannte Herausforderungen von Microservices wie Orchestrierung, Scheduling oder Service-Discovery.
Der Vorteil: Sie alle werden auf die Ebene der Container verlagert, wodurch die eigentliche Geschäftslogik innerhalb der Microservices nicht mit Operationscode angereichert werden muss. Schlüsselkompetenzen, wie etwa Service-Discovery, finden sich in einem Master, der idealerweise in mehrfacher Ausführung vorhanden ist. Bei einem Ausfall laufen die darunterliegenden CNs eigenständig weiter, bis ein Sekundärmaster einspringt.
Die CNs stellen eine Container-Runtime auf ihrem Hostsystem bereit, mit deren Hilfe ein oder mehrere Container auf dem CN laufen. Kubernetes organisiert diese Container in Form von Pods. Die Container innerhalb der Pods teilen sich ihre Spezifikation und ihre verfügbaren Ressourcen [Lin]. Ein Agent je CN sorgt dafür, dass der Master mit dem jeweiligen CN kommunizieren kann.
Persistierung
Auch in verteilten Systemen müssen Daten persistiert werden. Microservices verfolgen das Prinzip der dezentralen Daten, um ihre Unabhängigkeit zu wahren. Dies wird erreicht, indem beispielsweise eine Datenbank je Service vorhanden ist. Je nach Anzahl der Microservices geht die hohe Unabhängigkeit allerdings mit einem hohen Ressourcenbedarf einher. Ein Datenbankschema je Service oder die Nutzung privater Tabellen reduziert diesen Aufwand [Ric19]. Für nicht zentral vorgehaltene Daten lässt sich je nach Anforderung eine gemeinsame Speicherung mittels Netzwerkfestplatten, Datenbanken, Objektspeicher oder Key-Value-Storages umsetzen.
Infrastruktur
Die darunterliegende Infrastruktur ist in unterschiedlichen Cloud-Bereitstellungsmodellen wie Public, Private oder Hybrid Cloud organisierbar. Die Bereitstellung einer Cloud auf eigener Hardware ist nicht zu empfehlen, da sämtliche ansonsten vom Provider übernommene Aufgaben beim eigenen Unternehmen liegen.
Sicherheit
Da jegliche Kommunikation über das Netzwerk läuft, sind entsprechende Sicherheitsaspekte zu berücksichtigen. Hierzu zählen Faktoren wie Authentifizierung und Autorisierung, Zertifikate für eine sichere Kommunikation sowie das Management von vertraulichen Inhalten, beispielsweise Konfigurationsdaten. Sofern IaaS oder CaaS durch einen Cloud-Provider erfolgen, ist dieser für die Sicherung der Infrastruktur verantwortlich.
Service-Mesh
Da in der Regel mehrere Microservices eingesetzt werden, ist eine funktionierende und robuste Service-zu-Service-Kommunikation notwendig. Ein Service-Mesh hilft hierbei und unterstützt zudem dabei, verteilte Anwendungen zu steuern, zu überwachen und zu sichern. Damit die eigene Geschäftslogik nicht mit Operationscode angereichert werden muss, empfiehlt es sich, Netzwerk-Angelegenheiten an „Beiwagen“ abzugeben. Dieses Entwurfsmuster, besser bekannt unter dem Namen Sidecar-Pattern, versieht jeden Microservice mit einem schlanken Proxy – dem „Beiwagen“ –, der für den ein- und ausgehenden Netzwerkverkehr zuständig ist. Über die uniforme Betriebsschnittstelle, die sich aus dem Einsatz der Proxys ergibt, lassen sich Policies verteilen.
Eine der bekanntesten Implementierungen eines Service-Mesh für Kubernetes ist Istio [Ist]. Neben der Regelung des Netzwerkverkehrs ist es mit Istio auch möglich, für Microservices typische Elastizitätsmuster wie Recovery, Timeouts, Retrys, Circuit-Breaking usw. aufzusetzen. Dabei nutzt Istio intern die von Kubernetes über das Master-API zur Verfügung gestellten Kompetenzen wie Service-Discovery, Config-Map und Healthchecks. Außerdem sorgt Istio für ein automatisiertes Bereitstellen von Sidecar-Proxys und eine automatisierte Absicherung der Service-zu-Service-Verbindungen. Durch die Einführung von Policies kann der Netzwerkverkehr besser kontrolliert werden. Weiterhin ermöglicht Istio ein zentrales Monitoring und Logging der registrierten Services.
Function as a Service (FaaS)
Bekannte externe FaaS-Lösungen sind beispielsweise AWS Lambda und Google Cloud Functions. Allerdings müssen FaaS-Funktionen nicht unbedingt extern betrieben werden. Die Referenzarchitektur liefert neben der Containerplattform für Microservices auch eine Antwort auf den Einsatz von FaaS-Funktionen.
Soll FaaS auf einer bestehenden Containerplattform betrieben werden, so ist es möglich, entweder auf den Ansatz „FaaS per Container-Image“ oder die Variante „FaaS per Code-Injektion“ zurückzugreifen. Wer stattdessen eine von CaaS getrennte FaaS-Plattform nutzen möchte, kann diese entweder eigenständig bereitstellen oder aus einer Vielzahl von externen Cloud-Provider-Lösungen wählen.
Damit bestehende Ressourcen der Containerplattform genutzt werden, ist es sinnvoll, auf diese aufzusetzen. Im Rahmen des Entwurfs der Referenzarchitektur wurden die identifizierten Ansätze „FaaS per Container-Image“ und „FaaS per Code-Injektion“ näher untersucht. Abbildung 3 verdeutlicht die Funktionsweise beider Ansätze.
Bei diesem Ansatz wird der Funktionscode inklusive einer Laufzeitumgebung in Form eines Container-Abbilds bereitgestellt. Das Abbild beziehungsweise Container-Image lässt sich mit dem zugehörigen Kommandozeilen-Tool der FaaS-Plattform bauen und verfügbar machen. Über die Programmierschnittstelle des Masters werden Ressourcen von der Containerplattform allokiert.
Aufgerufene Funktionen werden mittels eines Kaltstarts in einem temporären Container bereitgestellt. Bei Kubernetes geschieht dies durch einen temporären Pod, in dem der Funktionscontainer (FC) vorgehalten wird. Um Folgeanfragen zügig bearbeiten zu können, wird der FC und damit die beinhaltete FaaS-Funktion eine definierte Zeit lang vorgehalten. Der FC kann in diesem Fall auch als Hot-Container bezeichnet werden. Am Ende beauftragt eine Watcher-Komponente das Herunterfahren des FC und gibt damit die zugewiesenen Ressourcen wieder frei.
FaaS per Code-Injektion
Beim Ansatz FaaS per Code-Injektion werden von der FaaS-Plattform Hot-Runtime-Container (HRC) vorgehalten. Diese Container stellen bis zu ihrem Einsatz lediglich eine Laufzeitumgebung wie beispielsweise NodeJS bereit. Über eine Router-Komponente wird ein zentraler Pool-Manager beauftragt, eine Funktion auszuführen. Ein Controller bezieht dafür den zugehörigen Funktions-Quellcode aus einem Speicher und schickt diesen an den Pool-Manager. Dieser veranlasst dann das Injizieren des Quellcodes in einen laufenden HRC. Sobald der Quellcode in diesen geladen wurde, wechselt der Zustand des HRC zu einem aktiven Funktionscontainer, der direkt aufgerufen werden kann, solange er aktiv ist.
Auch hier wird der FC eine definierte Zeit vorgehalten, ehe der Quellcode wieder abgelegt wird und der Container wieder zum Pool der HRC zurückgekehrt. Dieser Ansatz eignet sich vor allem für Skriptsprachen wie JavaScript, da diese Sprachen direkt ausführbar sind und somit der injizierte Code ebenfalls sofort ausführbar ist. Andere Sprachen müssen hingegen erst kompiliert werden.
Einordnung von Plattformen, Frameworks und Tools
Nachdem die Referenzarchitektur vorgestellt wurde, lassen sich die unterschiedlichen Plattformen, Frameworks und Tools kategorisieren und den Bausteinen beziehungsweise einer Bausteinmenge der Referenzarchitektur zuordnen. Tabelle 1 stellt nur einen Auszug an möglichen Frameworks und Tools dar und resultiert größtenteils aus der stetig aktualisierten Landscape der Cloud Native Foundation (CNCF). Diese gibt es zudem in einer empfehlenswerten, interaktiven Version [Land].
Eine häufig gestellte Frage ist, ob Kubernetes auf Apache Mesos bereitgestellt werden kann: Ja, das ist möglich und unter Umständen auch sinnvoll [Rot]. Apache Mesos besitzt zwar ein eigenes Containermanagement, doch wer Kubernetes einsetzen möchte oder muss, kann dies problemlos tun. Während Kubernetes nur Container bereitstellt, ist das Einbinden anderer Workloads – etwa Legacy-Anwendungen – unter Mesos kein Problem. Aus diesem Grund betrachten wir Mesos, beziehungsweise die kommerzielle Variante Mesosphere DC/OS, als ein Cloud OS, während Kubernetes eine ausschließliche, aber mächtige Containerplattform darstellt.
Praxiseinsatz: Der öffentliche Sektor
Im Zuge eines laufenden Verfahrens im öffentlichen Sektor wurden Teile der Referenzarchitektur bereits umgesetzt. Ziel war es dabei, eine bestehende Anwendung zur Visualisierung von Infrastrukturdaten zunächst zu containerisieren und dann auf Kubernetes bereitzustellen. Die Funktionalität wurde exemplarisch um einen Microservice sowie eine FaaS-Funktion erweitert. Der eingesetzte Framework-Stack bestand hierbei neben Kubernetes aus OpenFaaS als aufbauende FaaS-Plattform und Quarkus sowie Helidon MP als Serverless-Frameworks. Die Referenzarchitektur ebnet den Weg für weitere umfassendere Serverless-Projekte.
Fazit
Cloud-Technologien und Cloud-native Lösungen verzeichnen momentan ein enormes Interesse. So erschien erst kürzlich mit Micronaut Predator ein neues Open-Source-Projekt der Micronaut-Entwickler, das beim Einsatz von Java einen schnelleren Datenzugriff für Microservices verspricht. Stetig hinzukommende Frameworks und damit verbundene Begriffe führen zu einer zunehmenden Unübersichtlichkeit im Cloud-Umfeld.
Aus Sicht der Autoren ist es daher notwendig gewesen, dem Begriffschaos entgegenzuwirken und eine klare Struktur in Form einer Referenzarchitektur zu schaffen, die das Entwerfen Cloud-nativer Anwendungen sowie die Entwicklung von Microservices und FaaS-Funktionen unterstützt. Die vorgestellte containerbasierte Serverless-Referenzarchitektur zeigt, dass CaaS oder FaaS keine Entweder-oder-Entscheidung sein muss: Microservices lassen sich für dauerhafte, elementare Bestandteile einer verteilten Anwendung nutzen, während FaaS-Funktionen den Funktionsumfang dynamisch erweitern können. Durch die Entscheidung für CaaS als Hauptteil der Architektur ist nicht zuletzt auch der Cloud-Provider weitgehend frei wählbar. Die hier vorgestellte Serverless-Referenzarchitektur kommt für verschiedene Einsatzszenarios infrage, etwa für die Auslastungsanalyse von Rechenzentren. Für diesen konkreten Anwendungsfall wird sie aktuell bereits eingesetzt. Im nächsten Schritt gilt es daher, den Praxiseinsatz näher zu betrachten und hieran aufzuzeigen, warum eine Serverless-Referenzarchitektur insbesondere für dieses und ähnliche Szenarios geeignet ist.
Ergänzende Anmerkung: Bei der Erstellung des Artikels sind Ergebnisse der Bachelorarbeit „Entwerfen einer Referenzarchitektur für die Entwicklung und den Betrieb von Microservices“ des Autors Daniel Friedmann eingegangen.
Literatur & Links
[Doc] What is a Container?, Docker Inc., siehe: https://www.docker.com/resources/what-container
[Ist] Istio, siehe: https://istio.io/
[Land] CNCF Cloud Native Interactive Landscape, siehe: https://landscape.cncf.io
[Lin] Pods, Linux Foundation, siehe: https://kubernetes.io/docs/concepts/workloads/pods/pod/
[Qua] Quarkus, Supersonic Subatomic Java, siehe: https://quarkus.io/
[Ric19] C. Richardson, Pattern: Database per service, 2019, siehe: https://microservices.io/patterns/data/database-per-service.html
[Rot] D. Rothman, [Infographic] The Docker vs. Kubernetes vs. Apache Mesos Myth: Why What You Think You Know is Wrong, 2018, siehe: https://mesosphere.com/blog/the-docker-vs-kubernetes-vs-apache-mesos-myth/
[Siy01] H. Siy, L. Votta, Does The Modern Code Inspection Have Value?, in: Proc. of the IEEE Int. Conf. on Software Maintenance (ICSM’01), 2001