zurück zum Artikel

Cloud-nativ entwickeln mit Kubernetes

Architektur/Methoden
Cloud-nativ entwickeln mit Kubernetes

Kubernetes ist nicht nur ein beliebtes Werkzeug für Cloud-Architekten und Administratoren, sondern wird zunehmend auch direkt von Anwendungsentwicklern genutzt. Beim Einsatz von Kubernetes in der Softwareentwicklung gilt es jedoch, einige Herausforderungen zu meistern – trotz Best Practices und geeignetem Tooling.

Als Container-Orchestrator beherrscht Kubernetes vor allem den Betrieb und die Skalierung von Anwendungen. Im Developer Report 2019 von Stack Overflow taucht Kubernetes jedoch auch auf Platz 4 der Technologien auf, die Entwickler in Zukunft gerne lernen und verwenden möchten. Das wachsende Interesse von seiten der Developer-Community ist ein Zeichen dafür, dass Kubernetes in Zukunft nicht mehr nur ein allein auf Admins zugeschnittenes Tool für den Betrieb von Software bleibt, sondern unter Anwendungsentwicklern wachsende Bedeutung gewinnt. Doch bei der Nutzung von Kubernetes in der Softwareentwicklung treten eine ganze Reihe an Herausforderungen auf:

Auf diese und weitere Fragestellungen gibt es eine Reihe möglicher Antworten, die dieser Artikel diskutiert. Dabei nehmen die Autoren einen auf die Praxis gerichteten Blickwinkel ein, um eine Übersicht an Techniken vorstellen zu können, die sich für verschiedene Aufgabenstellungen nutzen lassen. Denn nicht nur die Open-Source-Community hat die Notwendigkeit zur Verwendung von Kubernetes in der Entwicklung bereits erkannt, auch innerhalb der Cloud-Native-Community zielt eine Reihe neu gestarteter Projekte darauf, Entwicklern das Arbeiten mit Kubernetes zu erleichtern.

Cluster-Bereitstellung für Entwickler

Wenn Entwickler Kubernetes einsetzen möchten, stellt sich zuerst die Frage, welche Art von Cluster am geeignetsten ist. Dabei gibt es grundsätzlich zwei Optionen:

Kubernetes auf der ContainerConf

Kubernetes und andere Cloud-native-Themen spielen auch auf der ContainerConf [1] eine wichtige Rolle. Gemeinsam mit der Continuous Lifecycle [2] findet die von heise Developer, iX und dpunkt.verlag veranstaltete Konferenz in diesem Jahr vom 12. bis 15. November in Mannheim statt.

Einen lokalen Single-Node-Cluster können Entwickler mit Tools wie minikube [3] oder kind [4] (Kubernetes in Docker) einfach mit nur wenigen Kommandos innerhalb weniger Minuten erstellen. Noch einfacher geht es mit der grafischen Oberfläche von Docker Desktop. Unabhängig von der Wahl der Tools bietet dieser lokale Ansatz den Vorteil, dass alle Entwickler einen eigenen Cluster nutzen können und der Administrationsaufwand niedrig bleibt. Darüber hinaus lässt sich ein solcher Cluster ohne große Mühe wieder löschen und neu aufsetzen, um bei Bedarf zu einem Clean State zurückzukehren oder etwaige Administrationsfehler zu beheben.

Lokale Cluster haben jedoch auch entscheidende Nachteile. Solche Testumgebungen unterscheiden sich typischerweise stark vom später anvisierten eigentlichen Produktionssystem. Cluster-interne oder Cloud-Provider-spezifische Services lassen sich in der Regel nicht nutzen, Persistent Volumes werden immer auf dem lokalen Rechner verwaltet und ein Zugriff auf Cloud-Storage wie S3 oder die Nutzung von Cloud-Hardware wie GPUs entfällt. Lokale Cluster sind zudem durch die Hardware-Ressourcen des Entwicklerrechners beschränkt, die der dauerhafte Betrieb eines Kubernetes-Clusters überdurchschnittlich belastet.

Methoden zur Cluster-Bereitstellung
Cluster-Bereitstellung Lokaler Cluster Remote Cluster Remote Cluster
Cluster-Typ Separater Cluster für jeden Entwickler Separater Cluster für jeden Entwickler Shared Cluster für mehrere Entwickler
Isolationsebene Cluster Cluster Namespace
Setup-Aufwand sehr gering gering in Public Clouds, höher in Private Clouds überschaubar
Administrationsaufwand sehr gering hoch gering
Administration durch Entwickler Admins / Entwickler Admins
Produktions-ähnliche Testumgebung nein ja ja
Zugriff auf Cloud-Services nur remote ja ja

Remote-Cluster in der Cloud

Die Alternative zum lokalen Cluster sind dedizierte Remote-Cluster. Dabei gibt es grundsätzlich zwei Optionen: Entweder erhalten alle Entwickler eigene Cluster zum Testen oder ein Team teilt sich einen. In beiden Fällen profitieren Entwickler davon, ihre Software in einer Umgebung testen zu können, die sich kaum vom späteren Umfeld für den produktiven Einsatz der Applikationen unterscheidet. Darüber hinaus können Entwickler unkompliziert weitere Cloud-spezifische Dienste einbinden sowie die Leistungsfähigkeit und Skalierbarkeit der zugrundeliegenden Cloud-Hardware ausreizen, ohne den eigenen Rechner durch den Betrieb eines Clusters belasten zu müssen.

Soll ein separater Kubernetes-Cluster für jeden Entwickler zur Verfügung stehen, liegt die größte Herausforderung darin, die Berechtigungen der Nutzer so zu konfigurieren, dass alle Entwickler möglichst schnell und unkompliziert Cluster erstellen oder zumindest darauf zugreifen können. Wer auf einen Public Cloud Service wie Amazon Web Services (AWS) oder Microsoft Azure setzt, ist hier klar im Vorteil, denn die integrierte Rechteverwaltung dieser Plattformen bietet dafür meist vielfältige Optionen.

Darüber hinaus lassen sich mittels Terraform [5] oder Cloud-Provider-spezifischem Tooling wie eksctl [6] von AWS Kubernetes-Cluster mit nur einem Kommando erstellen. Mit der Anzahl an Clustern wächst allerdings der Administrationsaufwand. Daher ist insbesondere die Verwendung eines Managed Kubernetes Service – beispielsweise Elastic Kubernetes Service von AWS [7] oder Google Kubernetes Engine [8] – zu empfehlen, um typische Administrationsaufgaben wie Cluster-Upgrades zu automatisieren. Wer jedoch auf Public Cloud Services verzichtet, weil er die Entwicklung und den Betrieb der Software allein im eigenen Rechenzentrum belassen muss oder möchte, den erwartet ein deutlich höherer Aufwand, um eine Vielzahl an Kubernetes-Clustern für einzelne Entwickler zu erstellen und zu administrieren.

Shared-Cluster für mehrere Entwickler im Team

In diesem Fall hilft die oben erwähnte Variante der Shared-Cluster für mehrere Entwickler. Setup- und Administrationsaufwand lassen sich auf wenige oder sogar einen einzelnen Entwicklungs-Cluster reduzieren, wenn ein Team von Entwicklern Zugriff auf denselben Cluster erhält und die gegenseitige Isolierung für ein ungestörtes Nebeneinander auf Namespace-Ebene [9] erfolgt.

Die größte Herausforderung bei der Nutzung eines solchen Shared-Clusters ist die Strukturierung der Multi-Tenancy innerhalb des Clusters. Damit mehrere Entwickler denselben Cluster nutzen können, muss sichergestellt sein, dass sie einander nicht in die Quere kommen. Dazu sind entsprechende Konfigurationen in Kubernetes vorzunehmen, darunter Role-Based Access Control [10], Pod Security Policies [11], Resource Quotas [12] und Network Policies [13]. Kubernetes bietet dabei sogar die Möglichkeit, eigene Admission Controller [14] mit maßgeschneiderter Logik zu erstellen, die bei jeder Erstellung und Manipulation von Kubernetes-Ressource-Objekten anfragen und API-Server-Anfragen abbrechen oder abändern können.

Anwender, die den damit verbundenen manuellen Konfigurationsaufwand scheuen, können auf Werkzeuge wie Red Hat OpenShift [15] oder DevSpace Cloud [16] zurückgreifen. Rancher Labs hat zudem kürzlich ein neues Projekt namens k3v [17] vorgestellt, das es neben der Isolierung mittels Namespaces und Service Accounts ermöglichen soll, virtuelle Cluster auf Basis eines oder sogar mehrerer darunterliegender Kubernetes-Cluster zu erstellen. Das Projekt befindet sich bisher jedoch noch im Status eines Proof of Concept.

Development Workflows

Unabhängig davon, welche Variante letztlich Entwicklern Zugriff auf Kubernetes verschafft, ist darauf zu achten, dass der Aufwand für die Konfiguration des kubectl-Kontext auf dem lokalen Rechner möglichst einfach bleibt, damit Entwickler die Komplexität, die Kubernetes mit sich bringt, nicht überfordert und sie mit minimalem Aufwand produktiv werden können.

Denn die Bereitstellung eines Kubernetes-Clusters ist nur der erste Schritt, um Kubernetes in der Softwareentwicklung zu nutzen. Verwenden Entwickler allein das Kommandozeilentool kubectl [18] oder den Package Manager Helm [19], dann kostet es sie oft viel Zeit, Container-Images manuell zu bauen, in einer Registry zu speichern und Kubernetes-Objekte wie Deployments, Services oder Ingresses zu erstellen und zu aktualisieren. Das verringert nicht nur die Produktivität, sondern ist wegen der vielen sich wiederholenden Tätigkeiten zudem fehleranfällig. Es kann dazu führen, dass Entwickler sich zu stark mit Deployment-Themen beschäftigen müssen statt mit der eigentlichen Anwendungsentwicklung. Daher empfiehlt es sich, den Deployment Workflow für Entwickler weitgehend zu standardisieren – entweder per Continuous Deployment oder In-Cluster Development.

Cloud-native Workflow-Tools
Workflow-Tool Draft DevSpace Skaffold Tilt
Image Building Docker, custom script Docker, kaniko, Minikube-Docker, custom script Docker, kaniko, Google Cloud Build, Bazel, Jib, custom script Docker, custom script
Deployment Helm Helm, kubectl, kustomize, custom script Helm, kubectl, kustomize, custom script Helm, kubectl, kustomize, custom script
Microservice Deployments via Helm via Helm und git Dependencies via Helm via Helm
Auto-Redeploy ja ja ja ja
Hot Reload & File Synchronization nein ja, bidirektional ja, nur upstream ja, nur upstream

Continuous Deployment (CD) baut auf das automatisierte Ausführen einer vordefinierten Deployment Pipeline auf. Die klassische (post-commit) Variante des CD startet durch Einchecken des Codes in ein Code Repository und setzt voraus, dass ein CI/CD-Service wie Jenkins X [20] oder GitLab CI [21] zur Verfügung steht. In den meisten Organisationen dürften diese Tools mittlerweile Standard sein – auch unabhängig von Kubernetes.

Im Cloud-Native-Umfeld hat sich in den vergangenen zwölf Monaten noch eine weitere Variante des Continuous Deployment etabliert: Das Ausführen von Deployment Pipelines mit lokalen Tools direkt auf dem Rechner des Entwicklers (pre-commit). Bei dieser Vorgehensweise erhalten Entwickler deutlich direkter und schneller Feedback. Anstatt zu warten, bis das Scheduling des CI-Servers den Deployment Prozess durchführt, können Entwickler unmittelbar vom lokalen Rechner aus ihre Anwendung in Kubernetes instanziieren. Sie behalten außerdem mehr Kontrolle darüber, was während des Deployment-Prozesses passiert, und haben auch nach dem Deployment direkten Zugriff auf die Container und die übrigen Kubernetes-Objekte. Beim lokalen Deployment erhalten Entwickler darüber hinaus die Möglichkeit, bei Bedarf ein Container-Terminal zu öffnen, auf Dateien im Container zuzugreifen oder Remote Debugger direkt im Container zu verwenden, anstatt nur die Logs des Jenkins-Servers einzusehen.

Automatisieren von Workflows mit Draft, DevSpace, Skaffold und Tilt

In der Cloud-Native-Community finden sich zurzeit vier aufstrebende Projekte, die es Entwicklern ermöglichen, Deployment-Workflows zu automatisieren und damit auf dem lokalen Rechner eine Art Continuous Deployment Pipeline auszuführen: Draft [22], DevSpace [23], Skaffold [24] und Tilt [25]. Während Tilt vor allem auf die Bereitstellung über lokale Kubernetes-Cluster wie Minikube, Microk8s & Co. ausgelegt ist, spezialisiert sich Draft auf Deployments via Helm. DevSpace, Skaffold und Tilt unterstützen sowohl die Bereitstellung via Helm als auch mit kubectl und kustomize.

Während sich Abhängigkeiten zwischen verschiedenen Projekten, beispielsweise für voneinander abhängige Microservices, durch Helm Dependencies abbilden lassen, bietet DevSpace zusätzlich die Möglichkeit, Abhängigkeiten auch zwischen verschiedenen Arten von Deployments zu definieren und somit das parallele Instanziieren von mehreren Microservices mit einem einzelnen Deployment-Kommando zu vereinfachen. Bevor jedoch das Deployment von Helm Charts oder Kubernetes Manifestos erfolgen kann, müssen alle Tools zuerst Docker Images bauen und in einer entsprechenden Registry speichern.

Während alle genannten Tools das Erstellen von Images mittels Docker beherrschen und benutzerdefinierte Build Commands zulassen, unterstützen DevSpace und Skaffold auch kaniko [26], um direkt im Kubernetes-Cluster Images zu bauen. Skaffold ist in Sachen Image Building das mit Abstand mächtigste Tool, es integriert auch direkt Jib [27], Bazel [28] und Google Cloud Build [29]. Um den Continuous-Deployment-Prozess wirklich kontinuierlich auszuführen, bieten alle bisher aufgeführten Tools die Möglichkeit, einen File Watcher zu starten und automatisch einen Redeploy zu triggern, wenn Änderungen am Sourcecode oder den Konfigurationsdateien auftreten.

In-Cluster Development statt lokaler Softwareentwicklung

DevSpace, Skaffold und Tilt gehen noch einen Schritt weiter: Sie ermöglichen In-Cluster Development direkt im Kubernetes-Cluster. Da jede Ausführung der lokalen Deployment Pipeline inklusive Image Building eine gewisse Zeit in Anspruch nimmt, lässt sich der File Watcher beim In-Cluster Development dazu nutzen, Dateiänderungen direkt in die laufenden Container zu synchronisieren und die Anwendung sofern nötig dort auch neu zu kompilieren. Dadurch lässt sich das wiederholte Bauen von Docker Images und Neustarten der Container vermeiden.

Der gesamte Prozess läuft somit schneller ab, insbesondere wenn noch Hot Reloading Tools wie nodemon hinzukommen, die direkt im Container ebenfalls mit File Watcher den Sourcecode überwachen und bei Änderungen die Anwendungen erneut kompilieren und anschließend starten. Während Skaffold und Tilt Änderungen vom lokalen Dateisystem in die Container synchronisieren können, bietet DevSpace zusätzlich die Möglichkeit, Dateien bi-direktional und Verzeichnisse auch nach dem Container-Start on Demand zu synchronisieren.

In-Cluster Development eignet sich als Ersatz für lokale Softwareentwicklung besondere dann, wenn es um komplexere Anwendungen wie Microservices oder Machine-Learning-Systeme geht, da Entwickler nicht mehr durch die Beschränkungen ihrer lokalen Arbeitsrechner limitiert sind. Genau für diese Szenarien wurden auch Tools wie Telepresence [30] entwickelt, die es erlauben, bestimmte Teile eines Systems lokal zu entwickeln und mit anderen Services zu verbinden, die in einem Remote-Cluster laufen.

Dieser Spagat zwischen lokaler und Cloud-nativer Entwicklung hat jedoch einige Nachteile, wie die erhebliche Latenz bei der Kommunikation zwischen Anwendungsbestandteilen sowie die Schwierigkeiten beim Arbeiten mit persistenten Daten. Mit der neuen Generation der Kubernetes Developer Tools wird die Cloud-native Entwicklung zur favorisierten Option und damit eine echte Konkurrenz zur lokalen Anwendungsentwicklung.

Fazit: Per Cluster-Zugriff zur Code-Instanziierung

Wenn Kubernetes nicht nur der De-facto-Standard für den Betrieb und die Skalierung von Anwendungen in der Cloud bleiben, sondern auch zunehmend direkt von Anwendungsentwicklern eingesetzt werden soll, muss für Developer ein effizienter und sicherer Zugriff auf Kubernetes sichergestellt sein. Neben lokalen Single-Node-Clustern lassen sich dafür auch Remote-Cluster nutzen, die entweder separat für jeden Entwickler oder als Shared-Cluster für ein Team zur Verfügung stehen.

Unabhängig von der jeweiligen Art des Cluster-Zugriffs ist entscheidend, dass Entwickler schnell und einfach neuen Code instanziieren können. Dabei gibt es neben dem ans Code Repository geknüpften Continuous Deployment auch die Möglichkeit, dass Entwickler selbst vom lokalen Rechner aus den Deployment Prozess starten. Cloud-native Workflow- und Automatisierungstools helfen dabei, Zeit zu sparen und die Produktivität von Entwicklern zu steigern. Developer, die darüber hinaus die Softwareentwicklung mit File Synchronization vollständig in den Kubernetes-Cluster verlagern, vollziehen sogar den Schritt zum „cloud-native“-Paradigma in der Anwendungsentwicklung.

Lukas Gentele
ist CEO und Co-Founder der DevSpace Technologies Inc. und seit Jahren aktiv im Cloud-native Umfeld. Mit seinem Team arbeitet er an Open-Source-Tools für Entwicklerteams, die mit Kubernetes arbeiten.

Michael Hausenblas
ist ein AWS Developer Advocate. Er hat sich auf Kubernetes, GitOps und Container Security spezialisiert.


URL dieses Artikels:
http://www.heise.de/-4510226

Links in diesem Artikel:
[1] https://www.containerconf.de/programm.php?source=11
[2] https://www.continuouslifecycle.de/jetzt_anmelden.php?source=11
[3] https://github.com/kubernetes/minikube
[4] https://github.com/kubernetes-sigs/kind
[5] https://www.terraform.io/docs/providers/kubernetes/index.html
[6] https://github.com/weaveworks/eksctl
[7] https://aws.amazon.com/de/eks/
[8] https://cloud.google.com/kubernetes-engine/
[9] https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
[10] https://kubernetes.io/docs/reference/access-authn-authz/rbac/
[11] https://kubernetes.io/docs/concepts/policy/pod-security-policy/
[12] https://kubernetes.io/docs/concepts/policy/resource-quotas/
[13] https://kubernetes.io/docs/concepts/services-networking/network-policies/
[14] https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/
[15] https://www.openshift.com/
[16] https://github.com/devspace-cloud/devspace-cloud
[17] https://github.com/ibuildthecloud/k3v
[18] https://kubernetes.io/docs/reference/kubectl/overview/
[19] https://helm.sh/
[20] https://jenkins.io/projects/jenkins-x/
[21] https://about.gitlab.com/product/continuous-integration/
[22] https://github.com/Azure/draft
[23] https://github.com/devspace-cloud/devspace
[24] https://github.com/GoogleContainerTools/skaffold
[25] https://github.com/windmilleng/tilt
[26] https://github.com/GoogleContainerTools/kaniko
[27] https://github.com/GoogleContainerTools/jib
[28] https://github.com/bazelbuild/bazel
[29] https://cloud.google.com/cloud-build/
[30] https://github.com/telepresenceio/telepresence