Was ist Kubernetes?

Einführung in Kubernetes

Einführung in Kubernetes

Was ist Kubernetes?

Einführung in Kubernetes


Was ist Kubernetes und wie funktioniert es? Das klären wir in dieser grundlegenden Einführung zur Container-Orchestrierung mit Kubernetes. Betrachten wir zunächst die Frage, in welchem Kontext Kubernetes entstanden ist und welche Probleme damit gelöst werden sollen.

Was ist Kubernetes?

Als Anfang 2013 Docker auf den Markt kam, dachten viele noch an einen neuen Hype, der schnell wieder in den Tiefen des Internets verschwindet. Mittlerweile hat sich Docker jedoch breitflächig durchgesetzt und die IT-Landschaft grundlegend verändert. Docker ist nicht mehr wegzudenken und genießt eine immer größer werdende Beliebtheit.

Enormen Einfluss auf diesen Erfolg hatte von Anfang an unter anderem Google, das mit Kubernetes ein Open-Source-Projekt zur Administration mehrerer Docker-Container veröffentlicht hat. Doch was ist Kubernetes genau und wie funktioniert es? Wir möchten in dieser Einführung einen Überblick über Docker und Kubernetes geben und so ein Grundverständnis der Funktionalitäten schaffen.

Von Docker nach Kubernetes

Das Konzept der Container existiert seit über einem Jahrzehnt und wurde bereits in vielen bekannten Unix-basierten Betriebssystemen wie zum Beispiel Linux, Solaris oder FreeBSD eingesetzt. Jedoch war es erst Docker, das die Containertechnologie für Entwickler und den IT-Betrieb im täglichen Umgang zugänglich und handhabbar gemacht hat. Docker bewies die Portabilität und Skalierbarkeit von Anwendungen, weshalb immer mehr Entwickler und der IT-Betrieb dazu übergingen, die Container für das Bauen von Software und Auflösen von Abhängigkeiten zu verwenden. Auch in DevOps-Prozessen spielen Container eine entscheidende Rolle. Sie sind zu einem integralen Bestandteil automatisierter Software-Builds und der kontinuierlichen Integration und Bereitstellung (Continuous Integration/Continuous Delivery) von Pipelines geworden.

Während man in kleinen Infrastrukturen ohne Probleme das Lifecycle-Management der Container manuell handhaben kann, verliert man in verteilten Rechenzentren mit hunderten Containern schnell den Überblick. Das Verwalten vieler unterschiedlicher Container in Entwicklungs- und Produktionsumgebungen mit unterschiedlichen Netzwerk- und Festplattenanforderungen erforderte eine Lösung. Eine der bekanntesten Lösungen ist Kubernetes.

Kubernetes ist ein Open-Source-System für das automatische Deployment, Skalieren und Verwalten von containerisierten Anwendungen. So ist Kubernetes auf seiner eigenenWebseite beschrieben. Urheber von Kubernetes ist Google, das Kubernetes als ein Google-Infrastructure-for-Everybody-Else-(GIFEE-)Projekt vollständig Open Source auf GitHub zur Verfügung gestellt hat. Neben Google als initialem und Hauptbeitragendem lebt das Projekt von vielen individuellen Entwicklern sowie beitragenden Firmen wie zum Beispiel Red Hat, Microsoft, NTT DATA oder Docker selbst. Kubernetes, auch bekannt unter dem Kürzel „k8s“, entstand nicht plötzlich aus dem Nichts. Google arbeitete bereits seit fünfzehn Jahren an einer Plattform zur Verwaltung von Containern. Damals hieß das Projekt noch „Borg“, wurde dann zu „Omega“ umbenannt und ist schließlich als Open-Source-Lösung unter dem Namen „Kubernetes“ erschienen.

Video: Kubernetes Patterns

Die Kubernetes-Architektur

Master: Wie fast alle verteilten IT-Plattformen besteht auch Kubernetes aus mindestens einem Master und mehreren Nodes. Es ist möglich, beide Komponenten auf einem einzelnen System zu betreiben. Das wird unter anderem bei Minikube, einer lokalen Testumgebung, basierend auf VirtualBox, so zur Verfügung gestellt. Der Master verwaltet und veröffentlicht die Schnittstelle (API), über die er angesteuert werden kann, und überwacht den Zustand des vollständigen Clusters. Darüber hinaus plant und steuert der Master auch das Deployment auf den jeweiligen Nodes. Der Master kann zum Beispiel aus Gründen der Hochverfügbarkeit auch auf mehreren Systemen betrieben und hinter einen Load Balancer, der in Kubernetes integriert ist, gesetzt werden. Der Master definiert sich durch folgende drei Komponenten:

  • Der API-Server ist die zentrale Anlaufstelle für alle Befehle des gesamten Clusters und erlaubt dem Administrator die Konfiguration der einzelnen Kubernetes-Objekte. Der API-Server überwacht außerdem die Konsistenz zwischen den Parametern in etcd und den bereitgestellten Containern. Mit anderen Worten validiert und konfiguriert der API-Server die Daten der Pods, Services und ReplicaSets. Er weist Pods den einzelnen Nodes zu und synchronisiert die Pod-Information mit der Service-Konfiguration.
  • Der Service Controller Manager verarbeitet die in den jeweiligen Replikationsaufgaben definierten Replikationsprozesse. Die dazu benötigten Informationen werden in der etcd festgehalten, die durch den Controller Manager auf Änderungen hin überwacht wird. Bei einer Änderung liest der Controller Manager die Informationen aus der Datenbank und führt die nötigen Schritte aus, um den gewünschten Zustand zu erreichen, zum Beispiel das Skalieren eines ReplicaSet oder das Löschen eines Pods. Auch der Controller Manager bedient sich hierbei des API.
  • Der Scheduler organisiert innerhalb des Clusters die Lastverteilung auf einzelne Nodes. Das erreicht er, indem er die aktuellen Anforderungen erfasst, den aktuellen Zustand aller Nodes analysiert und schließlich die Last auf einen oder mehrere Nodes neu verteilt.

Jeder einzelne Node beinhaltet jeweils die Container-Runtime Docker, einen Agenten, der mit dem Master kommuniziert, sowie weitere grundlegende Komponenten, die unter anderem für das Monitoring, Logging und die Service Discovery zuständig sind. An dieser Stelle sei erwähnt, dass es natürlich auch möglich ist, statt Docker zum Beispiel rkt auf einem Node zu verwenden.

kubectl ist ein Kommandozeilenprogramm, das für alle gängigen Betriebssysteme zur Verfügung steht. Es wird für die Kommunikation mit dem Kubernetes-API-Server benötigt. Darüber lassen sich alle verfügbaren Befehle des API-Servers ausführen, wie zum Beispiel das Hinzufügen und Löschen von Pods, ReplicaSets oder Services. Außerdem kann man darüber auch auf die Logdateien zugreifen und den generellen Zustand des Kubernetes-Clusters überprüfen.

etcd ist eine verteilte Schlüssel-Wert-Datenbank von CoreOS, die als Single Source of Truth (SSOT) für alle Objekte innerhalb des Kubernetes-Clusters zur Verfügung steht. Diese Datenbank dient als Abstraktion zwischen den einzelnen Anwendungen und der eigentlichen Infrastruktur. Primär wird dieser Speicher durch den Master verwendet, um unterschiedliche Informationen zu den Nodes, Pods und den darin befindlichen Containern zu erhalten.

Nodes, auch Worker Nodes genannt, dienen nicht nur dem Master als Kommunikationspartner, sie dienen vielmehr den einzelnen Containern und stellen diesen CPU-, Arbeitsspeicher-, Netzwerk- und Festplattenressourcen zur Verfügung. Ein Node kann ein dediziertes System in einem Rechenzentrum, aber auch eine virtuelle Maschine (VM) bei einem Cloud-Provider sein. Auch das Mischen von virtuellen Maschinen und dedizierten Systemen über mehrere Rechenzentren hinweg ist innerhalb eines Clusters möglich. Auf einem Node sind für gewöhnlich folgende Dienste zu finden:

  • Der Dienst kubelet, der den API-Dienst des Masters überwacht und sicherstellt, dass sich alle auf dem Node befindlichen Container im gewünschten Zustand befinden.
  • Der Dienst kube-proxy dient den auf dem Node befindlichen Pods als einfacher Load Balancer und Netzwerk-Proxy.

Ein Pod besteht aus einem oder mehreren Containern. Pods stellen eine logische Gruppe für Container dar und stellen den einzelnen Containern die zugeteilten Ressourcen zur Verfügung. Dies ermöglicht, mehrere voneinander abhängige Container gemeinsam lauffähig zu halten. Pods können mittels ReplicaSets während der Laufzeit skaliert, also in ihrer Anzahl vergrößert oder verkleinert, werden. Pods sind die kleinste deploybare Einheit. Dabei wird die Konfiguration der einzelnen Kubernetes-Objekte über den Master abgewickelt. Dieser entscheidet anhand der konfigurierten Ressourcenanforderungen und der -verfügbarkeit auf den Nodes automatisch, auf welchem Node der Pod zur Verfügung gestellt wird. Der aktive Node lädt anschließend von der konfigurierten Container Image Registry das Image und koordiniert die weitere Inbetriebnahme des Containers.

Namespaces helfen dabei, unterschiedliche Benutzer, Projekte oder Umgebungen wie zum Beispiel produktiv und entwicklung voneinander zu trennen. Ein Namespace ist nur ein Präfix vor dem Namen einer Ressource und verhindert die Kollision von gleichen Objektnamen. Durch Namespaces kann es zum Beispiel jeweils einen Service backend-api geben, der mit den eben erwähnten Namespaces in produktiv:backend-api und entwicklung:backend-api unterschieden wird. In zukünftigen Kubernetes-Versionen werden Objekte eines Namespace standardmäßig die gleichen Access Control Policies erhalten, die anschließend modifiziert werden können.

Die ReplicaSets kümmern sich darum, dass zum initialen Start und während der Laufzeit immer die gewünschte Anzahl an Pods zur Verfügung steht. Standardmäßig ist ein Pod beziehungsweise ReplicaSet nicht von einem anderen Pod oder einem öffentlichen Interface aus zugänglich. Durch die Nutzung von Services können diese intern wie extern veröffentlicht werden.

Services: Dadurch, dass Pods zu jedem Zeitpunkt beendet werden können und nie wiederhergestellt, sondern durch das ReplicaSet dynamisch neu generiert werden, kann sich jederzeit die IP-Adresse des Pods verändern. Dies führt zu dem Problem, dass eine Gruppe von Pods, die zum Beispiel für die Datenbank zuständig ist, nicht über eine zentrale, gleichbleibende Adresse erreicht werden kann. Die Lösung dazu sind die Kubernetes Services. Diese erhalten eine feste Adresse, unter der sie erreichbar sind, und ermöglichen durch das Zuweisen von frei definierbaren Schlüssel-Wert-Paaren, sogenannten Labels, einen oder mehrere Pods mit einem Service zu verknüpfen. Hierbei wird dem Service der gewünschte Filter hinterlegt, wie zum Beispiel app:webseite, und alle Pods mit dem gleichen Label werden automatisch während der Laufzeit dem Service hinzugefügt. Zusätzlich bieten Services auch Load Balancing für die Pods, sodass nicht ein Pod auf einem Node alle Anfragen verarbeiten muss.

Labels sind Schlüssel-Wert-Paare, die an einzelne Objekte wie zum Beispiel Services oder Pods gebunden werden. Labels werden für die Organisation und Gruppierung von einzelnen Kubernetes-Objekten verwendet. Sie können zu jedem Zeitpunkt hinzugefügt, verändert oder ganz gelöscht werden. Ein Objekt muss kein Label erhalten, der Schlüssel hingegen muss pro Objekt gleich sein. In einem Namespace kann es zum Beispiel app:backend und app:frontend geben. Einem Objekt kann hierbei nur eines der beiden _app-_Labels hinzugefügt werden, es kann nicht beides sein.

Ein Volume ist im Grunde genommen ein Verzeichnis, das von einem Container verwendet werden kann. Dabei wird das Volume in den Container eingebunden (mount) und dient dort als persistenter Speicher für die Anwendungsdaten. Kubernetes unterstützt zurzeit 26 verschiedene Typen von Volumes, darunter unter anderem Cloud Storages wie Amazon Elastic Block Store, Azure Disk Storage und GCE Persistent Disk, aber auch andere Arten wie gitRepo, hostPath, iscsi und persistentVolumeClaim, um nur ein paar aus der Liste zu nennen.

Am einfachsten ist hierbei das hostPath Volume, das ein Verzeichnis oder eine Datei des Host-Node-Dateisystems mit dem Pod verbindet. Darüber können zum Beispiel statische HTML-Dateien zur Verfügung gestellt werden oder eine Konfigurationsdatei, die von einem Docker Image benötigt wird.

Ein Secret ist, wie der Name schon sagt, ein Geheimnis innerhalb von Kubernetes. Es kann viele unterschiedliche Informationen enthalten, etwa SSL-Zertifikate, SSH-Schlüssel oder Benutzernamen und Passwörter für die Authentifizierung. Diese Secrets sind standardmäßig nicht von den Pods aus zugänglich, sie müssen zum Beispiel über die Label einem Pod zugewiesen werden und können dort als Umgebungsvariable oder als Datei in einem zugewiesenen mountPath zur Verfügung gestellt werden.

Helm: Eine optionale Komponente, die nicht zur Grundausführung von Kubernetes gehört, hier aber dennoch eine kurze Erwähnung finden sollte, ist Helm. Helm ist ein Paketmanager für Kubernetes, um komplexe Anwendungen, Plug-ins oder andere Erweiterungen bequem installieren und aktualisieren zu können. Unter dem Namen Helm Charts oder Kubeapps findet man z. B. Grafana oder den Continuous-Delivery-Server GoCD.

Kubernetes auf einen Blick

Infografik: Kubernetes auf einen Blick

Deployment mit Kubernetes

In der heutigen Zeit erwarten viele Anwender, dass ihre Anwendungen durchgehend verfügbar sind. Von Zero Downtime, also keiner Ausfallzeit, ist hier oftmals die Rede. Darüber hinaus wird auch von Entwicklern erwartet, dass sie mehrfach täglich neue Versionen der Anwendungen zur Verfügung stellen können. Früher stand das im krassen Widerspruch, wodurch definierte Wartungsfenster mit Anwendern und Entwicklern erarbeitet werden mussten. Dank der verschiedenen Deployment-Verfahren (etwa der Rolling Updates) von Kubernetes können jedoch beide Anforderungen bequem unter einen Hut gebracht werden. Mit Kubernetes Deployments werden gewünschte Zustände eines oder mehrerer Objekte definiert, die durch den Controller sichergestellt werden.

Aber alles der Reihe nach. Zunächst einmal müssen wir auf unserem bestehenden Cluster einen Pod erstellen. Sofern noch kein eigener Cluster zu Testzwecken besteht, empfehlen wir eine lokale Minikube-Installation, die in wenigen Minuten heruntergeladen und installiert ist.

Zuerst erstellen wir einen Pod mit nginx-Server darin, der über den Port 80 erreichbar sein soll. Hierzu verwenden wir folgenden Kurzbefehl:

$ kubectl run demo-nginx --image nginx --port 80
deployment.apps "demo-nginx" created

Dieser erstellt automatisch ein vollständiges Deployment namens demo-nginx mit gleichnamigem Label, einem nginx Pod und dem dazugehörigen ReplicaSet. Um auf den erstellten nginx-Server auch außerhalb des Clusters zugreifen zu können, muss noch ein Service erstellt werden. Das ist bequem mit dem Befehl $ kubectl expose deploy demo-nginx zu erreichen.

Die entsprechend generierte .yaml-Datei sieht wie in Abbildung 1 aus und kann über den Befehl $ kubectl get deploy demo-nginx -o yaml als YAML (oder JSON) exportiert werden.

Abb. 1: Die generierte „.yaml“-Datei für das Deployment

Abb. 1: Die generierte „.yaml“-Datei für das Deployment

Die ersten beiden Zeilen definieren die apiVersion des Kubernetes-Clusters und den Typ der .yaml-Datei, in unserem Fall ein Deployment. Anschließend folgen die Metadaten, die den Namen des Deployments, der im Namespace einzigartig sein muss, eine interne UID und den Zeitpunkt, zu dem das Deployment erstellt wurde, enthalten.

Außerdem findet man die aktuelle generation (Zeile 7) des Deployments, die in unserem Fall die erste ist. Diese Zahl findet in Zeile 5 (den annotations) ihre Wiederverwendung. Mit dieser annotation stellt Kubernetes sicher, welches Deployment bzw. welche Deployment-Generation die aktuell richtige und lauffähige ist.

In den Zeilen 15 bis 47 unseres Deployments finden wir die spec, also die Spezifikation. Die spec besteht hierbei aus mindestens zwei Parametern: dem Parameter replicas, welcher die Anzahl der gewünschten Pods im Deployment definiert, sowie dem Parameter template, der den Inhalt des gewünschten Pods, in unserem Fall einen Container mit dem Image nginx, widerspiegelt.

Optionale Angaben sind die Parameter strategy, der das gewünschte Deployment-Verfahren definiert, sowie selector, der über die Labels definiert, welche Pods zum Deployment gehören und welche nicht. In unserer Datei wurden diese beiden optionalen Parameter durch Kubernetes erstellt und repräsentieren die Standardwerte. Zuletzt findet man ab Zeile 48 den status des aktuellen Deployments. Dieser kann nicht verändert werden und wird automatisch durch Kubernetes geschrieben; er dient ausschließlich der Information.

Rolling Updates mit Kubernetes

Nachdem wir nun ein lauffähiges nginx Deployment auf unserem Kubernetes-Cluster haben, können wir ein Update des Pods ohne Ausfallzeit initiieren: das sogenannte Rolling Update mit 0 Sekunden Ausfallzeit. Um diesen Vorgang besser zu verstehen, werfen wir erneut einen Blick auf unsere .yaml-Datei für das Deployment, die in den Zeilen 24 bis 25 die Parameter maxSurge sowie maxUnavailable enthält.

  • maxSurge: die Anzahl der Pods, die über dem aktuell erlaubten Limit von 1 Pod (replicas, Zeile 17) während eines Deployments erstellt werden dürfen.
  • maxUnavailable: Definiert die Anzahl der Pods, die während eines Deployments nicht verfügbar sein dürfen.

In der momentanen Konstellation würde bei einem Update der Konfiguration zeitgleich der bestehende Pod terminiert und ein neuer Pod erstellt werden, wodurch es zu einer Ausfallzeit kommt. Indem wir den Parameter maxUnavailable auf 0 reduzieren, verhindern wir dieses Verhalten. Kubernetes erstellt daher zunächst einen neuen Pod, welcher durch den maxSurge-Parameter bewilligt ist, anschließend wird der alte Pod terminiert, um das Deployment abzuschließen und die Anzahl der replicas erneut zu gewährleisten.

Um das live zu testen und sichtbar zu machen, lassen wir uns die aktuellen Pods anzeigen und übergeben das zum Beispiel dem watch-Befehl, der jede Sekunde den Inhalt automatisch aktualisiert (Abb. 2): $ watch -n 1 "kubectl get pods".

Abb. 2: Aktiver „watch“-Befehl mit der aktuellen Anzahl an Kubernetes Pods

Abb. 2: Aktiver „watch“-Befehl mit der aktuellen Anzahl an Kubernetes Pods

Anschließend modifizieren wir das aktuelle Deployment und benutzen statt des aktuellsten nginx ein früheres Image. Dazu verwenden wir folgenden Befehl, der die yaml-Datei unseres Deployments im lokalen Editor öffnet: $ kubectl edit deploy demo-nginx.

Im Editor modifizieren wir nun den erwähnten Wert maxUnavailable auf 0 und fügen dem image eine Versionsnummer hinzu. Die Datei sollte wie im Abbildung 3 aussehen.

Abb. 3: Änderungen in der Deployment-Datei können im Betrieb durchgeführt werden und verursachen eine neue Deployment-Revision

Abb. 3: Änderungen in der Deployment-Datei können im Betrieb durchgeführt werden und verursachen eine neue Deployment-Revision

Nachdem die Änderungen durchgeführt wurden, speichern und schließen wir die Datei und beobachten in unserem zweiten Konsolenfenster, wie Kubernetes zuerst das neue Deployment erstellt und nach Abschluss das alte Deployment terminiert (Abb. 4 und 5).

Abb. 4: Die veränderte Konfiguration wird wie gewünscht ausgeführt; zunächst wird ein neuer Pod erstellt ...

Abb. 4: Die veränderte Konfiguration wird wie gewünscht ausgeführt; zunächst wird ein neuer Pod erstellt ...

Abb. 5: ... und anschließend der veraltete Pod terminiert

Abb. 5: ... und anschließend der veraltete Pod terminiert

Die Änderung des Deployments wird von Kubernetes in einer History protokolliert und kann nun bequem mehrfach wiederholt werden. Wenn wir den Befehl $ kubectl rollout history deploy demo-nginx ausführen, sollten wir zwei Revisionen unseres Deployments sehen. Mit dem Befehl undo können wir zur letzten Konfiguration springen oder mit dem optionalen Parameter --to-revision zu einer gewünschten anderen. In jedem Fall wird die History fortgesetzt und die erneute Änderung wird in einer neuen Revision festgehalten: $ kubectl rollout undo deploy demo-nginx [--to-revision=3].

Neben den Rolling Updates gibt es auch noch weitere Deployment-Verfahren, die durch Kubernetes standardmäßig unterstützt werden. Dazu gehören die folgenden:

  • blue/green erstellt ein Deployment neben dem bereits bestehenden Deployment und leitet anschließend den Traffic um.
  • _canary s_tellt ein neues Deployment einer eingeschränkten Useranzahl zur Verfügung, bevor es alle erhalten.
  • ramped erstellt wie unser eben erwähntes Rolling Update einen Pod nach dem anderen neu, der alte Pod wird terminiert.
  • recreate beendet den bestehenden Pod, bevor der neue Pod erstellt wird.

Die Kommunikation in Kubernetes

Ein Pod kann aus einem oder mehreren Containern bestehen und ist die kleinste nicht teilbare Einheit in Kubernetes, d. h., die Container eines Pods laufen in Kubernetes immer auf demselben Worker-Knoten und teilen sich Ressourcen wie den Netzwerkstack. Im Folgenden betrachten wir, wie die Kommunikation in Kubernetes erfolgt.

Zuerst erstellen wir einen Pod, der aus zwei Containern besteht. Hierzu wird eine neue Datei kreiert, um sie später Kubernetes zu übergeben. Dazu verwenden wir das in **Abbildung 6 **dargestellte Beispiel.

Abb. 6: „.yaml“-Datei, bestehend aus einem Pod mit zwei Containern

Abb. 6: „.yaml“-Datei, bestehend aus einem Pod mit zwei Containern

Der Beispiel-Pod besteht aus den Containern nginx und c_entos_. Mit kubectl get pods -o wide kann man den Zustand des Pods prüfen und die IP-Adresse des Pods einblenden (Abb. 7).

Abb. 7: Übersicht des Pods mit zwei Containern darin

Abb. 7: Übersicht des Pods mit zwei Containern darin

Man sieht, dass beide Container aktiv sind und der Pod die IP-Adresse 100.96.1.7 erhalten hat. Führt man in beiden Containern das Kommando ipconfig aus, sieht man in beiden Containers das identische Interface (Hardware und IP-Adresse), wie in Abbildung 8 gezeigt.

Abb. 8: Beide Container sprechen über das gleiche Netzwerkinterface

Abb. 8: Beide Container sprechen über das gleiche Netzwerkinterface

Beide Container sind über das gleiche Interface an die Außenwelt angeschlossen, und die Container eines Pods können auf die anderen Container des Pods via localhost zugreifen. Aus dem CentOS-Container heraus kann z. B. via curl http://localhost der nginx-Webserver im anderen Container angesprochen werden (Abb. 9 und 10).

Abb. 9: Ein „curl“ auf „localhost“ aus CentOS erreicht den nginx-Container im gleichen Pod

Abb. 9: Ein „curl“ auf „localhost“ aus CentOS erreicht den nginx-Container im gleichen Pod

Abb. 10: Aufbau des Pods und der Netzwerkinterfaces

Abb. 10: Aufbau des Pods und der Netzwerkinterfaces

Der hier betrachtete Kubernetes-Cluster verwendet Docker als Container-Runtime und folglich kann das Docker-Kommando verwendet werden. Der nginx Pod aus dem obigen Beispiel besteht, wie in Abbildung 11 zu sehen ist, aus den Containern centos, nginx und pause.

Abb. 11: Im Hintergrund besteht der Pod aus drei Containern; Ein „pause“-Container wird als Hilfskonstrukt erstellt

Abb. 11: Im Hintergrund besteht der Pod aus drei Containern; Ein „pause“-Container wird als Hilfskonstrukt erstellt

Der pause-Container ist ein Hilfskonstrukt von Kubernetes, dessen Aufgabe es ist, das virtuelle Netzwerkinterface im Pod zur Verfügung zu stellen. Andere Aufgaben hat dieser Container nicht. Pod-interne Kommunikation ist, wie in Abbildung 10 gezeigt, über veth0 möglich. Das virtuelle Netzwerkinterface ist wiederum mit der Ethernet Bridge cni0 verbunden (Abb. 12). Darüber ist die Kommunikation mit anderen Pods auf demselben Kubernetes-Knoten möglich.

Abb. 12: Die Verbindung zwischen Pods erfolgt über das „cniO“

Abb. 12: Die Verbindung zwischen Pods erfolgt über das „cniO“

In Kubernetes erhält jeder Pod eine eigene IP-Adresse, und die Pod-zu-Pod-Kommunikation erfolgt über das integrierte Containernetzwerk. Über das Container Network Interface (CNI) können unterschiedliche Netzwerklösungen in Kubernetes eingeklinkt werden. Es gibt folgende Ansätze, wie ein Containernetzwerk umgesetzt werden kann: Overlay, Underlay und Layer 3.

Im Folgenden wird auf die weit verbreitete Lösung flannel eingegangen, die einen Overlay-Ansatz verfolgt und ein virtuelles Netzwerk auf dem bereits existierenden aufbaut. Hierbei setzt flannel auf VXLAN, d. h. zwischen den Kubernetes-Knoten wird ein Overlay-Netzwerk basierend auf VXLAN-Tunneln aufgebaut. Der Einsatz von Tunneln hat zur Folge, dass die Pakete, die durch einen Tunnel geleitet werden, gekapselt werden müssen. Die Netzwerklösung ist verantwortlich für:

  • das Erstellen und Löschen der Netzwerkinterfaces,
  • das Zuweisen und Freigeben der IP-Adressen (das kann optional auch über andere Plug-ins wie DHCP erfolgen),
  • die Anbindung der Pods an das Netzwerk,
  • das Ein- und Auspacken der Pakete etc.

Abb. 13: Aufbau zwischen zwei Nodes, auch Worker genannt, mit je einem Pod

Abb. 13: Aufbau zwischen zwei Nodes, auch Worker genannt, mit je einem Pod

flannel.1 ist ein VXLAN-Tunnelinterface, das die Pakete an den flannel Daemon (flanneld) weiterreicht. Der flannel Daemon übernimmt das Ein- und Auspacken der Pakete. In Abbildung 13 schickt der Pod auf Worker 1 ein Paket an den Pod auf Worker 2. Das den Pod verlassende Paket hat die 100.96.2.33 als Quelladresse und die 100.96.1.7 als Zieladresse. Der flannel Daemon nimmt das IP-Paket und erstellt daraus ein VXLAN-Paket, d. h. es fügt einen UDP- und einen IP-Header an. Das neue Paket hat eine neue Ziel- (10.0.49.156) und Quelladresse (10.0.49.252). Das Wissen, wohin ein Paket geschickt werden muss, bezieht flannel aus dem Key Value Store (etcd). Darin speichert Flannel, welches Subnetz auf welchem Kubernetes-Knoten läuft und kann so die korrekten Pakete bauen. Auf der Gegenseite entfernt Flannel die für den Tunnel notwendigen Headerdaten.

Service, Service Discovery und externer Zugriff

Pods gelten als kurzlebige Komponenten, die auf Dauer nicht stabil adressierbar sind. Wird ein Kubernetes-Knoten neu gestartet, erhalten die betroffenen Pods nach dem Neustart eine neue IP-Adresse. Um dieses Problem zu lösen, gibt es in Kubernetes Services und für die Service Discovery einen integrierten DNS-Server. Im Folgenden wird das Konzept anhand eines nginx Deployments erklärt.

Abb. 14: Zwei nginx Pods mit je einem Container

Abb. 14: Zwei nginx Pods mit je einem Container

Wie in Abbildung 14 dargestellt, besteht das nginx Deployment aus zwei Pods. Für diese Pods erstellen wir im nächsten Schritt einen Service. Im Service Manifest (Abb. 15) wird festgelegt, dass

  • alle Pods mit dem Label run:nginx Bestandteil des Service sind,
  • der Service über TCP Port 80 angeboten wird,
  • die Lastverteilung Random oder in Form einer Sticky Session durchgeführt werden kann sowie
  • der Service eine interne IP-Adresse erhält und somit nicht von außen erreichbar ist (type: ClusterIP).

Abb. 15: Beispielhafte „.yaml“-Service-Datei, die alle Pods mit dem Label „run:nginx“ über Port 80 veröffentlicht

Abb. 15: Beispielhafte „.yaml“-Service-Datei, die alle Pods mit dem Label „run:nginx“ über Port 80 veröffentlicht

Abb. 16: Die Namensauflösung funktioniert innerhalb des Clusters über den Service-Namen

Abb. 16: Die Namensauflösung funktioniert innerhalb des Clusters über den Service-Namen

In Kubernetes erfolgt die Service Discovery über DNS (Abb. 16). Für jeden Service wird ein Eintrag, ein sogenannter „A Record“, im Kubernetes-internen DNS-Server angelegt und Kubernetes sorgt dafür, dass die DNS-Konfiguration der Pods auf den internen DNS-Server zeigt. Im Beispiel des nginx Service löst der Name nginx.default.svc.cluster.local auf die Service IP 100.71.152.0 auf. Über diese IP werden alle Pods gemäß dem gewählten Lastverteilungsverfahren angesprochen.

Im Beispiel ist der nginx Service nicht von außen erreichbar. Möchte man dies, gibt es die Möglichkeiten Ingress, Load Balancer und NodePort.

Sicherheit

Die folgenden Aspekte sollten bei der Installation und dem Betrieb von Kubernetes-Clustern berücksichtigt werden. Die Kommunikation mit Kubernetes Master, Key Value Store und Kubelet sollte ausschließlich verschlüsselt und authentifiziert erfolgen. Weiter bietet die integrierte rollenbasierte Zugriffskontrolle die Möglichkeit, maßgeschneiderte Berechtigungskonzepte umzusetzen, d. h. es kann feingranular festgelegt werden, was ein Benutzer mit welchen Ressourcen eines Clusters machen kann. Für die Nachvollziehbarkeit bietet Kubernetes ein Audit-Log; über die Policy wird konfiguriert, welche Ereignisse festgehalten werden sollen. Eine Pod Security Policy bietet die Möglichkeit, Anforderungen zu definieren, die ein Pod erfüllen muss, um ausgeführt werden zu können. Die in Abbildung 17 dargestellte Pod Security Policy verhindert die Ausführung privilegierter Container. Im Rahmen einer Pod Security Policy können viele weitere Aspekte eingestellt werden.

Abb. 17: Mit Pod Security Policies können sicherheitsrelevante Funktionen (de-)aktiviert werden

Abb. 17: Mit Pod Security Policies können sicherheitsrelevante Funktionen (de-)aktiviert werden

Initial können innerhalb eines Kubernetes-Clusters alle Pods miteinander kommunizieren. Möchte man dies nicht, kann über Network Policies festgelegt werden, wie Pods untereinander und mit anderen Endpunkten kommunizieren dürfen. Voraussetzung hierfür ist, dass die ausgewählte Netzwerklösung das unterstützt (z. B. Calico, Weave Net). Network Policies können zum Beispiel verwendet werden, um Namespaces netzwerktechnisch voneinander zu trennen.

Aktuelle Themen und Ausblick

Kubernetes erlangt in der Containerorchestrierung und als Cloud-natives Anwendungsmanagement eine immer größere Bedeutung. Während es viele alternative Plattformen für die Orchestrierung und als PaaS (Plattform as a Service) gibt, ist Kubernetes durch die immer größer werdende Community und das von Google erschaffene Ökosystem einer der Topfavoriten auf dem Markt.

Ein wichtiger Treiber rund um Kubernetes ist die Cloud Native Computing Foundation (CNCF), deren Zielsetzung es ist, einen Cloud-nativen Stack bereitzustellen, der Cloud-Portabilität ohne Vendor Lock-in ermöglicht. In diesem Konzept spielt Kubernetes eine zentrale Rolle, denn die CNCF definiert Cloud-nativ wie folgt:

  • Cloud-nativ ist ein containerbasierter Ansatz, alle Anwendungen und Prozesse werden also in einem Container ausgeführt.
  • Die Container werden dynamisch orchestriert, d. h. es findet ein aktives Scheduling statt, das die aktuellen Gegebenheiten berücksichtigt.
  • Anwendungen folgen dem Architekturmodell der Microservices, d. h. komplexe Anwendungen werden effizient modularisiert.

Wie wichtig das Thema Kubernetes ist, lässt sich auch daran erkennen, dass alle Hyperscaler (bei Google ist der Grund offensichtlich) Mitglied der CNCF sind und alle einen Managed Kubernetes Service anbieten. Service Meshes sind ein weiteres aufkommendes Thema im Dunstkreis von Microservices. Istio und Linkerd sind dabei die bekanntesten Kandidaten und adressieren die Aspekte Traffic Management, Sicherheit und bessere Überwachbarkeit. Aber auch Kubernetes hat seine Grenzen und Stärken, die wir nicht unerwähnt lassen möchten.

Die Unterstützung für sogenannte Stateful Applications, wie sie zum Beispiel Datenbanken sind, ist weiterhin noch nicht perfekt. Gerade in komplexeren Datenbankszenarien mit Replikation kann es hier zu Schwierigkeiten kommen. Positiv anzumerken ist aber, dass daran stetig weiterentwickelt wird und es mit jedem neuen Release von Kubernetes besser wird.

Auch ist die Fragmentierung der für Kubernetes entwickelten Plug-ins und Erweiterungen oft ein Hindernis. Viele Projekte wurden gestartet und liefen mit älteren Versionen von Kubernetes. In der Zwischenzeit wurde das API von Kubernetes aktualisiert, wodurch viele Plug-ins nicht mehr funktionieren oder mühsam an die aktuelle Version angepasst werden müssen. Hier kann man leicht den Überblick verlieren und hat Mühe, das beste und richtige Programm für die Produktivumgebung zu finden.

Zu den absoluten Stärken gehört der große Support von unterschiedlichen Plattformen, die an Kubernetes-Cluster angebunden und auf denen sie betrieben werden können. Ob bekannte Cloud-Provider wie AWS, Azure, GCP oder der alte Windows-Rechner im Arbeitszimmer, Kubernetes kann fast überall betrieben werden.

Allerdings sollte man stets bedenken: Nur weil man Kubernetes hat, bedeutet es nicht, dass man alles in Kubernetes abbilden und betreiben muss.

Mehr zu Kubernetes

In unserem Video-Tutorial Kubernetes Grundlagen geht Kubernetes-Experte Thomas Kruse auf alle wichtigen Funktionen und Verfahrensweise von und mit Kubernetes ein. In über dreieinhalb Stunden lernst du die Verwendung von Docker kennen, richtest einen lokalen Kubernetes Cluster auf deinem PC oder Notebook ein und erarbeitest dir die Möglichkeiten des Kubernetes API, um eigene Anwendungen in der Cloud bereitzustellen.

Dr. Andreas Hess

Dr. Andreas Hess arbeitet seit über zehn Jahren in der IT. Sein spezielles Interesse gilt innovativen Technologien wie Kubernetes und der Erstellung geeigneter Systemarchitekturen. Aktuell ist er als Cloud-Architekt bei NTT DATA Deutschland GmbH bundesweit unterwegs.

Thomas Schwärzl

Thomas Schwärzl ist als Lead Engineer Cloud Services bei der NTT DATA Deutschland GmbH tätig und betreut in unterschiedlichen Branchen die Einführung von OpenShift oder Kubernetes. Zuvor plante und leitete er mehrere Jahre Migrationen sowie Inbetriebnahmen von Rechenzentren. Seinen Ausgleich zur IT findet er im Kampf gegen die Zeit beim Triathlon.


Weitere Artikel zu diesem Thema