Qgelm

Zwölf Regeln für Web- und Cloud-Anwendungen

Originalartikel

Backup

<html> <time datetime=„2020-11-17T13:51:00+01:00“>17.11.2020 13:51</time>Golo Roden<p><strong class=„manuell_vorspann“>Das Entwickeln skalierbarer und verl&#228;sslicher Anwendungen f&#252;r Web und Cloud ist ein komplexes Thema, das eine gewisse Erfahrung fordert. Dennoch gibt es Leitplanken f&#252;r den einfachen Einstieg, allen voran die Regeln der 12-Factor-Apps. Was hat es mit damit auf sich?</strong></p><p>F&#252;r die Entwicklung von Web- und Cloud-Anwendungen gibt es verschiedene Architekturtypen. Doch je gr&#246;&#223;er und komplexer eine Anwendung wird, desto eher werden Aspekte wie Skalierbarkeit, Hochverf&#252;gbarkeit und Ausfallsicherheit relevant. Diese Anforderungen lassen sich allerdings nicht mit einer monolithischen Architektur umsetzen, weshalb man bei solchen Anwendungen h&#228;ufig auf ein Netz aus verteilten Diensten st&#246;&#223;t, die einander erg&#228;nzen.</p><p>Damit ein derartiges Netz funktionieren kann, sind an die einzelnen Dienste gewisse Anforderungen zu stellen. Beispielsweise ist f&#252;r eine elastische Skalierbarkeit unabdingbar, dass sich Dienste je nach Bedarf starten und auch beenden lassen, ohne daf&#252;r in gr&#246;&#223;erem Rahmen Zeit einplanen zu m&#252;ssen. Das wiederum bedingt gewisse Herangehensweisen an die Entwicklung solcher Dienste.</p><p>Da verteilte Architektur an sich kein einfaches Thema ist, f&#228;llt auch der Einstieg h&#228;ufig schwer. Um diesem Problem entgegenzuwirken und einen einfachen Einstieg in die Thematik zu erm&#246;glichen, hat der Cloud-Plattform-Anbieter Heroku schon vor etlichen Jahren zw&#246;lf Regeln verfasst, die als Leitplanken f&#252;r den Entwurf und die Entwicklung von Diensten gelten k&#246;nnen. Selbstverst&#228;ndlich umfasst verteilte Architektur weitaus mehr als diese zw&#246;lf Regeln, aber sie erm&#246;glichen einen strukturierten erstem Kontakt mit dem Thema. <a href=„https://12factor.net/“ rel=„external“ target=„_blank“>Gef&#252;hrt werden diese Regeln unter dem Begriff der 12-Factor-Apps</a>.</p><p>Im folgenden werden diese Regeln vorgestellt, allerdings nicht in ihrer eigentlichen Reihenfolge, sondern thematisch gruppiert. Einige der Regeln liegen inhaltlich n&#228;mlich n&#228;her beieinander als andere, weshalb es sinnvoll ist, sie als einen gemeinsamen Block zu behandeln.</p><p>Die erste Regel besagt, dass es f&#252;r eine Anwendung genau eine Codebase geben sollte. Das bedeutet, dass der Code f&#252;r eine Anwendung in einem einzigen Repository und nicht &#252;ber zahlreiche Repositories verstreut vorliegen sollte. Das mag trivial und offensichtlich erscheinen, geh&#246;rt aber tats&#228;chlich nicht in allen Projekten zum Alltag.</p><p>Selbstverst&#228;ndlich verbietet das nicht, in mehreren Anwendungen gleichsam genutzten Code in ein gemeinsames Modul zu extrahieren und dieses in ein eigenes Repository auszulagern. Vielmehr ist damit gemeint, dass jener Code, der tats&#228;chlich anwendungsspezifisch ist, an einem einzigen Ort abgelegt sein sollte, der nur f&#252;r diese Anwendung gedacht ist.</p><p>Aus dieser Codebase repr&#228;sentiert jeder einzelne Commit praktisch eine eigenst&#228;ndige Version, die deployt werden k&#246;nnte. Das mag nicht f&#252;r jeden Commit sinnvoll sein, aber jede &#196;nderung am Code l&#228;sst sich einzeln adressieren und k&#246;nnte als Grundlage f&#252;r ein Deployment dienen. Das bedeutet, dass es von einer Codebase unterschiedliche Versionen und von jeder dieser Versionen wiederum auch mehrere Deployments geben kann.</p><p>Die zweite Regel besagt, dass man Codefragmente, die in verschiedenen Anwendungen verwendet werden oder die anwendungsunabh&#228;ngig und damit generisch sind, in eigene Module extrahieren sollte, die in jeweils einem eigenen Repository verwaltet werden. Diese Module k&#246;nnen dann als Abh&#228;ngigkeiten in die Anwendungen eingebracht werden, sollten dort aber explizit deklariert werden.</p><p>Das bedeutet, dass man nicht davon ausgehen sollte, dass eine bestimmte Abh&#228;ngigkeit in einer bestimmten Version systemweit vorhanden ist, sondern die ben&#246;tigten Abh&#228;ngigkeiten explizit in den Kontext der Anwendung installieren muss. Dazu ben&#246;tigt man im Idealfall eine Paketverwaltung, die das Verwalten von Abh&#228;ngigkeiten und deren Versionen vereinfacht. Welche Paketverwaltung das konkret ist, h&#228;ngt von der verwendeten Technologie ab &#8211; f&#252;r Node.js w&#228;re das beispielsweise npm in Verbindung mit der Datei <em>package.json</em>.</p><p>Eine Anwendung, die sich an die Regeln der 12-Factor-Apps h&#228;lt, sollte also ihren Code und den Code aller Abh&#228;ngigkeiten in einem Verzeichnis und dessen Unterverzeichnissen verwalten, aber nicht von Dateien oder Verzeichnissen au&#223;erhalb dieses Anwendungsverzeichnisses abh&#228;ngen.</p><figure class=„video akwa-inline-video video–fullwidth“><figcaption class=„bildunterschrift akwa-caption“>Die Codebase als Single-Source of Truth</figcaption></figure><p>Die sechste Regel besagt, dass Anwendungen aus einem oder mehreren Prozessen bestehen sollen. Das hei&#223;t, dass, obwohl es nur eine Codebase geben darf, damit nicht zwingend gemeint ist, dass auch s&#228;mtlicher Code in einem einzigen Prozess ausgef&#252;hrt wird. Das w&#228;re im Hinblick auf die eingangs erw&#228;hnte verteilte Architektur, die viele verschiedene Dienste zu einem gro&#223;en Ganzen kombiniert, auch nicht sinnvoll.</p><p>Eine Anwendung in Prozesse zu zerlegen, bietet auch den Vorteil, dass diese individuell und unabh&#228;ngig voneinander deployt, gestartet und aktualisiert werden k&#246;nnen. Auch ein Ausfall eines Dienstes bedeutet nicht notwendigerweise den Ausfall der gesamten Anwendung: Im Idealfall l&#228;uft die Anwendung anstandslos weiter, zwar mit gegebenenfalls geringerer Performance oder mit dem Ausfall einiger Features, aber eben ohne vollst&#228;ndig auszufallen.</p><p>Die achte Regel erg&#228;nzt diese Denkweise, indem sie vorgibt, dass man &#252;ber Prozesse skalieren soll. Die Idee dahinter ist, dass man &#8211; sp&#228;testens wenn man eine Anwendung auf mehrere Server verteilt &#8211; ohnehin &#252;ber Prozessgrenzen hinweg skalieren muss, weshalb man dieses Vorgehen von vornherein einplanen sollte.</p><p>Das bedeutet auch, sich von Anfang an Gedanken &#252;ber die Synchronisation von Prozessen und dementsprechend ebenfalls &#252;ber Interprozesskommunikation (IPC) zu machen. Das Praktische daran ist, dass diese Mechanismen auch auf einem einzigen Server funktionieren, wenn man lediglich mehrere Instanzen der Anwendung auf einer Maschine betreiben will. Damit unterscheidet sich das Modell von der Skalierbarkeit mit Threads, und das Skalierungsmodell bleibt so oder so das gleiche.</p><p>Die neunte Regel besagt, dass es essenziell ist, Prozesse z&#252;gig starten und stoppen zu k&#246;nnen. Das zielt auf den eingangs bereits erw&#228;hnten Punkt ab, elastisch skalieren zu k&#246;nnen: In dem Moment, zu dem mehr Performance ben&#246;tigt wird, sollte es ein Leichtes sein, zus&#228;tzliche Instanzen zu starten. Es liegt auf der Hand, dass das dann keinen umfangreichen, aufwendigen und zeitintensiven Vorgang nach sich ziehen darf &#8211; ansonsten w&#252;rde der gew&#252;nschte Effekt n&#228;mlich verpuffen.</p><p>&#196;hnliches gilt f&#252;r das Beenden eines Prozesses: Auch hier sollte ein Dienst so gebaut sein, dass es problemlos m&#246;glich ist, ihn im laufenden Betrieb abzuschie&#223;en, ohne dass zun&#228;chst noch Aufr&#228;umarbeiten erforderlich w&#228;ren. Auf dem Weg wird es m&#246;glich, nicht mehr ben&#246;tigte Ressourcen z&#252;gig auch wieder freizugeben. Wird diese Regel beachtet, ist es auch weitaus einfacher, Dienste in Docker beziehungsweise in der Cloud zu betreiben.</p><p>Die zw&#246;lfte Regel besagt, dass administrative Aufgaben ebenfalls als Prozesse implementiert werden sollten, die auch Bestandteil der einen Codebase einer Anwendung sein d&#252;rfen. Auf dem Weg kann man einer Anwendung zum Beispiel Kommandozeilenwerkzeuge beilegen, die helfen, die Anwendung zu verwalten und zu steuern.</p><figure class=„video akwa-inline-video video–fullwidth“><figcaption class=„bildunterschrift akwa-caption“>Warum Prozesse so wichtig sind</figcaption></figure><p>Die siebte Regel besagt, dass eine Anwendung ihre eigene Funktionalit&#228;t &#252;ber Ports zur Verf&#252;gung stellen sollte &#8211; dass es also eine im Netzwerk zur Verf&#252;gung gestellte API geben sollte, auf die andere Anwendungen von au&#223;en zugreifen k&#246;nnen. Der Grund f&#252;r diesen Ansatz ist, dass er unabh&#228;ngig von Server- und Plattformgrenzen funktioniert, da sich auf Ports nicht nur lokal, sondern eben auch remote zugreifen l&#228;sst. Das wiederum ist die Grundlage f&#252;r Skalierbarkeit.</p><p>Die vierte Regel kehrt diesen Ansatz um und besagt, dass externe Dienste ebenfalls &#252;ber Ports angebunden werden sollten. Auf dem Weg besteht kein Unterschied zwischen dem Bereitstellen und dem Konsumieren von Funktionalit&#228;t, die Kommunikation erfolgt stets &#252;ber Ports. Das gilt aber nicht nur f&#252;r fachliche Dienste, sondern auch f&#252;r Infrastrukturkomponenten wie Datenbanken oder Message-Queues.</p><p>Zu beachten ist, dass beide Regeln nicht vorgeben, welches Protokoll zu verwenden ist. Das ist bewusst offen gelassen, sodass man HTTP, HTTPS, TCP, UDP oder ein beliebiges anderes Protokoll je nach Bedarf ausw&#228;hlen kann.</p><figure class=„video akwa-inline-video video–fullwidth“><figcaption class=„bildunterschrift akwa-caption“>&#220;ber Ports mit Diensten kommunizieren</figcaption></figure><p>Die dritte Regel betrifft die Art, wie eine Anwendung konfiguriert wird. Sie besagt, dass man Konfiguration mit Hilfe von Umgebungsvariablen vornehmen soll. Das bietet gleich mehrere Vorteile.</p><p>Zum einen ist das Vorgehen plattformunabh&#228;ngig, was beispielsweise f&#252;r Kommandozeilenparameter und Konfigurationsdateien in der Regel nicht gilt, ganz zu schweigen von den zahlreichen Dateiformaten. Zum anderen l&#228;uft man nicht so schnell Gefahr, Umgebungsvariablen versehentlich in die Versionsverwaltung einzuchecken, da sie &#8211; anders als beispielsweise eine Konfigurationsdatei &#8211; &#252;blicherweise nicht in einer Datei in dem Quellcode-Verzeichnis abgelegt werden.</p><p>Bei komplexeren Konfigurationen kann man zur Not nat&#252;rlich immer noch auf eine Konfigurationsdatei ausweichen. Trotzdem empfiehlt sich dann, den Pfad zur Konfigurationsdatei &#252;ber eine Umgebungsvariable konfigurierbar zu machen. Auf dem Weg lassen sich n&#228;mlich auf einer Maschine mehrere Instanzen einer Anwendung starten, ohne dass diese sich gegenseitig in die Quere kommen.</p><p>Die elfte Regel besagt, dass Logging stets auf den Standardausgabe- beziehungsweise Standardfehlerstrom der Anwendung erfolgen sollte. Das wirkt zun&#228;chst kontraintuitiv. Trotzdem ist es sinnvoll, da sp&#228;testens beim Betrieb der Anwendung in einem Container Log-Dateien fl&#252;chtig sind: Wird der Container zerst&#246;rt, wird auch die Log-Datei verworfen.</p><p>Daher ist es ratsam und in einem verteilten System auch einfacher und &#252;bersichtlicher, die verschiedenen Ausgabestr&#246;me der einzelnen Dienste zentral zu sammeln und auszuwerten. Diese Aufgabe l&#228;sst sich gut au&#223;erhalb der Anwendung durchf&#252;hren, was die Entwicklung der Anwendung an sich wiederum einfacher macht.</p><figure class=„video akwa-inline-video video–fullwidth“><figcaption class=„bildunterschrift akwa-caption“>Konfiguration und Logging</figcaption></figure><p>Die f&#252;nfte Regel besagt, dass man das Bauen und das Ausf&#252;hren einer Anwendung in zwei verschiedene Phasen zerlegen soll. Eine Anwendung wird zun&#228;chst erstellt und beispielsweise in ein Installationspaket oder ein Docker-Image verpackt. Anschlie&#223;end wird sie ausgef&#252;hrt. Es sollte f&#252;r die Ausf&#252;hrung nicht mehr erforderlich sein, zun&#228;chst noch Code zu kompilieren oder &#196;hnliches &#8211; denn das verschlechtert nicht nur die Startzeit, sondern f&#252;hrt auch zu fragileren Anwendungen.</p><p>Die zehnte Regel schlie&#223;lich besagt, dass die Entwicklungs- und die Ausf&#252;hrungsumgebung m&#246;glichst gleich sein sollen. Damit ist beabsichtigt, dass man Fehler m&#246;glichst fr&#252;h (also bereits w&#228;hrend der Entwicklung) aufsp&#252;ren kann, und nicht in der Staging- oder der Produktivumgebung die ein oder andere b&#246;se &#220;berraschung erlebt, weil sich die Umgebung dort gravierend von der lokalen unterscheidet.</p><p>Das Gleiche gilt nat&#252;rlich nicht nur f&#252;r die lokale Entwicklungs- und die Produktivumgebung, sondern auch f&#252;r alle anderen Umgebungen, beispielsweise Test und QA. Auch dort sollte die Infrastruktur der finalen Umgebung m&#246;glichst gleichen. Das bedeutet nat&#252;rlich nicht, dass alle Umgebungen auf die Datenbank aus dem Produktivsystem zugreifen sollten, aber es sollte eben lokal eine Datenbank zur Verf&#252;gung stehen, die der aus der produktiven Umgebung m&#246;glichst gleicht.</p><figure class=„video akwa-inline-video video–fullwidth“><figcaption class=„bildunterschrift akwa-caption“>Entwickeln, bauen und betreiben</figcaption></figure><p>Wer sich an die 12 Regeln h&#228;lt, macht im Hinblick auf eine tragf&#228;hige verteilte Architektur schon vieles richtig. Wie eingangs erw&#228;hnt, sind diese Regeln nicht die einzigen, die man bei komplexen Anwendungen im Sinn haben sollte, aber dennoch bilden sie eine gute Grundlage.</p><p>Gerade, wer noch nicht &#252;ber allzu viel Erfahrung in der Struktur und Architektur von Anwendungen verf&#252;gt, aber trotzdem eine verteilte Anwendung entwickeln will oder muss, der ist gut beraten, diese Regeln zu beachten, da sie sich in der Praxis bew&#228;hrt haben.</p> </html>

Cookies helfen bei der Bereitstellung von Inhalten. Diese Website verwendet Cookies. Mit der Nutzung der Website erklären Sie sich damit einverstanden, dass Cookies auf Ihrem Computer gespeichert werden. Außerdem bestätigen Sie, dass Sie unsere Datenschutzerklärung gelesen und verstanden haben. Wenn Sie nicht einverstanden sind, verlassen Sie die Website.Weitere Information