Ein logischer Ausgangspunkt für unsere Blogserie über Kubernetes-Beratung wäre eine Einführung in die Kubernetes-Architektur selbst. Zumindest sollte man annehmen, dass dies der natürliche Ausgangspunkt ist! Tatsächlich ist dies der “ x-te “ (ich konnte nicht mitzählen…?) Beitrag in dieser Reihe.
Aber besser spät als nie. Diese grundlegende Einführung in Kubernetes basiert auf der wunderbar einfachen und prägnanten Erklärung der Container-Orchestrierungstechnologie von IBM.
Was ist Kubernetes?
Kubernetes ist ein Orchestrierungstool, mit dem wir containerbasierte Arbeitslasten ausführen und verwalten können. Zur Erklärung werfen wir einen Blick auf eine Referenzarchitektur von verwalteten Kubernetes-Diensten. Außerdem gehen wir etwas näher darauf ein, wie wir unsere Microservices in einer Kubernetes-Architektur bereitstellen würden.
Eine Kubernetes-Architektur besteht aus zwei Seiten – Cloud und Kunde
Eine Kubernetes-Architektur kann in zwei Seiten unterteilt werden:
- Cloud-verwaltete Seite von K8s
- Kunden-verwaltete Seite von K8s
Cloud-Seite – API-Server und Master Node
Die wichtigste Komponente auf der Cloud-Seite der Kubernetes-Architektur ist der K8-Master beziehungsweise der Master Node. Der Kubernetes-Master enthält eine Vielzahl wichtiger Komponenten, von denen der API-Server eine der wichtigsten Rollen einnimmt.
Kubernetes API-Server
Der Kubernetes-API-Server, der auf dem Master läuft, ist für die Ausführung aller Workloads unerlässlich. Er stellt eine Reihe von Funktionen bereit, mit denen wir genau festlegen können, wie diese Arbeitslasten ausgeführt werden sollen.
Kundenseitig – Woker Nodes und Kubelets
Worker Nodes, die ebenfalls auf Kubernetes basieren, befinden sich auf der vom Kunden verwalteten Seite der Architektur. Jeder Worker-Node enthält ein Kubelet.
Das Kubelet ist für die Planung verantwortlich und sorgt dafür, dass die Anwendungen auf den Worker Nodes funktionieren. Wie Sie sich wahrscheinlich vorstellen können, bedeutet dies, dass der Master-Node und die Kubelets häufig zusammenarbeiten.
Warum sollte man Kubernetes verwenden?
Lassen Sie uns jedoch einen Schritt zurücktreten, um die grundlegende Frage zu stellen: „Warum sollte man Kubernetes verwenden“?
Microservices
Wenn wir eine Cloud-native Anwendung haben, die aus Microservices besteht, müssen diese Microservices über das Netzwerk miteinander kommunizieren. Nehmen wir als vereinfachtes Beispiel an, wir haben ein Front-End und ein Back-End. Diese beiden Komponenten sollen noch heute skaliert und im Cluster bereitgestellt werden.
Kubernetes verwendet YAML, um die Ressourcen zu definieren, die an den API-Server gesendet werden und um am Ende die eigentliche Anwendung zu erstellen. Wie würde also ein einfaches YAML für das Deployment eines Pods, einer kleinen logischen Einheit, die es uns erlaubt, einen einfachen Container in einem Worker Node auszuführen, aussehen?
Wir beginnen mit einem Pod und einem damit verbundenen Image. Nehmen wir an, es handelt sich hierbei um einen Container, den wir bereits auf den Docker Hub hochgeladen haben. Wir verwenden eine Registry und nennen die Anwendung „fv.1.0“.
Außerdem haben wir Labels, die sehr wichtig sind und auf die wir gleich noch näher eingehen werden. Mit Labels können wir genau definieren, um welche Art von Artefakt es sich hier handelt. Die Beschriftung sollte etwa so lauten: „Die App ist f“:
Labels – a:f
Sobald der Pod erstellt ist, wollen wir ihn durch unseren Prozess in einen Worker-Node verschieben.
Kubectl
Dies wird über die Kubectl erreicht. Mithilfe von Kubectl stellen wir das einfache Manifest unseres Pods auf einem der Worker Nodes bereit. Das Pod-Manifest wird durch Kubetcl übertragen, trifft auf die Kubernetes-API, die auf dem K8s-Master läuft, woraufhin es wiederum mit einem der Kubelets auf der Kundenseite unserer Architektur kommuniziert.
Das Kubelet startet dann den Pod in seinem Worker-Node. Dem Pod wird auch eine interne IP-Adresse zugewiesen. An diesem Punkt können wir uns per SSH in einen der Worker Nodes einwählen und die IP-Adresse verwenden, um die Anwendung zu erreichen.
Damit ist die Bereitstellung einer einfachen Anwendung in einer Kubernetes-Architektur abgeschlossen. Gehen wir also noch einen Schritt weiter.
Kubernetes-Deployments und Desired State
Kubernetes verfügt über eine Abstraktion namens „Deployments“ (zu Deutsch: Bereitstellungen), mit der wir einen so genannten „Desired State“ („Soll-Zustand“) erstellen können. Wir können die Anzahl der Replikate festlegen, die wir für einen Pod wünschen, und wenn diesem Pod etwas zustößt und er ausfällt, wird ein neuer für uns erstellt.
Kehren wir zu dem Pod zurück, den wir auf einem Worker-Node bereitgestellt und als app:f bezeichnet haben. Wir wollen 3 Replikate dieses Pods erstellen. Kehren wir auch zu unserem ursprünglichen Manifest auf der Cloud-Seite zurück. Eine Sache, die wir Kubernetes mitteilen müssen, ist, dass wir keinen Pod, sondern eher eine Vorlage für einen Pod benötigen.
Darüber hinaus gibt es noch ein paar andere Dinge, die wir betrachten sollten. Wir müssen die Anzahl der Replikate definieren, zum Beispiel 3 Replika. Wir haben auch einen Selector. Damit weisen wir das Deployment an, jede Anwendung zu verwalten, die den gleichen Namen (app:f) trägt wie die Anwendung, die auf unserem Worker Node läuft.
Abschließend müssen wir noch definieren, mit welcher Art von Artefakt wir es zu tun haben – ein Deployment. Unser neues Manifest wird in etwa folgendermaßen aussehen:
Wir schieben das neue Manifest durch Kubectl so, dass es den API-Server erreicht. Hierbei sollte beachtet werden, dass das Manifest kein kurzlebiges (ephemeres) Objekt ist. Kubernetes muss den Soll-Zustand verwalten. Solange wir das Deployment haben und sie nicht gelöscht wurde, wird sie von Kubernetes in unserem Master Node verwaltet.
Unser Master Node erstellt jetzt ein Deployment. Da wir nun drei Replikate haben, wird sichergestellt, dass immer drei ausgeführt werden. Sobald das Deployment erstellt wurde, wird festgestellt, dass derzeit nur ein einziges Replikat auf unserem Working Node ausgeführt wird und dass zwei weitere erforderlich sind.
Der Master Node plant das Deployment der Anwendung in 3 Replikaten überall dort, wo unsere Kubernetes-Architektur über Ressourcen auf der Kundenseite verfügt. Es werden also weitere zwei in leere Worker Nodes platziert, die noch verfügbar sind.
Wir haben nun unser Kubernetes-Deployment erstellt. Wenn wir dasselbe für das Backend unserer Anwendung machen müssen, erstellen wir hierzu ein weiteres Anwendungs-Deployment. In unserem Main Node fügen wir App: back-end hinzu und skalieren es beispielsweise mit 2 Replika. Nun haben wir etwas wie:
Wie kommunizieren Services in einer Kubernetes-Architektur?
Jetzt gilt es, über die Kommunikation zwischen den Services nachzudenken, die wir auf der Kundenseite unserer K8-Architektur bereitgestellt haben. Wie bereits erwähnt, hat jeder Pod eine IP-Adresse. Aber einige könnten Fehler aufweisen oder müssen irgendwann aktualisiert werden.
Wenn ein Pod verschwindet und zurückkommt, geschieht dies mit einer anderen IP-Adresse. Wenn wir jedoch vom Backend oder sogar von externen Benutzern auf einen dieser Pods zugreifen möchten, benötigen wir eine IP-Adresse, auf die wir uns verlassen können. Dies ist ein seit langem bestehendes Problem – daher wurden Service-Registry- und Service-Recovery-Funktionen speziell für dieses Problem entwickelt.
Diese Funktionen sind in Kubernetes integriert. Jetzt werden wir einen Dienst erstellen, um eine stabilere IP-Adresse festzulegen, damit wir durch eine einzelne App und nicht durch separate Services auf unsere Pods zugreifen können.
Wir treten erneut einen Schritt zurück und erstellen eine Service-Definition für unsere drei Pods. Hierzu benötigen wir einige neue Manifeste in YAML, wofür wir einen neuen Abschnitt in unserer Datei erstellen. Wir müssen kind: service und einen Selector hinzufügen, der mit dem App-Label übereinstimmt. Zuletzt brauchen wir noch einen type – dies ist unsere Cluster-IP, damit wir auf Dinge innerhalb des Kubernetes-Clusters zugreifen können. Das Resultat sieht in etwa wie folgt aus:
Wir stellen unser YAML erneut über Kubectl bereit. Es wird unseren Master erreichen und dann auf die Kundenseite übertragen, um die jeweilige Abstraktion zu erstellen, die unsere Pods zu einer einzigen Anwendung gruppiert. Wir haben jetzt eine interne Cluster-IP, mit der wir die Kommunikation zwischen unseren Diensten gewährleisten können:
Darüber hinaus ermöglicht der DNS-Dienst von Kubernetes, der normalerweise standardmäßig ausgeführt wird, dass selbst unsere Services leichter aufeinander zugreifen können, indem nur ihre Namen wie Frontend und Backend oder kurz f und b verwendet werden.
Wie stellen wir Nutzern unser Frontend zur Verfügung?
Um nun einen Schritt weiterzugehen und das Frontend unserer Anwendung Endnutzern zugänglich zu machen, müssen wir die Art des Dienstes definieren. In diesem Falle brauchen wir einen Load Balancer. Es gibt andere Optionen, um dies zu erreichen, wie beispielsweise Node Ports, aber ein Load Balancer erstellt eine externe IP zu unserem Cluster.
Wir geben diese externe IP-Adresse dann direkt an Endnutzer weiter, die anschließend hierüber direkt auf das Frontend zugreifen können.
Fazit
Wir haben drei Hauptkomponenten einer Kubernetes-Architektur besprochen:
- Pods
- Bereitgestellte und anschließend von Deployments verwaltete Pods
- Erleichterung des Zugriffs auf die von diesen Deployments erstellten Pods mithilfe von Services
Dies sind die drei Hauptkomponenten, die mit den Kubernetes-Master- und Worker Node zusammenarbeiten, damit wir den DevOps-Workflow für die Bereitstellung von Anwendungen in einem verwalteten Kubernetes-Dienst nach unseren Wünschen neu definieren können.
Wenn Sie von Kubernetes-Beratung oder Softwareentwicklungs-Ressourcen mit Kubernetes-Erfahrung profitieren könnten, unterstützen wir Sie gerne. Sprechen Sie uns einfach an!