KAPITEL 14

Cluster als Code bauen

In Kapitel 3 wurde ein Anwendungs-Hosting-Cluster als Service beschrieben, der Anwendungs-Instanzen dynamisch auf einen Pool von Servern deployt und dort ausführt (siehe »Computing-Ressourcen« auf Seite 58). Beispiele für Anwendungs-Cluster-Systeme sind Kubernetes, AWS ECS, HashiCorp Nomad, Mesos und Pivotal Diego. Dieses Modell trennt die Aspekte des Orchestrierens von Anwendungen von den Aspekten des Provisionierens und Konfigurierens der Server, auf denen sie laufen (siehe Abbildung 14-1).

image

Abbildung 14-1: Ein Anwendungs-Cluster erstellt eine Schicht zwischen Infrastruktur-Ressourcen und den Anwendungen, die darauf laufen.

Sie sollten natürlich Code nutzen, um Ihr Anwendungs-Cluster zu definieren und zu managen. Wie in Kapitel 10 beschrieben können Sie ein Anwendungs-Cluster als einen oder mehrere Anwendungs-Stacks als Code bauen. In diesem Kapitel zeige ich Ihnen ein paar Beispiele dafür, wie so etwas aussehen kann. Ich stelle unterschiedliche Stack-Topologien vor, zum Beispiel einen einzelnen Stack für ein ganzes Cluster oder das Aufteilen von Clustern auf mehrere Stacks (siehe »Stack-Topologien für Anwendungs-Cluster« auf Seite 272). Zudem finden Sie hier Abbildungen für Pipelines, um Infrastruktur-Code-Änderungen für diese Topologien auszuliefern.

Und ich stelle Strategien für das gemeinsame oder eben auch nicht gemeinsame Nutzen von Clustern über Umgebungen und Teamgrenzen hinweg vor (siehe »Strategien zur gemeinsamen Verwendung von Anwendungs-Clustern« auf Seite 280). Am Ende des Kapitels finden Sie Informationen zu Infrastruktur für Serverless-Anwendungen (siehe »Infrastruktur für FaaS Serverless« auf Seite 286).

Container als Code

Eine der vielen Stärken von Containern ist, dass sie als Code definiert sind. Ein Container-Image wird einmal aus einer Definitionsdatei gebaut und dann verwendet, um mehrere Instanzen zu erstellen. Um Container effektiv einzusetzen, ist es wichtig, dass Sie sie als immutabel und zustandslos behandeln.

Nehmen Sie keine Änderungen am Inhalt einer Container-Instanz vor – ändern Sie stattdessen die Definition, erstellen Sie ein neues Image und ersetzen Sie dann die Instanz. Und speichern Sie den Zustand nicht innerhalb eines Containers. Legen Sie Zustand und Daten stattdessen auf anderen Infrastruktur-Ressourcen ab (siehe »Datenkontinuität in einem sich ändernden System« auf Seite 430).

Durch ihre immutable und zustandslose Natur passen Container perfekt zur dynamischen Natur von Cloud-Plattformen, weshalb sie so stark mit Cloud-nativer Architektur verbunden werden (siehe »Cloud-native und anwendungsgesteuerte Infrastruktur« auf Seite 190).

Lösungen für Anwendungs-Cluster

Es gibt zwei unterschiedliche Vorgehensweisen beim Implementieren und Warten eines Anwendungs-Clusters. Eine ist die Verwendung eines Managed Cluster als Service, meist als Teil Ihrer Infrastruktur-Plattform bereitgestellt. Die andere ist das Deployen einer Packaged-Cluster-Lösung auf Low-Level-Infrastruktur-Ressourcen.

Cluster as a Service

Die meisten Infrastruktur-Plattformen bieten einen Managed Cluster Service an. Sie können ein Cluster als Code definieren, provisionieren und ändern, indem Sie Ihr Stack-Konfigurationstool nutzen. Um ein Cluster as a Service zu nutzen, erstellen Sie einen Stack, der das Cluster und die unterstützenden Elemente enthält.

Viele dieser Cluster basieren auf Kubernetes, unter anderem EKS, AKS und GKE. Andere nutzen eigene Container-Services, wie zum Beispiel ECS.

image

Managed Kubernetes Cluster sind keine Cloud-Abstraktionsschicht

Auf den ersten Blick scheinen durch einen Anbieter gemanagte Kubernetes-Cluster eine wunderbare Lösung für das transparent auf unterschiedlichen Cloud-Plattformen durchgeführte Entwickeln und Betreiben von Anwendungen zu sein. Jetzt können Sie Anwendungen für Kubernetes bauen und sie auf jeder Cloud laufen lassen, die Sie nutzen wollen!

In der Praxis mag ein Anwendungs-Cluster als ein Teil Ihrer Anwendungslaufzeit-Schicht nützlich sein, aber es ist viel mehr Arbeit erforderlich, um eine vollständige Laufzeitplattform zu schaffen. Und es ist nicht einfach, eine vollständige Abstraktion von den zugrunde liegenden Cloud-Plattformen zu erreichen.

Anwendungen benötigen neben dem Computing auch Zugriff auf andere Ressourcen, die von einem Cluster bereitgestellt werden, unter anderem Storage und Networking. Diese Ressourcen werden von verschiedenen Plattformen unterschiedlich angeboten, sofern Sie nicht eine Lösung schaffen, um diese zu abstrahieren.

Zudem brauchen Sie Services wie Monitoring, Identitätsmanagement und Secrets-Management. Auch hier werden Sie entweder auf jeder Cloud-Plattform einen anderen Service verwenden oder einen Service beziehungsweise eine Abstraktionsschicht erstellen, den/die Sie auf jede Cloud deployen können und dort auch warten müssen.

Daher ist das Anwendungs-Cluster tatsächlich nur ein kleines Element Ihrer gesamten Anwendungs-Hosting-Plattform. Und selbst dieses Element hat die Tendenz, je nach Cloud gewisse Unterschiede aufzuweisen – mit verschiedenen Versionen, Implementierungen und Tools für das eigentliche Kubernetes-System.

Packaged Cluster Distribution

Viele Teams installieren und managen Anwendungs-Cluster lieber selbst, statt gemanagte Cluster zu verwenden. Kubernetes ist die verbreitetste Core-Cluster-Lösung.

Sie können einen Installer wie kops (https://oreil.ly/D3xVB), Kubeadm (https://oreil.ly/XeMTa) oder kubespray (https://kubespray.io) verwenden, um Kubernetes auf eine Infrastruktur zu deployen, die Sie dafür provisioniert haben.

Es gibt auch zahlreiche fertig gepackte Kubernetes-Distributionen, die andere Services und Features mitbringen, unter anderem:1

Manche dieser Produkte besaßen zunächst eigene Container-Formate und Services zum Anwendungs-Scheduling und Orchestrieren. Viele entschieden sich dazu, ihre Kernprodukte rund um das überwältigend beliebte Docker und Kubernetes neu aufzubauen, statt weiter an einer eigenen Lösung zu arbeiten.

Ein paar Produkte haben sich der Assimilation in die Kubernetes Borg (https://oreil.ly/tgr7o) allerdings widersetzt. Dazu gehören HashiCorp Nomad (https://www.nomadproject.io) und Apache Mesos (http://mesos.apache.org), die jeweils Container-Instanzen, aber auch nicht-containerisierte Anwendungen auf unterschiedlichen Computing-Ressourcen orchestrieren können. Die Cloud Foundry Application Runtime (CFAR, https://oreil.ly/trdM1) besitzt seine eigene Container-Orchestrierung (Diego, https://oreil.ly/vyW86), auch wenn sie zusammen mit Kubernetes genutzt werden kann.1

Stack-Topologien für Anwendungs-Cluster

Ein Anwendungs-Cluster besteht aus einer Reihe von Elementen. Ein Teil dieser Elemente sind die Anwendungen und Services, die das Cluster managen. Zu diesen Cluster-Management-Services gehören unter anderem:

Viele Cluster-Deployments lassen Management-Services auf dedizierten Servern laufen, die von den Services getrennt sind, die Anwendungs-Instanzen hosten. Diese Services sollten aus Resilienzgründen vermutlich auch geclustered laufen.

Die anderen wichtigen Elemente eines Anwendungs-Clusters sind die Knoten, die die Anwendung hosten. Diese Knoten sind ein Pool von Servern, auf denen der Scheduler Anwendungs-Instanzen ausführt. Es ist üblich, diese als Server-Cluster einzurichten (siehe »Computing-Ressourcen« auf Seite 58), um die Menge und die Standorte der Server-Instanzen zu managen. Ein Service Mesh (siehe »Service Mesh« auf Seite 284) kann Sidecar-Prozesse auf den Host-Knoten neben den Anwendungs-Instanzen ausführen.

Ein Beispiel für ein Anwendungs-Cluster (Abbildung 14-2) enthält Server zum Ausführen der Cluster-Management-Services, ein Server-Cluster zum Betreiben der Anwendungs-Instanzen und Netzwerk-Adressblöcke.

image

Abbildung 14-2: Ein Beispiel-Anwendungs-Cluster

Die Netzwerkstrukturen für ein Anwendungs-Cluster können flach sein. Die Cluster-Services weisen den Anwendungs-Instanzen Netzwerkadressen zu. Sie sollten sich auch um die Netzwerksicherheit kümmern, zum Beispiel um Verschlüsselung und das Verbindungs-Management, wofür oft ein Service Mesh zum Einsatz kommt.

Sie verwenden Infrastruktur-Stacks, um diese Infrastruktur zu provisionieren. In Kapitel 5 ist beschrieben, dass die Größe eines Stacks und der Scope dessen Inhalts Auswirkungen auf die Geschwindigkeit und die Risiken von Änderungen haben.

»Patterns und Antipatterns für das Strukturieren von Stacks« auf Seite 86 beschreibt Patterns für das Zuordnen von Ressourcen in Stacks. Die folgenden Beispiele zeigen, wie man diese Patterns auf Cluster-Infrastruktur anwendet.

Monolithischer Stack, der Cluster as a Service nutzt

Das einfachste Design ist, alle Elemente Ihres Clusters in einem einzelnen Stack zu definieren und damit dem Monolithic-Stack-Antipattern zu folgen (siehe »Antipattern: Monolithic Stack« auf Seite 86). Auch wenn Monolithen im größeren Maßstab zu einem Antipattern werden, kann ein einzelner Stack nützlich sein, wenn Sie mit einem kleinen und einfachen Cluster beginnen.

In Listing 14-1 wird ein Cluster as a Service genutzt, ähnlich zu AWS EKS, AWS ECS, Azure AKS und Google GKE. Der Code definiert damit das Cluster, muss aber keine Server provisionieren, auf denen das Cluster-Management läuft, weil sich die Infrastruktur-Plattform darum schon hinter den Kulissen kümmert.

Listing 14-1: Stack-Code, der alles für ein Cluster definiert

address_block:

name: cluster_network

address_range: 10.1.0.0/16"

vlans:

- vlan_a:

address_range: 10.1.0.0/8

- vlan_b:

address_range: 10.1.1.0/8

- vlan_c:

address_range: 10.1.2.0/8

application_cluster:

name: product_application_cluster

address_block: $address_block.cluster_network

server_cluster:

name: "cluster_nodes"

min_size: 1

max_size: 3

vlans: $address_block.cluster_network.vlans

each_server_node:

source_image: cluster_node_image

memory: 8GB

Dieses Beispiel lässt vieles weg, was Sie für ein echtes Cluster bräuchten, wie zum Beispiel Netzwerk-Routen, Sicherheitsrichtlinien oder Monitoring. Aber es zeigt, dass die signifikanten Elemente – Networking, Cluster-Definition und Server-Pool für Host-Knoten – alle im gleichen Projekt liegen.

Monolithischer Stack für eine Packaged-Cluster-Lösung

Der Code in Listing 14-1 verwendet einen Anwendungs-Cluster-Service, der von der Infrastruktur-Plattform angeboten wird. Viele Teams setzen stattdessen auf eine Packaged-Cluster-Lösung (wie in »Packaged Cluster Distribution« auf Seite 271 beschrieben). Diese Lösungen haben Installer, die die Cluster-Management-Software auf die Server deployen.

Beim Einsatz einer dieser Lösungen provisioniert Ihr Infrastruktur-Stack die Infrastruktur, die der Installer zum Deployen und Konfigurieren des Clusters benötigt. Das Ausführen des Installers sollte ein eigener Schritt sein. So können Sie den Infrastruktur-Stack getrennt vom Anwendungs-Cluster testen. Hoffentlich können Sie die Konfiguration für Ihr Cluster als Code definieren. Dann können Sie diesen Code entsprechend managen – mit Tests und einer Pipeline, die dabei hilft, Updates und Änderungen einfach und sicher auszuliefern.

Es kann nützlich sein, Serverkonfigurationscode (wie in Kapitel 11) zum Deployen Ihres Cluster-Management-Systems auf Ihre Server zu verwenden. Manche der Packaged-Produkte nutzen Standard-Konfigurationstools, wie zum Beispiel Ansible for OpenShift, sodass Sie diese in Ihren Stack-Building-Prozess einbinden können. In Listing 14-2 sehen Sie einen Code-Ausschnitt, den Sie zum monolithischen Stack-Code aus Listing 14-1 hinzufügen können, um einen Server für die Cluster-Management-Anwendung zu erzeugen.

Listing 14-2: Code zum Bauen eines Cluster-Management-Servers

virtual_machine:

name: cluster_manager

source_image: linux-base

memory: 4GB

provision:

tool: servermaker

parameters:

maker_server: maker.shopspinner.xyz

role: cluster_manager

Der Code konfiguriert den Server, indem er den fiktiven Befehl servermaker ausführt, mit dem die Rolle cluster_manager angewendet wird.

Pipeline für einen monolithischen Anwendungs-Cluster-Stack

Da es nur einen Stack gibt, kann eine einzelne Pipeline Code-Änderungen an Instanzen des Anwendungs-Clusters testen und ausliefern. Aber es sind andere Elemente involviert, unter anderem das Server-Image für die Host-Knoten und die Anwendungen selbst. In Abbildung 14-3 sehen Sie ein potenzielles Design für diese Pipelines.

image

Abbildung 14-3: Ein Beispiel für Pipelines für ein Cluster, das einen monolithischen Stack nutzt

Die obere Pipeline (Abbildung 14-4) baut ein Server-Image für die Host-Knoten, wie dies in »Eine Pipeline zum Testen und Ausliefern eines Server-Image verwenden« auf Seite 259 beschrieben wurde. Das Ergebnis dieser Pipeline ist ein Server-Image, das isoliert getestet wurde. Die Tests für dieses Image prüfen vermutlich, ob die Container-Management-Software installiert wurde und ob es den Sicherheitsrichtlinien entspricht.

image

Abbildung 14-4: Die Pipeline für das Server-Image des Host-Knotens

Die untere Pipeline (Abbildung 14-5) ist für eine Anwendung gedacht, die auf das Cluster deployt. In der Praxis werden Sie mehrere davon haben – jeweils eine für jede separat deployte Anwendung. Diese Pipeline enthält mindestens eine frühe Stage zum Bauen und eigenständigen Testen der Anwendung. Dann gibt es Stages, die die Anwendung in jeder Umgebung auf das Cluster deployen. Die Anwendung kann in diesen Umgebungen getestet, begutachtet und zum Einsatz verfügbar gemacht werden. Die Pipelines für die Anwendungen sind mit den Pipelines für die Cluster-Instanzen sehr lose gekoppelt. Sie können sich dazu entscheiden, die Stages zum Testen der Anwendungen nach Cluster-Updates anzutriggern. Das hilft Ihnen dabei, Probleme zu finden, die durch Änderungen am Cluster in den Anwendungen entstanden sind.

image

Abbildung 14-5: Die Pipeline für das Ausliefern von Anwendungen auf das Cluster

Die Pipeline für den Anwendungs-Cluster-Stack in Abbildung 14-6 startet mit einer Offline-Stage (siehe »Offline-Test-Stages für Stacks« auf Seite 165), die die Syntax prüft und den Stack-Code auf einen lokalen Mock der Infrastruktur-Plattform anwendet (siehe »Testen mit einer Mock-API« auf Seite 167). Diese Tests können Probleme auf der Coding-Ebene abfangen, ohne Ressourcen der Infrastruktur-Plattform einsetzen zu müssen, weshalb sie schnell durchlaufen.

image

Abbildung 14-6: Die Pipeline für den Cluster-Stack-Code

Die zweite Stage dieser Pipeline ist eine Online-Stage (siehe »Online-Test-Stages für Stacks« auf Seite 168) und sie erzeugt eine Instanz des Stack auf der Infrastruktur-Plattform. Die Instanz kann persistent (siehe »Pattern: Persistent Test Stack« auf Seite 176) oder kurzlebig sein (siehe »Pattern: Ephemeral Test Stack« auf Seite 178). Die Tests in dieser Stage können prüfen, ob die Cluster-Management-Services korrekt erstellt wurden und erreichbar sind. Sie können auch Sicherheitsaspekte kontrollieren – zum Beispiel sicherstellen, dass der Zugriff auf Cluster-Management-Endpunkt nicht möglich ist.1

Weil dieser monolithische Cluster-Stack den Code zum Erstellen der Host-Knoten-Server enthält, kann die Online-Test-Stage diese ebenfalls prüfen. Ein Test könnte eine Beispielanwendung auf das Cluster deployen und zeigen, dass sie funktioniert. Der Vorteil einer Beispielanwendung liegt gegenüber einer echten Anwendung darin, dass Sie sie einfach halten können. Reduzieren Sie sie auf einen minimalen Satz an Abhängigkeiten und Konfiguration, sodass Sie sicher sein können, dass fehlschlagende Tests mit Problemen beim Provisionieren des Clusters zusammenhängen und nicht durch die Komplexität beim Deployen einer echten Anwendung verursacht werden.

Beachten Sie, dass diese Pipeline-Stage groß ist. Sie testet sowohl die Cluster-Konfiguration wie auch das Server-Cluster für die Host-Knoten. Es gibt viele verschiedene Dinge, die dafür sorgen können, dass diese Stage fehlschlägt, was die Fehlersuche und das Beheben von Bugs erschwert.

Der größte Teil der Zeit für diese Stage wird sicherlich durch das Provisionieren von diesem und jenem verbraucht werden – weit mehr, als für das eigentliche Ausführen der Tests nötig ist. Diese zwei Aspekte – die verschiedenen Dinge, die in dieser Stage getestet werden, und die Zeit zum Provisionieren – sind die entscheidenden Gründe dafür, das Cluster in mehrere Stacks zu unterteilen.

Beispiel für mehrere Stacks in einem Cluster

Durch das Aufteilen des Infrastruktur-Codes für ein Cluster auf mehrere Stacks können die Zuverlässigkeit und Geschwindigkeit Ihres Änderungsprozesses verbessert werden. Versuchen Sie, jeden Stack so zu entwerfen, dass Sie ihn isoliert provisionieren und testen können, statt Instanzen von anderen Stacks provisionieren zu müssen.

Beginnen Sie damit, den Server-Pool mit den Host-Knoten in einen eigenen Stack auszulagern (siehe Listing 14-3). Sie können eine Instanz dieses Stacks ohne das Anwendungs-Cluster provisionieren und testen. Prüfen Sie, ob die Plattform erfolgreich Server aus Ihren Images bootet und dass Netzwerk-Routen korrekt funktionieren. Sie können auch die Zuverlässigkeit testen, ein Problem mit einem der Server »verursachen« und zeigen, ob die Plattform ihn automatisch ersetzt.

Listing 14-3: Stack-Code, der den Server-Pool mit Host-Knoten definiert

server_cluster:

name: "cluster_nodes"

min_size: 1

max_size: 3

vlans: $address_block.host_node_network.vlans

each_server_node:

source_image: cluster_node_image

memory: 8GB

address_block:

name: host_node_network

address_range: 10.2.0.0/16"

vlans:

- vlan_a:

address_range: 10.2.0.0/8

- vlan_b:

address_range: 10.2.1.0/8

- vlan_c:

address_range: 10.2.2.0/8

Dieser Code fügt anders als der vorige Code für den monolithischen Stack (siehe Listing 14-1) für die Host-Knoten eigene VLANs hinzu. Es ist gute Praxis, die Host-Knoten und das Cluster-Management auf unterschiedliche Netzwerksegmente zu verteilen, was Sie im monolithischen Stack auch tun könnten. Durch das Aufteilen der Stacks müssen wir das tun, und wenn es nur dazu dient, die Kopplung zwischen den beiden Stacks zu verringern.

Das Aufteilen der Stacks sorgt für eine neue Pipeline für den Host-Knoten-Cluster-Stack (siehe Abbildung 14-7).

image

Abbildung 14-7: Zusätzliche Pipeline für den Host-Knoten-Pool

Auch wenn es in dieser kombinierten Pipeline ein paar mehr Stages gibt, sind sie schlanker und schneller. Die Online-Test-Stage für den Cluster-Management-Stack (in Abbildung 14-8 hervorgehoben) provisioniert nur die Cluster-Management-Infrastruktur, was schneller ist als die Online-Stage in der monolithischen Stack-Pipeline. Dieser Stack hängt nicht mehr länger von der Pipeline für Host-Knoten-Server-Images ab und enthält auch nicht die Server-Knoten. Daher können sich die Tests in dieser Stage darauf konzentrieren, zu prüfen, ob das Cluster-Management korrekt konfiguriert und abgesichert ist.

image

Abbildung 14-8: Online-Test-Stage für die Cluster-Pipelines

Dieses überarbeitete Design führt die Pipeline für den Host-Knoten-Server-Stack mit der Pipeline für den Cluster-Management-Stack in einer Stack-Integrations-Stage zusammen (siehe Abbildung 14-9).

image

Abbildung 14-9: Test-Stage zur Stack-Integration für ein Cluster

Dies ist eine Online-Test-Stage, die Instanzen auf beiden Stacks zusammen provisioniert und testet. Diese Tests können sich auf Probleme fokussieren, die nur aus dieser Kombination entstehen, daher sollten hier keine Testaktivitäten aus früheren Stages wiederholt werden. In dieser Stage würden Sie eine Beispielanwendung deployen und zeigen, dass sie im Cluster korrekt läuft. Sie könnten auch die Zuverlässigkeit und die Skalierungsfähigkeiten testen, indem Sie für Ausfälle in Ihrer Testanwendung sorgen und Situationen schaffen, für die zusätzliche Instanzen erforderlich sind.

Vielleicht entscheiden Sie sich dazu, das Ganze in mehr Stacks zu unterteilen – zum Beispiel könnten Sie die allgemeine Netzwerk-Infrastruktur aus dem Management-Stack herausnehmen. In den Kapiteln 15 und 17 geht es um die Details des Aufteilens und Integrierens der Infrastruktur auf verschiedene Stacks.

Strategien zur gemeinsamen Verwendung von Anwendungs-Clustern

Wie viele Cluster sollten Sie betreiben, wie groß sollten diese sein und wie viel sollten Sie in jedem laufen lassen?

Theoretisch könnten Sie ein einzelnes Cluster betreiben und die Umgebungen und andere Anwendungsgrenzen in der Cluster-Instanz umsetzen. Aber es gibt eine Reihe von Gründen, warum ein einzelnes Cluster nicht unbedingt praktikabel ist:1

Änderungen managen

Sie müssen Ihr Cluster aktualisieren, Fehler beheben und Änderungen darin vornehmen. Daher müssen Sie diese Änderungen zumindest irgendwo testen können, damit keine Services unterbrochen werden. Bei disruptiven Änderungen – zum Beispiel solchen, für die Downtime erforderlich oder zumindest möglich ist – ist es schwierig, einen Zeitpunkt zu finden, der die Bedürfnisse aller Teams, Anwendungen und Regionen erfüllt. Durch das Betreiben mehrerer Cluster wird es einfacher, Wartungsfenster zu finden, und es verringern sich die Auswirkungen fehlgeschlagener Änderungen.

Abgrenzung

Viele Cluster-Implementierungen bieten keine ausreichend starke Abtrennungen zwischen Anwendungen, Daten und Konfiguration. Sie haben vielleicht auch abhängig von den Services, die Sie betreiben, unterschiedliche Governance-Anforderungen. So haben beispielsweise Services, die mit Kreditkartennummern umgehen, strengere rechtliche Anforderungen, daher vereinfacht ein Betreiben auf einem eigenen Cluster die Anforderungen für Ihre anderen Cluster.

Konfigurierbarkeit

Manche Anwendungen oder Teams haben andere Konfigurations-Anforderungen für die genutzten Cluster. Durch eigene Cluster-Instanzen verringern Sie Konfigurations-Konflikte.

Performance und Skalierbarkeit

Cluster-Lösungen besitzen unterschiedliche Skalierungs-Charakteristiken. Viele kommen nicht gut mit einer höheren Latenz zurecht, wodurch es unpraktisch wird, ein einzelnes Cluster auf mehrere geografische Regionen zu verteilen. Anwendungen können auch an Ressourcengrenzen stoßen oder sich gegenseitig ins Gehege kommen, wenn sie auf einem einzelnen Cluster skaliert werden.

Verfügbarkeit

Ein einzelnes Cluster ist auch ein Single Point of Failure. Betreiben Sie mehrere Cluster, kann das dabei helfen, mit verschiedenen Ausfallszenarien umzugehen.

Es gibt ein paar mögliche Strategien für das Bestimmen der Größe und für das gemeinsame Nutzen von Cluster-Instanzen. Um die richtige Strategie für Ihr System zu wählen, berücksichtigen Sie Ihre Anforderungen beim Ändern der Abgrenzung, Konfigurierbarkeit, Performance, Skalierbarkeit, Verteilung und Verfügbarkeit. Dann testen Sie Ihre Anwendungs-Clustering-Lösung anhand dieser Anforderungen.

Ein großes Cluster für alles

Ein einzelnes Cluster lässt sich eventuell einfacher managen als mehrere Cluster. Die offensichtliche Ausnahme ist der Umgang mit Änderungen. Daher sollten Sie mindestens eine eigene Cluster-Instanz zum Testen von Änderungen nutzen und dort eine Pipeline zum Deployen und Testen von Änderungen an der Cluster-Konfiguration verwenden, bevor Sie diese auf Ihr Produktiv-Cluster loslassen.

Getrennte Cluster für Auslieferungs-Stages

Sie können unterschiedliche Cluster für die verschiedenen Elemente Ihres Software-Auslieferungsprozesses nutzen. Das kann ganz einfach ein Cluster pro Umgebung sein (siehe Abbildung 14-10).

Ein dediziertes Cluster für jede Umgebung vermeidet Inkonsistenzen, die Sie eventuell erhalten, wenn Anwendungen aus mehreren Umgebungen die gleichen Ressourcen verwenden. Aber es kann schwierig und teuer sein, eine eigene Instanz für jede Auslieferungs-Stage zu warten. Erzeugt Ihr Auslieferungsprozess beispielsweise dynamisch Test-Instanzen, müssen Sie eventuell auch dynamisch Cluster-Instanzen erstellen, auf denen diese laufen, was sehr langsam sein kann.

image

Abbildung 14-10: Pipelines managen ein Cluster für jede Anwendungs-Deployment-Umgebung.

Eine Abwandlung des Separierens von Clustern nach der Delivery-Stage ist das gemeinsame Verwenden von Clustern über mehrere Stages hinweg. So können Sie beispielsweise verschiedene Cluster abhängig von den Governance-Anforderungen einsetzen. In Abbildung 14-11 gibt es drei Cluster. Das DEV-Cluster dient der Entwicklung, hier laufen Instanzen, in denen Datensätze für eher explorative Testszenarien erzeugt und genutzt werden. Das NON-PROD-Cluster dient rigoroseren Delivery-Stages mit gemanagten Testdatensätzen. Das PROD-Cluster hostet die Umgebungen PREPROD und PROD, die beide Kundendaten enthalten und daher strengere Governance-Anforderungen besitzen.

image

Abbildung 14-11: Cluster-Instanzen, die von mehreren Umgebungen gemeinsam genutzt werden

Beim Hosten mehrerer Umgebungen in einem gemeinsamen Cluster sollten Sie versuchen, jede Umgebung von den anderen so getrennt wie möglich zu halten. Idealerweise sollten Anwendungen und operative Services nicht dazu in der Lage sein, Instanzen aus anderen Umgebungen zu sehen oder mit ihnen zu interagieren. Viele der Mechanismen, die Anwendungs-Clustering-Lösungen für das Trennen von Anwendungen zur Verfügung stellen, sind »sanft«. So können Sie beispielsweise vielleicht Instanzen taggen, um die Umgebung deutlich zu machen – aber das ist eine reine Konvention. Sie sollten nach strikteren Methoden zum Aufteilen von Anwendungen Ausschau halten.

Cluster für die Governance

Einer der Vorteile getrennter Cluster für die verschiedenen Elemente des Auslieferungsprozesses ist, dass sich die Governance-Anforderungen für die unterschiedlichen Stages im Prozess im Allgemeinen unterscheiden. Die Produktivumgebung besitzt strengere Anforderungen, weil die geschäftskritischsten Services dort laufen und die Daten die vertraulichsten sind.

Ziemlich häufig haben die unterschiedlichen Elemente eines Systems auch unterschiedlichen Governance- und Compliance-Anforderungen, die quer durch Delivery-Stages verlaufen. Das häufigste Beispiel sind Services, die sich um Kreditkartendaten kümmern, bei denen es sich um Bestandteile der PCI-Standards (https://oreil.ly/FUMrX) handelt. Andere Beispiele sind Services, die sich mit persönlichen Kundendaten befassen, die wiederum Thema von Richtlinien wie der DSGVO sind (https://dsgvo-gesetz.de/).

image

Abbildung 14-12: Getrennte Cluster je nach Auslieferungs-Stage und Regulierungs-Anforderungen

Wenn Sie Services, die strikteren Standards unterliegen, auf dedizierten Clustern hosten, kann das Compliance und Auditing vereinfachen und verbessern. Sie können strengere Kontrollelemente für diese Cluster, die darauf laufenden Anwendungen und das Ausliefern von Code-Änderungen umsetzen. Cluster, die Services mit weniger strengen Compliance-Anforderungen hosten, können auch vereinfachte Governance-Prozesse und Kontrollelemente besitzen.

Zum Beispiel könnten Sie zwei Cluster nutzen – eines für Entwicklung, Testen und Produktiv-Hosting für die regulierten Services und eines für die unregulierten Services. Oder Sie teilen Cluster-Instanzen nach Delivery-Stage und Regulierungs-Anforderungen auf, wie das in »Cluster für Teams« auf Seite 284 dargestellt ist.

Cluster für Teams

Ein weiterer Faktor für das Organisieren in mehreren Clustern ist die Team-Ownership. Häufig sind verschiedene Teams für das Ausliefern und Betreiben unterschiedlicher Arten von Anwendungen und Services zuständig, die auch unterschiedliche Hosting-Anforderungen haben können. So hat beispielsweise ein Team, das für Kundenservices verantwortlich ist, andere Anforderungen in Bezug auf Governance und Verfügbarkeit als ein Team, das Services für eine interne Abteilung betreibt. Ein Cluster, das einem Team zugeordnet ist, kann auf die Anforderungen dieses Teams und seiner Anwendungen hin optimiert werden.

Service Mesh

Ein Service Mesh ist ein dezentralisiertes Netzwerk aus Services, das die Verbindungen zwischen den Elementen eines verteilten Systems dynamisch managt. Es holt Networking-Features aus der Infrastruktur-Schicht in die Anwendungslaufzeit-Schicht des in »Die Elemente eines Infrastruktur-Systems« auf Seite 53 beschriebenen Modells. In einer typischen Service-Mesh-Implementierung delegiert jede Anwendungs-Instanz die Kommunikation mit anderen Instanzen an einen Sidecar-Prozess (siehe Abbildung 14-13).

image

Abbildung 14-13: Sidecars ermöglichen die Kommunikation mit anderen Prozessen in einem Service Mesh.

Zu den Diensten, die ein Service Mesh den Anwendungen bereitstellen kann, gehören unter anderem:

Routing

Leitet den Traffic zur passendsten Instanz einer gegebenen Anwendung, wo auch immer die gerade läuft. Durch das dynamische Routen mit einem Service Mesh ergeben sich ausgefeiltere Deployment-Szenarien, wie zum Beispiel Blue/Green oder Canary, die in »Live-Infrastruktur ändern« auf Seite 415 beschrieben sind.

Verfügbarkeit

Setzt Regeln zum Begrenzen der Anzahl an Requests um – zum Beispiel Circuit Breakers (https://oreil.ly/thM6m).

Sicherheit

Umgang mit Verschlüsselung, unter anderem mit Zertifikaten.

Authentifizierung

Setzt Regeln um, die festlegen, welche Services sich mit welchen anderen Services verbinden können. Kümmert sich um Zertifikate für Peer-to-Peer-Authentifizierung.

Observability, Monitoring und Fehlerbehebung

Zeichnet Verbindungen und andere Ereignisse auf, sodass man Requests in komplexen verteilten Systemen nachverfolgen kann.

Ein Service Mesh funktioniert gut zusammen mit einem Anwendungs-Hosting-Cluster. Das Anwendungs-Cluster provisioniert dynamisch und entkoppelt von Low-Level-Ressourcen Computing-Ressourcen. Das Service Mesh managt die Anwendungskommunikation dynamisch und entkoppelt von den Low-Level-Netzwerk-Ressourcen. Die Vorteile dieses Modells sind:

Beispiele für Service Meshes sind HashiCorp Consul (https://www.consul.io), Envoy (https://www.envoyproxy.io), Istio (https://istio.io) und Linkerd (https://linkerd.io).

Service Meshes werden meist mit containerisierten Systemen assoziiert. Aber Sie können das Modell auch in nicht-containerisierten Systemen implementieren – zum Beispiel durch das Deployen eines Sidecar-Prozesses auf virtuelle Maschinen.

Ein Service Mesh sorgt für zusätzliche Komplexität. Wie bei Cloud-nativen Architekturmodellen wie Microservices klingt ein Service Mesh verlockend, weil es die Entwicklung der einzelnen Anwendungen vereinfacht. Aber die Komplexität verschwindet nicht – Sie haben sie nur in die Infrastruktur verschoben. Daher muss Ihre Organisation darauf vorbereitet sein, damit umzugehen und sich auf eine steile Lernkurve einrichten.

Es ist wichtig, klare Grenzen zwischen dem Networking auf Infrastruktur-Ebene und dem Networking im Service Mesh zu trennen. Ohne ein gutes Design und entsprechende Disziplin beim Implementieren werden bestimmte Aspekte eventuell doppelt angegangen und durcheinandergewürfelt. Ihr System lässt sich schwerer verstehen, Änderungen werden riskanter, und die Fehlersuche wird komplexer.

Infrastruktur für FaaS Serverless

In Kapitel 3 ist FaaS Serverless als eine Möglichkeit aufgeführt, wie eine Plattform Computing-Ressourcen Anwendungen bereitstellen kann (siehe »Computing-Ressourcen« auf Seite 58). Normalerweise läuft Anwendungscode kontinuierlich in einem Container oder auf einem Server. FaaS führt Anwendungscode auf Anforderung hin aus – als Reaktion auf ein Event oder einen Zeitplan.

FaaS-Code ist für wohldefinierte, kurzlebige Aktionen nützlich, bei denen der Code schnell startet. Typische Beispiele sind die Behandlung von HTTP-Requests oder die Reaktion auf Fehler-Events in einer Message Queue. Die Plattform startet bei Bedarf mehrere Instanzen des Codes, um beispielsweise mehrere Events abzuarbeiten, die gleichzeitig eintreffen.

FaaS kann sehr effizient sein, wenn die Auslastung sehr stark variiert – es wird in Spitzenzeiten hochskaliert, und wenn kein Bedarf besteht, läuft auch nichts.

»Serverless« ist dafür eigentlich keine richtige Bezeichnung, weil der Code natürlich auf einem Server läuft. Es geht nur darum, dass der Server für Sie in der Entwicklung im Endeffekt unsichtbar ist. Das Gleiche gilt bei Containern, daher unterscheidet sich das sogenannte Serverless nicht im Abstraktionsgrad von Servern. Der wirkliche Unterschied liegt darin, dass es ein kurzlebiger und kein lang laufender Prozess ist.

Aus diesem Grund nutzen viele statt Serverless lieber den Begriff FaaS. Mit FaaS drücken Sie sich auch eindeutiger aus als mit Serverless, denn das kann auch Backend as a Service (BaaS) bedeuten, was ein extern gehosteter Service ist.1

FaaS Runtimes folgen den gleichen Modellen wie Anwendungs-Cluster – bereitgestellt als Service durch Ihre Infrastruktur-Plattform oder Packaged FaaS, für das Sie Infrastruktur und Management-Tools provisionieren und konfigurieren müssen.

Beispiele für FaaS Runtime als Service sind unter anderem:

Beispiele für Packaged-FaaS-Runtime-Lösungen sind:

Sie können die gleichen Strategien, die weiter oben in diesem Kapitel für das Provisionieren von Infrastruktur beschrieben wurden, für eine Packaged-FaaS-Lösung einsetzen, wie zum Beispiel Server-Pools und Management-Services. Achten Sie darauf, dass Sie im Detail verstehen, wie Ihre FaaS-Lösung funktioniert, damit Sie sicher sein können, dass aus Ihrem Code keine Daten »lecken«. Es könnten zum Beispiel temporäre Dateien und anderes an Orten übrig bleiben, die von anderem FaaS-Code einsehbar sind, was zu Sicherheits- und Compliance-Problemen führen kann. Ihre Entscheidungen zum Betreiben mehrerer Instanzen Ihrer FaaS Runtime sollten davon abhängen, wie gut die FaaS-Lösung Ihre Anforderungen für das Trennen von Daten erfüllt und ob sie skaliert.

Die von Cloud-Anbietern bereitgestellten FaaS-Services lassen Ihnen meist nicht so viele Konfigurationsmöglichkeiten, wie das Anwendungs-Cluster tun. So müssen Sie normalerweise nicht die Größe und Art des Host-Servers angeben, auf dem der Code ausgeführt wird. Das verringert drastisch die Menge an Infrastruktur, die Sie definieren und managen müssen.

Aber ein Großteil des FaaS-Codes interagiert mit anderen Services und Ressourcen. Sie müssen eventuell Networking für eingehende Requests definieren, die eine FaaS-Anwendung antriggern, und für ausgehende Requests, die vom Code ausgehen. FaaS-Code greift oft lesend und schreibend auf Daten und Messages auf Storage Devices, in Datenbanken und Message Queues zu. Für all das müssen Sie Infrastruktur-Ressourcen definieren und diese testen. Und natürlich sollte FaaS-Code mithilfe einer Pipeline ausgeliefert und getestet werden – wie jeder andere Code auch. Daher brauchen Sie trotzdem all die Praktiken rund um das Definieren und Transportieren von Infrastruktur-Code und das Integrieren mit Anwendungs-Testprozessen.

Zusammenfassung

Computing-Infrastruktur existiert, um Services zu unterstützen. Services werden durch Anwendungssoftware bereitgestellt, die auf einer Laufzeitumgebung läuft. Dieses Kapitel hat beschrieben, wie Sie Code verwenden, um Infrastruktur zu definieren, die Laufzeitumgebungen für die Anwendungen Ihrer Organisation bereitstellt.