Die Grundbegriffe von Kubernetes

the next big thing Golo Roden  –  22 Kommentare

Wer sich erstmals mit Kubernetes beschäftigt, steht einer Vielzahl von Begriffen gegenüber: Pod, Service, Deployment, Ingress-Controller, Network-Policy und so weiter. Welche Begriffe sollte man zu Beginn kennen?

Kubernetes ist eine Plattform, um Docker-Images in einem Cluster verteilt ausführen zu können. Aus Sicht einer Entwicklerin oder eines Entwickler verhält sich Kubernetes dabei wie ein einziger großer Server, der – ja nach Art und Anzahl der eingesetzten Maschinen – über sehr viele CPUs, RAM und Speicherkapazität verfügt. Man könnte sagen, dass im Fall von Kubernetes das Netzwerk der Computer ist, mit all seinen angeschlossenen Knoten.

Wie Docker arbeitet Kubernetes dabei nach dem Client-Server-Prinzip, wobei die Server in Kubernetes nochmals in zwei Kategorien unterschieden werden: Die Master-Server, von denen es in der Regel drei oder fünf gibt, dienen der zentralen Steuerung und Verwaltung des Clusters, die Worker-Server übernehmen die eigentliche Arbeit, Docker-Container auszuführen. Zur Konfiguration von außen stellt Kubernetes eine API zur Verfügung, die entweder direkt über HTTPS oder sich mit dem Kommandozeilenwerkzeug kubectl ansprechen lässt.

Vor knapp drei Wochen war in der Fachpresse zu lesen, Kubernetes beende den Support für Docker. Um derartige Schlagzeilen richtig einordnen zu können, muss man zunächst wissen, dass Kubernetes anfangs tatsächlich lediglich eine Abstraktionsschicht über Docker war. Es gab jedoch früh Bemühungen, auch alternative Container-Werkzeuge zu integrieren und einen gemeinsamen Standard zu schaffen, das Container Runtime Interface (CRI). Leider ist Docker bis heute nicht zu CRI kompatibel.

Daher enthält Kubernetes den sogenannten Docker-Shim, quasi einen Adapter, der es ermöglicht, dass Kubernetes sich nur mit CRI befassen muss, und der die Aufrufe auf der anderen Seite in Aufrufe für die Docker-Engine übersetzt. Docker selbst ist allerdings auch wieder nur eine Abstraktion über die Dienste containerd und runc, wobei containerd intern auf runc zugreift. Da containerd kompatibel zu CRI ist, wurde für Kubernetes entschieden, dass man zukünftig direkt mit containerd interagieren wird, statt den Umweg über Docker und den Docker-Shim zu gehen.

Da das Format der Docker-Images, der Dockerfiles und von ähnlichen Artefakten allerdings ebenfalls standardisiert ist, dürfte sich für die wenigsten Anwenderinnen und Anwender von Kubernetes überhaupt etwas spürbar verändern. Anpassungen sind allenfalls erforderlich, wenn man das Hosting für Kubernetes selbst betreibt oder wenn man sehr ausgefallene Konstrukte wie "Docker in Docker" oder den direkten Zugriff auf den Docker-Socket verwendet.

Was ist Kubernetes?

Pods und Services

Die kleinste Einheit in Kubernetes sind dabei übrigens anders als in Docker nicht die einzelnen Container, sondern Pods. Ein Pod ist dabei nichts anderes als ein Behälter, der einen oder mehrere Container beherbergen kann, die gemeinsam verwaltet werden sollen. Man startet Container in Kubernetes also nicht direkt, sondern stets über einen Pod. Dabei wird dem Pod irgendeine verfügbare IP-Adresse des Clusters zugewiesen, unter der die im Pod enthaltenen Container anschließend erreichbar sind.

Selbstverständlich lässt sich ein Pod auch mehrfach starten, um eine Anwendung zu skalieren. Allerdings stellt sich dann die Frage, wie man diese Pods erreicht, da sie jeweils auf einer zufällig zugewiesenen IP-Adresse ausgeführt werden. Außerdem würde man für das Szenario einen Load-Balancer benötigen, der zu jedem Zeitpunkt Kenntnis darüber hat, welcher Pod erreichbar ist und welcher nicht.

Genau für diesen Zweck verfügt Kubernetes über Services. Ein Service hat eine stabile benannte Adresse, die über KubeDNS (das Kubernetes-eigene und interne DNS-System) aufgelöst werden kann. Pods lassen sich Services zuweisen, und Services halten die Liste der zugeordneten und verfügbaren Pods automatisch aktuell. An den Service eingehende Anfragen werden automatisch auf die verschiedenen Pods weitergeleitet. Das funktioniert, allerdings ohne weitere Konfiguration nur innerhalb des Clusters.

Der Grund dafür ist, dass ein Service für unterschiedliche Betriebsarten konfiguriert werden kann. Der Standard, der als Cluster-IP bezeichnet wird, stellt den Service lediglich über eine Cluster-interne IP-Adresse bereit. Die Einstellung "Node-Port" hingegen ermöglicht den Zugriff auf den Service auch von außerhalb des Clusters, bewirkt aber, dass auf jedem Worker-Server der gewünschte Port für ebendiesen Service blockiert wird. Auf dem Weg ist es also beispielsweise nicht möglich, verschiedene Dienste über einen gemeinsamen Standardport zu betreiben.

Das lässt sich dann mit Hilfe der Einstellung "Load-Balancer" bewirken, was allerdings nur für Kubernetes-Cluster funktioniert, die auf einem Cloud-Fundament betrieben werden. In diesem Fall wird dem Service nämlich ein Load-Balancer der zugrunde liegenden Plattform vorgeschaltet, beispielsweise ein Elastic Load Balancing im Kontext von AWS. Da jeder Load-Balancer über eine eigene, von außen erreichbare IP-Adresse verfügt, können auf dem Weg unterschiedliche Services auf demselben Port angeboten werden. Der Nachteil dabei ist allerdings, dass derartige Load-Balancer schnell teuer werden – zumal für jeden Service ein eigener Load-Balancer benötigt wird.

Pods und Services

Deployments und Ingress

Insgesamt gilt es in diesem Zusammenhang, drei Probleme zu lösen. Erstens starten Pods nicht automatisch neu, wenn sie abstürzen. Stattdessen muss man sich von Hand um deren Lebenszyklus kümmern. Abhilfe schafft hier das Konzept des Deployments, das die ihm zugeordneten Pods überwacht und sie bei Bedarf neu starten kann. Der Service ist allerdings nicht Bestandteil des Deployments, er muss gesondert verwaltet werden.

Besonders praktisch ist, dass Deployments auch das Skalieren und das Aktualisieren der Pods übernehmen können, wobei sogar Rolling-Updates in verschiedenen Strategien unterstützt werden. Das ermöglicht sogenannte Zero-Downtime-Deployments und ist ein wichtiges Mittel, bei denen Anwendungen kontinuierlich und ohne Unterbrechung betrieben werden.

Das zweite zu lösende Problem betrifft den Umstand, dass mit dem bisherigen Wissen für jeden Service ein eigener Load-Balancer benötigt wird. Abhilfe schaffen hier die Ingress-Ressourcen, mit denen man einen eingehenden Netzwerk-Endpunkt für einen Service definieren kann. Damit ein Ingress allerdings auch tatsächlich einen Effekt hat, benötigt man zusätzlich einen sogenannten Ingress-Controller, wofür man beispielsweise Nginx einsetzen kann. Der Controller übernimmt dann die Rolle eines zentralen Endpunkts, mit dem sich beispielsweise auch TLS-Terminierung umsetzen lässt.

Das dritte Problem betrifft die Isolation der einzelnen Pods auf Netzwerkebene. Logisch lässt sich das in Kubernetes mit Hilfe von Namespaces lösen, doch eine echte Isolation erreicht man erst durch den Einsatz von Network Policies. Ähnlich zu Ingress-Ressourcen bedarf es aber auch hier einer zusätzlichen Instanz, die die konfigurierten Regeln durchsetzt. Dabei handelt es sich in dem Fall um ein CNI-Plugin, wobei CNI für Container Network Interface steht.

Deployments und Ingress

ConfigMaps, Secrets und Volumes

Auch für die Konfiguration von Anwendungen bietet Kubernetes geeignete Konstrukte. Mit ConfigMaps stehen zum einen Key-Value-Speicher zur Verfügung, die entweder als Umgebungsvariablen oder als Dateien in die Pods eingehängt werden können. Zum anderen gibt es für vertrauenswürdige Daten wie Zugangsdaten, Tokens und ähnliches Secrets, die den ConfigMaps sehr ähnlich sind, aber zusätzliche Sicherheit versprechen. Derzeit besteht die zwar lediglich in einem Base64-Encoding, für die Zukunft sind aber Ideen wie die Rotation der Daten und die Unterstützung von Hardware Security Modules (HSM) im Gespräch.

Das Einbinden von Dateien (und Verzeichnissen) ist aber nicht nur mit Hilfe von ConfigMaps möglich. Wie bei Docker können auch Dateien und Verzeichnisse des Hostsystems als Volume eingebunden werden. Was bei Docker sinnvoll sein kann, ist bei Kubernetes allerdings wenig ratsam, da ein Pod jederzeit auf einen anderen Worker-Server verschoben werden kann, weshalb die Bindung an den Host fehlt.

Interessanter ist daher die Verwendung von Persistent Volumes, die im Netzwerk zur Verfügung gestellten Laufwerken entsprechen. Sie können über verschiedene Protokolle wie beispielsweise NFS oder iSCSI angebunden werden. Eine entsprechende Plattform als Grundlage vorausgesetzt, können diese Speicherorte sogar dynamisch von der Anwendung selbst angefordert und angelegt werden, wozu man in Kubernetes Storage Classes definieren kann, die verschiedene Qualitäten von Speichern repräsentieren. Insgesamt findet sich das Konzept der dynamischen Erzeugung allerdings eher in Cloud-Umgebungen.

ConfigMaps und Volumes

Managed Kubernetes

Das wirft schließlich die Frage auf, ob sich der Betrieb eines eigenen Kubernetes-Clusters rentiert oder ob man nicht besser auf eine schlüsselfertige Lösung zurückgreift, die von einem Cloud-Anbieter verwaltet wird. Das wird inzwischen von allen großen Anbietern zur Verfügung gestellt, beispielsweise EKS auf AWS oder AKS auf Azure.

Der große Vorteil dabei ist, dass man sich um Aktualisierungen und Sicherheitsupdates nicht kümmern muss, da das vom Hoster übernommen wird. Da Kubernetes zudem eine vom Hersteller unabhängige Abstraktionsschicht darstellt, verbaut man sich auf dem Weg auch keinen Wechsel zu einem anderen Anbieter. Allerdings ist das alles natürlich eine Frage des Preises, denn das Managed-Hosting ist in vielen Fällen teurer als das Hosting selbst zu übernehmen.

Es läuft letztlich die Entscheidung auf die Frage hinaus, ob man über die Ressourcen und die Kompetenz verfügt, ein Cluster selbst auf einem ähnlich hohen Niveau zu betreiben wie das die professionellen Anbieter machen. Sofern Kubernetes nicht zum Kerngeschäft gehört, ist die Frage höchstwahrscheinlich eher mit "nein" zu beantworten. In solchen Fällen fährt man mit einer verwalteten Lösung in der Regel besser.

Managed Kubernetes

Fazit

Kubernetes ist eine hervorragende Plattform für das Deployment und den Betrieb von Web- und Cloud-Anwendungen, sofern man diese in Docker-Images verpackt. Der Einstieg fällt tatsächlich gar nicht so schwer wie es die vielen Begriffe zunächst vermuten lassen. Man kann sich an Hand der in diesem Artikel vorgestellten Begriffe gut von einem zum nächsten vorarbeiten, wodurch man in kurzer Zeit zu einer guten Plattform für den Betrieb von Webservern und anderen Diensten kommt.

Die wichtigste Entscheidung ist letztlich die Frage, ob man ein Cluster selbst aufbauen, betreiben und warten möchte, oder ob man das anderen überlässt und man das Cluster lediglich als fertige Dienstleistung von der Stange einkauft einschließlich der dafür notwendigen Verwaltung. Sofern der Betrieb und die Verwaltung von Servern und Rechenzentren nicht die Kernkompetenz darstellen, ist der Einsatz einer Managed-Lösung vermutlich die deutlich bessere Wahl.