Cloud-native vs. Rechenzentrum
Im ultimativen Infrastrukturvergleich treten zwei unterschiedliche Plattformansätze einer Anwendungsfamilie gegeneinander an. Anhand der Prinzipien der 12-Factor App vergleichen wir eine cloud-native mit einer Rechenzentrum-basierten Infrastruktur:
-
Teil 2: Factor III: Config - Store config in the environment
-
Teil 3: Factor IV: Backing Services - Treat backing services as attached resources
- Teil 4: Factor VII: Port Binding – Export services via port binding
In dieser Runde erfahren Sie, wie Self-Containment fehlerfreie Deployments unterstützt.
Inhalt
Aussage des Faktors
Eine 12-Factor App ist unabhängig von einem externen Container wie zum Beispiel einem Webserver, in dem sie ausgeführt wird [12F4]. Stattdessen wird die Anwendung als Dienst betrachtet, der eigenständig ausgeführt wird und über eine klar definierte Schnittstelle von außen erreichbar ist. Die Applikation ist also an einen Port gebunden auf den einkommende Requests geroutet werden. In der Java Welt, insbesondere bei der Verwendung von Frameworks wie Spring Boot, kann dies sehr leicht erreicht werden. Mithilfe des integrierten Tomcats können wir Spring Boot Apps als einfache jars ausliefern. Dies bietet einen großen Vorteil gegenüber traditionellen Deployments von Webapplikationen. Hier muss pro Server eine eigene Tomcat-Instanz installiert, konfiguriert und gewartet werden. Dies führt potenziell zu Fehlern aufgrund von unterschiedlichen Versionen und/oder Konfigurationen auf verschiedenen Stages oder gar innerhalb einer Stage, wenn die Anwendung auf mehreren Servern läuft. Außerdem sind wir bei der Entwicklung an die Erzeugung einer .war-Datei mit entsprechender Verzeichnisstruktur gebunden.
Die standalone Varianten a la Spring Boot umgehen diese Probleme. Insbesondere können wir sicher sein, dass ein einmal gebautes Artefakt immer gleich ausgeführt wird, egal auf welcher Stage. Dies ist vor allem in der Cloud relevant, da hier bei Bedarf dynamisch Stages und Instanzen erzeugt werden können. Skalieren wir eine Anwendung horizontal, indem wir die Anzahl der Instanzen erhöhen, so wird beim Hochfahren der zusätzlichen virtuellen Maschinen oder Docker Container, die ausführbare Datei bereitgestellt und gestartet. Dies wird bei Plattformen wie Cloud Foundry durch die zugrundeliegende Cloud Architektur gewährleistet. Mehr Details hierzu findet der Leser in der offiziellen Dokumentation [CF5].
Tomcat Instanzen im Rechenzentrum
APP1 wird im Rechenzentrum auf jeweils zwei Test-, QA- und Produktionsservern betrieben. Auf diesen sind jeweils Tomcat Instanzen installiert. Der Betrieb und die Prozesse im Rechenzentrum erwarten entsprechend, dass eine .war-Datei deployed wird. Obwohl es sich bei APP1 um eine Spring Boot Anwendung handelt, die technisch als .jar-Datei ausgeliefert und eigenständig ausgeführt werden könnte, ist dies im Rechenzentrum so also nicht vorgesehen. In diesem Kontext ist es wichtig anzumerken, dass die Anwendung, auch wenn das Frontend mithilfe von Angular umgesetzt ist, aus Perspektive der Auslieferung tatsächlich als pure Spring Boot Applikation anzusehen ist, da Frontend und Backend gemeinsam als .war ausgeliefert werden. Die relevanten Javascript, HTML und CSS Files werden dazu im Build Prozess als Ressourcen für die Spring Boot Applikation bereitgestellt.
Auslieferung als .jar in der Cloud
Cloud Foundry unterstützt die Umsetzung der 12-Factor App und damit auch die Auslieferung als .jar-Datei. Auch im Rahmen von APP2 wird momentan Frontend und Backend gemeinsam ausgeliefert. Wie bereits im Artikel zum Faktor Backing Services beschrieben, kann dies als Verletzung des entsprechenden Prinzips interpretiert werden. Für die Zwecke der gegenwärtigen Diskussion ist es aber erstmal nur wichtig, dass das Projekt eigenständig, also unabhängig von einem externen Container wie Tomcat ausgeführt werden kann.
Cloud Foundry erlaubt es uns die Applikation mittels
cf push <APP_NAME> -b <BUILDPACK_NAME> -p <PFAD_ZU_JAR>.jar
zu deployen. Dazu ist es natürlich erforderlich, dass wir zuvor das Artefakt als .jar gebaut haben. Dies kann lokal oder auf einem Build Server erfolgen, wobei letzterer im produktiven Betrieb zu bevorzugen ist.
Das buildpack zur Ausführung des Artefakts 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. Alternativ zur Spezifikation von Parametern innerhalb des cf push Kommandos ist es auch möglich und empfohlen stattdessen eine manifest.yml Datei anzulegen und dort Cloud Foundry die für das Deployment relevante Information mitzuteilen. Eine solche Datei stellt Infrastruktur als Code zur Verfügung und ermöglicht uns somit eine Versionierung der bereitgestellten Ressourcen mithilfe von git. Ein Auszug aus einem von uns beim Deployment von APP2 verwendeten Manifest sieht zum Beispiel folgendermaßen aus (für die Diskussion wurden irrelevante Details ausgelassen):
applications:
- name: <APP_NAME>
path: <PFAD_ZU_JAR>.jar
buildpacks:
- java_buildpack
memory: 3G
instances: 4
Wir sehen hier, dass neben den oben bereits erwähnten Parametern weitere Informationen wie Speicher und Anzahl an Instanzen definiert werden (ohne explizite Angabe dieser Optionen werden defaults von Cloud Foundry verwendet). Das Manifest gibt uns also die Möglichkeit schnell und unkompliziert vertikale und horizontale Skalierung der Anwendung anzupassen, sollte dies vonnöten sein. Führen wir cf push ohne weitere Parameter aus, so wird das Manifest herangezogen (vorausgesetzt es folgt der Namenskonvention manifest.yml), um die entsprechenden Werte zu setzen. Außerdem wird anhand des Namens der App eine Route erzeugt, unter der die Anwendung erreichbar ist. Soll diese hingegen explizit gesetzt werden, so ist dies mit
cf create-route <ROUTE_NAME> --hostname <APP_NAME>
möglich. Es ist noch wichtig zu erwähnen, dass beim Betrieb auf mehreren Stages auch unterschiedliche Manifeste angelegt werden sollten, da es sich aus Sicht von Cloud Foundry um verschiedene Anwendungen mit entsprechenden Ressourcen handelt. Diese können in verschiedenen Ordnern abgelegt werden und/oder durch Wahl des Dateinamens unterschieden werden. Dies führt zu größerer Übersicht und Klarheit bei der Verwaltung der Spaces. In diesem Fall muss dem cf push Befehl als Argument mitgeteilt werden, welches Manifest für das Deployment verwendet werden soll:
cf push -f <FILENAME>
Zusammenfassend wollen wir festhalten und betonen, wie simpel das Deployment einer Webanwendung in Cloud Foundry ist. Die Plattform kümmert sich um das Management und die Konfiguration der Infrastruktur. Die Aufgabe der Entwickler besteht darin, eine ausführbare Datei bereitzustellen. Dies ist zu vergleichen mit dem analogen Vorgehen im Rechenzentrum. Hier muss die Infrastruktur explizit bereitgestellt und gewartet werden. Dies führt oft zur Trennung von Entwicklung und Betrieb. Während diese Aufteilung aus Sicht der Überschaubarkeit individueller Aufgaben nachvollziehbar ist, bringt sie jedoch Probleme mit sich. Diese bestehen insbesondere darin, dass simple Anpassungen der Infrastruktur nicht durch das Team vorgenommen werden können. Stattdessen ist der Umweg über den Betrieb notwendig.
Fazit
Die Kombination Cloud Foundry und Spring Boot macht es uns leicht, Dienste bereitzustellen, die ohne externe Abhängigkeiten betrieben werden. Aber auch andere Cloud Plattformen und Technologien ermöglichen es uns heutzutage Anwendungen zu deployen, die unabhängig von externen Webcontainern ausgeführt werden können. Der Mehrwert eines solchen Betriebs besteht insbesondere darin, dass kein Management des externen Containers auf verschiedenen Instanzen und Stages mehr nötig ist. Als Konsequenz davon sind wir unabhängig von der konkreten Infrastruktur, auf der die Anwendung ausgeführt wird. Dies hat zur Folge, dass unsere Applikationen wesentlich portabler sind, als es auf klassischer Server-Infrastruktur der Fall ist. Wir gewinnen also Unabhängigkeit und Flexibilität, die als Nebenprodukt zur Folge hat, dass ein einmal gebautes Artefakt auf jeder Stage gleich ausgeführt wird. Im Gegensatz dazu besteht beim traditionellen Betrieb einer Webapplikation in einem externen Container die Gefahr, dass Versions- und/oder Konfigurationsunterschiede zu Diskrepanzen der Stages oder gar der Server einer gegebenen Stage führen. Im schlimmsten Fall kann dies dazu führen, dass die Anwendung auf vorgelagerten Stages wie Test oder QA fehlerfrei läuft und erst beim Deployment auf Produktion, wenn es schon zu spät ist, Fehler auftreten. Die technologischen Fortschritte von Frameworks wir Spring Boot erlauben es uns, grundsätzlich auch im Rechenzentrum von diesen Vorteilen zu profitieren. Der klassische Betrieb erwartet aber oft, so auch im Falle von APP1, dass die Anwendung als .war-Datei ausgeliefert und in einem externen Webserver ausgeführt wird. Aus klassischer Sicht macht das durchaus Sinn, wenn man bedenkt, dass häufig eine Vielzahl verschiedener Apps in einem Rechenzentrum betrieben werden. Um Konsistenz und Übersicht im Betrieb zu gewährleisten ist es durchaus nachvollziehbar auf eine Form der Standardisierung wie die Verwendung von Tomcat und .war-Dateien zu setzen. Allerdings wird durch unsere Diskussion ein anderes Problem offenbart. Die Problematik ergibt sich als direkte Konsequenz der Trennung von Entwicklung und Betrieb, wie es bei klassischen Applikationen bzw. im Rechenzentrum häufig der Fall ist. Würde hier ein Mindset adaptiert werden, dass die Konzepte DevOps und Selbstverantwortlichkeit stärker im Fokus hat, so könnten wir auch im Rechenzentrum flexiblere Deployments ermöglichen, die all die oben angesprochenen Vorteile mit sich bringen. Die Cloud, insbesondere Cloud Foundry setzt dieses Mindset voraus und stellt somit sicher, dass Artefakte schneller, flexibler und mit weniger Fehlern deployed werden, vorausgesetzt wir halten uns an die Spielregeln und Best Practices in der Cloud.
Noch Fragen?
Weiter Informationen und Hilfestellungen rund um die Gestaltung und Weiterentwicklung hochperformanter IT-Infrastrukturen und Applikationslandschaften finden Sie unter:
Referenzen:
[12F4]: THE TWELVE-FACTOR-APP - VII. Port binding, siehe: https://12factor.net/port-binding
[CF5]: Runtime Components, siehe: https://docs.cloudfoundry.org/concepts/architecture/