Kubernetes ist aus der Container-Orchestrierung nicht mehr wegzudenken, und seine Beliebtheit nimmt weiterhin nicht ab. Das bedeutet jedoch nicht, dass die Entwicklung im Bereich der Container-Orchestrierung stillsteht. In diesem Blogbeitrag zeigen wir einige Gründe auf, warum insbesondere Kubernetes-Nutzer und Entwickler über das traditionelle Kubernetes hinausblicken sollten, das wir in den vergangenen Jahren kennengelernt haben – hin zu Paradigmen, die sich möglicherweise besser für Cloud-Native-Anwendungen eignen.
Michael Vittrup Larsen
Michael is a Consultant at our office in Aarhus. He has an MScEE from Aalborg University where he specialized in digital signal processing. Before joining us Michael was working on building and optimizing cloud infrastructure for the telecoms sector. In his spare time, he likes to explore the wilderness on his mountain bike.
Der Aufstieg von Kubernetes
Ein Grund für die große Beliebtheit von Kubernetes ist, dass es auf Docker aufbaut. Container haben in Linux- und BSD-Varianten eine lange Geschichte. Docker machte Container jedoch durch den Fokus auf die User Experience extrem populär und vereinfachte das Erstellen und Ausführen von Containern erheblich. Kubernetes baute auf der Popularität von Containern auf und erleichterte das Ausführen – also Orchestrieren – von Containern auf einem Cluster von Compute-Nodes.
Ein weiterer Grund für die Beliebtheit und breite Akzeptanz von Kubernetes ist, dass es das Modell für den Betrieb von Software nicht grundlegend verändert hat. Es war relativ einfach, sich einen Weg von der Art, wie wir Software vor Kubernetes betrieben haben, hin zum Betrieb von Software auf Kubernetes vorzustellen.
Alten Paradigmen kann man keine neuen Tricks beibringen
Container-Images zu erstellen, um Abhängigkeiten und ein „überall ausführbar“-Erlebnis einzufrieren, ist in Kombination mit Kubernetes-Deployment-Ressourcenspezifikationen zur Orchestrierung von Container-Replikaten sehr leistungsfähig. Es unterscheidet sich jedoch nicht grundlegend von der Art, wie wir VMs vor Docker und Kubernetes betrieben haben. Der geringe gedankliche Umstieg machte die Einführung von Kubernetes einfach, ist aber auch der Grund, warum wir über das „traditionelle“ Kubernetes hinausblicken sollten, das wir heute kennen.
Dieser Blog betrachtet die Zukunft von Kubernetes aus Entwicklersicht. Grundsätzlich wird das Kubernetes, das wir heute kennen, verschwinden – und Entwickler wird das nicht interessieren. Das bedeutet nicht, dass Kubernetes nicht Teil unseres Stacks sein wird. Wir werden jedoch durch neue Abstraktionen, die selbst auf Kubernetes aufbauen, die Art verbessern, wie wir Anwendungen entwickeln und betreiben. Anwendungen werden mit Plattformen entwickelt, die auf der Kubernetes-Plattform aufbauen:
Interessanterweise war Linux vor zehn oder mehr Jahren die Plattform, auf der wir alles aufgebaut haben. Linux ist noch immer allgegenwärtig und Teil unseres Stacks, aber nur wenige Entwickler beschäftigen sich intensiv damit, weil wir inzwischen einige Abstraktionen darauf aufgebaut haben. Dasselbe wird mit dem traditionellen Kubernetes passieren, das wir heute kennen.
Neue Paradigmen schaffen gründlich(er) auf
Sicherheit: OIDC ist besser als Secrets
Kubernetes bietet eine Secret-Ressource, um statische Secrets wie API-Keys, Passwörter usw. festzulegen. Entwickler sollten keine Kubernetes-Secret-Ressourcen verwenden.
Explizite Secrets, die in Secret-Ressourcen gespeichert sind, können geleakt werden und lassen sich nur umständlich rotieren und widerrufen. Bei GitOps-Workflows benötigen Secrets außerdem besondere Aufmerksamkeit, damit sie nicht im Klartext gespeichert werden. Anwendungen sollten stattdessen einen rollenbasierten Ansatz für Authentifizierung und Autorisierung verfolgen. Das bedeutet: Statt auf „Dinge, die wir wissen“ – Passwörter, API-Keys – sollten Authentifizierung und Autorisierung von Anwendungen darauf basieren, „wer wir sind“.
Starke Identitäten sind die Grundlage jeder Sicherheit. Es ergibt keinen Sinn, Netzwerkverkehr zu verschlüsseln, wenn ihr euch der Identität des Servers, mit dem ihr kommuniziert, nicht sicher seid. Genau dafür sorgen Zertifikate und Zertifizierungsstellen bei HTTPS-Verkehr, der das Internet im Allgemeinen absichert.
Kubernetes verfügt über ein System für starke Workload-Identitäten. Alle Workloads sind Service Accounts zugeordnet und erhalten kurzlebige OpenID-Connect-(OIDC)-Identity-Tokens, die von Kubernetes ausgestellt werden. Der Kubernetes-API-Server signiert diese OIDC-Tokens, und andere Workloads können sie über den Kubernetes-API-Server validieren. So entstehen starke Identitäten für Workloads auf Kubernetes, die als Grundlage für rollenbasierte Authentifizierung und Autorisierung dienen können.
Statt Kubernetes Secrets zu verwenden, sollten Entwickler Authentifizierung und Autorisierung auf OIDC-Tokens aufbauen. Das bedeutet: Statt beispielsweise ein Datenbankpasswort in einer Secret-Ressource zu speichern, sollten wir sicherstellen, dass unsere Datenbank nur Anfragen mit einem gültigen, nicht abgelaufenen Token akzeptiert.
Beispiele für die Nutzung von OIDC-Tokens zur Integration externer Systeme sind AWS-IAM-Rollen für Service Accounts und Hashicorp Vault Kubernetes Auth.
Networking: Ingress reicht nicht aus
Kubernetes bietet eine Ingress-Ressource, mit der sich festlegen lässt, wie HTTP-Traffic an Workloads weitergeleitet wird. Wie Tim Hockin, Mitgründer von Kubernetes, einräumt, gibt es viele Probleme mit der Ingress-Ressource. Das Hauptproblem ist, dass sie uns nur die grundlegenden Funktionen für das Routing von HTTP-Traffic bereitstellt. Wenn Entwickler Ingress-Ressourcen verwenden dürfen, wird das für Infrastruktur- und Site Reliability Engineering (SRE)-Teams, die eine umfangreiche Infrastruktur vernetzen und zuverlässig betreiben müssen, zum Problem. Die Ingress-Ressource ist zu einfach, und Entwickler sollten sie nicht zur Konfiguration des Netzwerks verwenden.
Der Bedarf an mehr Kontrolle und Programmierbarkeit im Kubernetes-Netzwerk zeigt sich am Aufstieg von Service Meshes (siehe unsere Schulung zu Istio Service Mesh, Kiali und Jaeger). Sie teilen die Ingress-Ressource in mehrere Ressourcen auf, um Verantwortlichkeiten besser zu trennen, und bieten zusätzliche Funktionen für Routing, Observability, Sicherheit und Fehlertoleranz.
Immer mehr Abstraktionen auf Kubernetes setzen ein programmierbares Netzwerk voraus, das über die Möglichkeiten von Ingress hinausgeht, etwa Knative, Kubeflow und Continuous-Deployment-Tools wie Argo Rollouts. Das unterstreicht, dass ein robusteres Netzwerkmodell in Kubernetes bereits ein De-facto-Standard ist.
Die Kubernetes-Community hat ein „Ingress v2“ entwickelt: die Gateway API. Sie löst zwar einige der Probleme von Ingress, deckt aber nur einen kleinen Teil der Funktionen ab, die die meisten Service Meshes unterstützen.
Kubernetes unterstützt ACLs, um über die Ressource NetworkPolicy einzuschränken, welche Workloads miteinander kommunizieren können. Diese Ressource wird in den Netzwerk-Plugins von Kubernetes implementiert und häufig in Linux-iptables-Filterregeln übersetzt, also in eine IP-adressbasierte Lösung ähnlich wie Firewalls – erneut ein altes Paradigma. Einige Service Meshes erweitern die starken OIDC-basierten Workload-Identitäten von Kubernetes, um gegenseitiges TLS zwischen Workloads zu implementieren. Dadurch erhalten Kubernetes-Netzwerkkommunikationen Vertraulichkeit und Authentizität, die auf stärkeren Prinzipien als IP-Adressen basieren.
Bei der Paketierung von Kubernetes-Anwendungen gibt es unterschiedliche Ansätze, wie Netzwerkkonfigurationen eingebunden werden. Viele Helm Charts enthalten Vorlagen für Ingress-Ressourcen. Beim Übergang zu fortschrittlicheren Netzwerkmodellen können diese Definitionen jedoch nicht verwendet werden. Künftig sollten Application Deployments wie Helm Charts Netzwerkkonfiguration als unabhängiges Thema betrachten und sie nicht in das Application-Deployment-Artefakt aufnehmen. Für die Netzwerkkonfiguration von Anwendungen gibt es möglicherweise keine Universallösung, und Unternehmen werden wahrscheinlich eigene Deployment-Artefakte für das „Routing von Anwendungen“ entwickeln wollen.
Kubernetes hat Netzwerke vereinfacht, indem es ein einheitliches Netzwerk über alle Nodes im Cluster hinweg geschaffen hat. Wenn eure Anwendung über mehrere Cluster oder Clouds hinweg läuft, profitiert sie möglicherweise ebenfalls von einem einheitlichen Netzwerk über Cluster oder Clouds hinweg. Das Kubernetes-Netzwerkmodell bietet dies nicht, daher benötigt ihr eine leistungsfähigere Lösung wie ein Service Mesh.
Aus organisatorischer und architektonischer Sicht gibt es daher mehrere Gründe, warum Entwickler das Netzwerk nicht mit Ingress-Ressourcen programmieren sollten. Es ist wichtig, die Optionen aus einer übergreifenden organisatorischen Perspektive zu betrachten, um einen handhabbaren und langfristig tragfähigen Ansatz für Netzwerkkonfiguration und -management sicherzustellen.
Workload-Definition: Auf den Punkt gebracht
Im Kern praktisch aller Kubernetes-Anwendungen steht eine Deployment-Ressource. Ein Deployment definiert, wie unser Workload in Form von Containern innerhalb von Pods ausgeführt werden soll. Die Skalierung von Deployments kann mit einer HorizontalPodAutoscaler-Ressource (HPA) gesteuert werden, um unterschiedlichen Kapazitätsbedarf abzudecken. HPAs nutzen häufig die CPU-Auslastung von Containern als Maßstab, um Pods hinzuzufügen oder zu entfernen, und arbeiten aufgrund des HPA-Algorithmus oft mit einer Zielauslastung von etwa 70 %. Das bedeutet, dass wir 30 % Verschwendung einplanen. Ein weiterer Grund für konservative Zielauslastungen ist, dass ein HPA häufig eine Reaktionszeit von einer Minute oder mehr hat. Um unterschiedlichen Kapazitätsbedarf abzudecken, benötigen wir daher freie Kapazitäten, während der HPA weitere Pods hinzufügt.
Die Verwaltung von Workloads mit Deployments und HPAs funktioniert gut, wenn der Kapazitätsbedarf unserer Anwendung sich langsam verändert. Mit dem Wandel hin zu Microservices, ereignisgesteuerten Architekturen und Funktionen, die ein oder wenige Events beziehungsweise Requests verarbeiten und sich dann beenden, ist diese Form der Workload-Verwaltung jedoch alles andere als ideal.
Der Kubernetes Event-Driven Autoscaling (KEDA) kann das Skalierungsverhalten von Microservices und sich schnell verändernden Workloads wie Funktionen verbessern. KEDA definiert eigene Kubernetes-Ressourcen zur Definition des Skalierungsverhaltens und kann als „HPA v3“ betrachtet werden, da die HPA-Ressource bereits bei „v2“ angekommen ist.
Ein Framework, das das Kubernetes-Deployment-Modell, Skalierung sowie Event- und Netzwerk-Routing kombiniert, ist Knative. Knative ist eine Plattform, die auf Kubernetes aufbaut und mit einer Knative-Service-Ressource einen klaren Ansatz für die Workload-Verwaltung verfolgt. Im Kern von Knative steht CloudEvents, und Knative Services sind im Grunde Funktionen, die durch Events ausgelöst und skaliert werden – entweder durch CloudEvents oder einfache HTTP-Requests. Knative verwendet einen Pod-Sidecar, um Event-Raten zu überwachen, und skaliert daher sehr schnell bei Änderungen dieser Raten. Knative unterstützt außerdem die Skalierung auf null und ermöglicht damit eine feinere Workload-Skalierung, die besser für Microservices und Funktionen geeignet ist.
Knative Services werden mit traditionellen Kubernetes Deployments/Services implementiert. Aktualisierungen von Knative Services, beispielsweise ein neues Container-Image, erstellen parallele Kubernetes-Deployment-/Service-Ressourcen. Knative nutzt dies zur Implementierung von Blue/Green- und Canary-Deployment-Mustern, wobei das Routing des HTTP-Traffics Teil der Ressourcendefinition des Knative Service ist.
Damit werden die Knative-Service-Ressource und die zugehörigen Ressourcen zur Definition des Event-Routings zu den wichtigsten Ressourcen, die Entwickler für die Definition ihres Application Deployments auf Kubernetes verwenden. Ähnlich wie wir heute häufig über Deployment-Ressourcen mit Kubernetes interagieren und Kubernetes die Pods verwalten lassen, beschäftigen sich Entwickler bei der Nutzung von Knative hauptsächlich mit dem Knative Service, während die Knative-Plattform die Deployments verwaltet.
Ich gehe zwar davon aus, dass das Knative-Modell für die große Mehrheit der Anwendungsfälle geeignet ist, aber eure Anforderungen können abweichen. Wenn ihr stattdessen Machine Learning betreibt, ist Kubeflow möglicherweise die bessere Abstraktion. Wenn ihr euch stärker auf DevOps und Delivery Pipelines konzentriert, könnten kpack, Tekton oder Cartographer die passende Abstraktion für euch sein. Was auch immer ihr auf Kubernetes macht: Es gibt eine Abstraktion dafür!
Storage: Weg von Persistent Volumes
Kubernetes stellt die Ressourcen PersistentVolume und PersistentVolumeClaim bereit, um Storage für Workloads zu verwalten. Sie sind wahrscheinlich die Ressourcen, die ich Entwickler am wenigsten nutzen lassen würde – außer für flüchtige Cache-Daten.
Aus einer übergeordneten Perspektive besteht ein Problem bei PersistentVolumes (PVs) darin, dass sie den primären Belang unserer Anwendung mit einem Storage-Belang verbinden. Das ist kein ideales Cloud-Native-Design-Pattern. Die Twelve-Factor-App-Methodik empfiehlt, alle unterstützenden Dienste als über das Netzwerk angebunden zu betrachten. Das hängt damit zusammen, wie wir Workloads in Kubernetes horizontal skalieren und Daten verwalten – denkt an das CAP-Theorem.
PVs bilden Dateisysteme aus Dateien und Verzeichnissen ab, und wir arbeiten mit Daten über eine POSIX-Dateisystemschnittstelle. Auch die Zugriffsrechte basieren auf einem POSIX-Modell, bei dem Benutzer und Gruppen Lese- oder Schreibzugriff erhalten. Dieses Modell passt nicht nur schlecht zum Design Cloud Native Anwendungen, sondern ist auch in der Praxis schwer zu nutzen. Daher werden PVs meist im Modus „Container kann auf alle Daten zugreifen“ eingebunden.
Entwickler sollten zustandsbehaftete Anwendungen entwickeln, die selbst zustandslos sind. Das bedeutet, dass Daten außerhalb der Anwendung und über andere Abstraktionen als Dateisysteme verarbeitet werden sollten, etwa in Datenbanken oder Object Stores. Datenbank- und Object-Store-Anwendungen können PVs für ihre Speicheranforderungen nutzen. Diese Systeme sollten jedoch von Infrastruktur-/SRE-Teams verwaltet und von Entwicklern as a Service genutzt werden.
Eine deutliche Verbesserung der Datensicherheit ist möglich, wenn wir Speicher als über das Netzwerk angebunden betrachten, zum Beispiel Object Storage über REST APIs. Mit REST APIs können wir Authentifizierung und Autorisierung über kurzlebige Zugriffstokens implementieren, die wie oben beschrieben auf Kubernetes-Workload-Identitäten basieren.
Mit der Einführung eines serverlosen Workload-Musters sollten wir mit dynamischeren und kurzlebigeren Workloads rechnen, etwa serverlosen Funktionen, die ein Ereignis pro Pod verarbeiten. Die Diskrepanz zwischen Workloads und „altmodischen Festplatten“ wird in solchen Situationen noch deutlicher.
In Kubernetes ist das Container Storage Interface (CSI) die Schnittstelle, um Workloads über PVs Datei- und Blockspeicher hinzuzufügen. Die Kubernetes Special Interest Group für Object Storage arbeitet an einem Container Object Storage Interface (COSI), das Object Storage zu einem zentralen Bestandteil von Kubernetes machen könnte.
O schöne neue Welt
In diesem Blog habe ich dargelegt, dass es gute Gründe gibt, bei der Definition von Kubernetes-Anwendungen über die „traditionellen“ Kubernetes-Ressourcen hinauszuschauen. Das bedeutet nicht, dass wir die herkömmlichen Ressourcentypen nie verwenden werden. Es wird weiterhin Legacy-Anwendungen geben, die sich nicht einfach umstellen lassen, und SRE-Teams müssen möglicherweise weiterhin zustandsbehaftete Services betreiben, die von Anwendungen genutzt werden können, die Entwickler erstellen. Das gilt insbesondere für private Cloud-Infrastrukturen.
Die Zukunft von Kubernetes liegt in den Custom Resource Definitions (CRDs) und den Abstraktionen, die wir auf Kubernetes aufbauen und über CRDs für Benutzer verfügbar machen. Kubernetes wird zu einer Control Plane für Abstraktionen, und Entwickler sollten sich auf die CRDs dieser Abstraktionen konzentrieren. Kubernetes-Control-Planes können Ressourcen innerhalb oder sogar außerhalb von Kubernetes verwalten, wie zum Beispiel Crossplane Cloud-Infrastruktur verwaltet.
Wie oben zusammengefasst, gibt es für die meisten traditionellen Kubernetes-Ressourcen möglicherweise bessere Alternativen für Entwickler. Durch den Einsatz dieser Alternativen werden wir Cloud Native Anwendungen in den kommenden Jahren besser entwickeln und betreiben können. Schließlich ist Kubernetes eine Plattform, um Plattformen zu bauen. Es ist nicht das Endziel!
- DevOps
- Cloud
Subscribe to our newsletter
Related blogs