Kubernetes CI/CD ist die Pipeline, die deine Kubernetes-Manifeste vor dem Rollout automatisch prüft, testet und in den Cluster bringt, ohne dass du jeden Schritt von Hand ausführst. Anders als eine reine Anwendungs-Pipeline kümmert sie sich zusätzlich um Syntax und Struktur der YAML-Dateien, um die richtige Reihenfolge beim Ausrollen und häufig um eine Freigabe, bevor etwas in der Produktionsumgebung landet. Am Ende steht ein Deployment, das genauso oft und genauso zuverlässig läuft wie dein Anwendungs-Build.
Ich betreibe Kubernetes-Cluster in Produktion und habe das Kubernetes-Praxisbuch bei Rheinwerk geschrieben (2024). Was hier steht, sind Pipelines, die ich selbst aufgebaut und wieder umgebaut habe, keine Demo aus einem Tutorial. Alle meine Beiträge zu Kubernetes findest du gesammelt auf der Seite Kubernetes.
Was eine Kubernetes-CI/CD-Pipeline zusätzlich braucht
Eine typische Anwendungs-Pipeline kompiliert, testet und baut ein Image. Für Kubernetes kommt eine zweite Ebene dazu: die Manifeste selbst. Ein Deployment mit einem Tippfehler im Feld replicas oder ein Service, der auf das falsche Label zeigt, fällt in einem normalen Build nicht auf, sondern erst beim Rollout, im schlimmsten Fall in Produktion.
Deshalb prüft eine ausgereifte Kubernetes-Pipeline drei Dinge, die eine reine Anwendungs-Pipeline nicht kennt: ob die YAML-Dateien syntaktisch und strukturell korrekt sind, in welcher Reihenfolge Ressourcen entstehen müssen, und ob ein Rollout nach dem Anwenden tatsächlich zum gewünschten Zustand geführt hat. Wie stark ein Team diese drei Punkte automatisiert, hängt stark davon ab, wie die Manifeste in Git organisiert sind. Genau da fängt jede Pipeline an.
Manifeste in Git: Ordnerstruktur und Branching
Bevor du eine Pipeline baust, entscheidest du, wie deine Manifeste in Git liegen. Zwei Repository-Modelle haben sich bei mir bewährt. Im Monorepo liegen Anwendungscode und Manifeste zusammen, ein Team hat die volle Verantwortung von Commit bis Rollout, und eine Änderung an Code und Deployment landet in einem einzigen Pull Request. Bei getrennten Repositories entwickeln Anwendungsteams unabhängig von einem Betriebsteam, das die Manifeste verwaltet. Das erhöht die Kontrolle, kostet aber Abstimmung zwischen den Teams, gerade wenn eine neue Umgebungsvariable auf beiden Seiten ankommen muss.
Welches Modell zu dir passt, hängt fast immer davon ab, wie dein Team organisiert ist: Ein Team mit voller Verantwortung für eine Anwendung tendiert zum Monorepo, getrennte Entwicklungs- und Betriebsteams tendieren zu getrennten Repositories mit eigenen Zugriffsrechten.
Auf die Struktur folgt die Branching-Strategie. Ich habe alle drei gängigen Workflows in Projekten eingesetzt, mit sehr unterschiedlichen Ergebnissen.
| Workflow |
Passt gut zu |
Größter Nachteil |
| Git Flow |
lange Release-Zyklen, mehrere Versionen parallel in Betrieb |
schwer lesbare Historie, aufwendige Pipeline durch zwei Dauerzweige |
| GitHub Flow |
kleine Teams, kontinuierliches Deployment direkt aus dem Hauptzweig |
der Hauptzweig muss jederzeit produktionsreif sein, das braucht Disziplin |
| GitLab Flow |
gestaffelte Umgebungen wie Entwicklung, Staging und Produktion |
zusätzlicher Merge-Aufwand zwischen den Umgebungszweigen |
Bei Ops-lastigen Teams mit unterschiedlich langen Release-Zyklen zwischen Staging und Produktion fahre ich meist am besten mit GitLab Flow, weil die Umgebungszweige eine ehrliche Sichtbarkeit schaffen: Was in staging liegt, läuft dort auch wirklich. Für kleinere Teams mit kurzen Zyklen ist GitHub Flow der einfachere Weg, wenn die Pipeline streng genug ist, um Fehler vor dem Merge abzufangen.
Linting und Testen, bevor der Cluster etwas sieht
Der günstigste Fehler ist der, den die Pipeline findet, bevor überhaupt etwas ausgerollt wird. Ein Linter für Kubernetes-Manifeste prüft Syntax und Struktur gegen das Schema der Kubernetes-API, noch bevor ein kubectl apply läuft. Ein verbreitetes Werkzeug dafür ist Kubeconform: Es braucht keinen laufenden Cluster und läuft in Sekunden durch.
Ein einfacher Lint-Schritt in GitLab CI sieht so aus:
stages:
- validate
- deploy
lint-manifests:
stage: validate
image:
name: ghcr.io/yannh/kubeconform:latest-alpine
entrypoint: [""]
script:
- kubeconform -strict -summary manifests/
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
Die Stage validate läuft vor jedem Deploy und bricht die Pipeline ab, sobald ein Manifest nicht zum Schema passt, etwa weil replicas als Text statt als Zahl steht. Wichtig ist die Alpine-Variante des Images samt leerem entrypoint: GitLab CI startet die Skriptzeilen in einer Shell, und die fehlt im schlanken Standard-Image. Die Regel unter rules sorgt dafür, dass der Schritt bei jedem Merge Request greift, nicht erst beim Merge selbst. Damit sieht ein Reviewer den Fehler, bevor er überhaupt zustimmt.
Dieses Beispiel ist nach der Dokumentation von Kubeconform und der GitLab-CI-Referenz zusammengesetzt, nicht aus einer meiner laufenden Pipelines kopiert. Lass es einmal in deinem eigenen Projekt durchlaufen und passe Image-Tag und Pfad an, bevor du dich darauf verlässt.
Linting prüft nur die Form, nicht das Verhalten nach dem Rollout. Dafür setze ich zusätzlich Kuttl ein, ein Testwerkzeug, das ein Manifest tatsächlich in einen Test-Cluster ausrollt und danach prüft, ob der erwartete Zustand eingetreten ist, zum Beispiel ob ein Pod mit einem bestimmten Label wirklich läuft. Solche Tests laufen bei mir in einer eigenen Stage nach dem Deploy in eine Staging-Umgebung, nicht vor dem Merge, weil sie einen laufenden Cluster brauchen und entsprechend länger dauern.
Pipeline-Architekturen: eine Pipeline oder mehrere
Sobald Linting und Tests stehen, folgt die Frage nach der Architektur der Pipeline selbst. Ich unterscheide zwei Achsen. Die erste ist monolithisch gegen aufgeteilt: läuft alles, vom Build bis zum Rollout, als ein einziger Durchlauf, oder sind Build- und Deployment-Pipeline getrennt und lösen sich gegenseitig aus? Die zweite Achse ist zentral gegen dezentral: gibt es eine gemeinsame Pipeline-Vorlage für mehrere Projekte, oder pflegt jedes Repository seine eigene Pipeline komplett selbst?
Eine getrennte Build- und Deployment-Pipeline habe ich vor allem dort eingesetzt, wo ein Entwicklungsteam keinen direkten Zugriff auf den Kubernetes-Cluster haben sollte. Die Build-Pipeline kompiliert, baut das Image, lädt es in eine Registry und übergibt nur die neue Versionsnummer an die Deployment-Pipeline. Die Deployment-Pipeline aktualisiert daraufhin den Image-Tag im Manifest und rollt aus, oft erst nach einer manuellen Freigabe für die Produktionsumgebung. Der Vorteil ist eine klare Trennung der Verantwortung, der Nachteil ist der zusätzliche Übergabepunkt zwischen beiden Pipelines, den ich meist über einen Webhook mit der Versionsnummer als Parameter gelöst habe.
Eine zentrale Pipeline-Vorlage lohnt sich, sobald mehrere Projekte dieselben Schritte brauchen: Linting, Image-Build, Rollout. Statt jede Pipeline neu zu schreiben, ziehst du dir eine gemeinsame Vorlage aus einem zentralen Repository und übergibst nur Parameter wie Anwendungsname oder Zielumgebung. Das spart Pflegeaufwand, bringt aber ein neues Problem mit: Ein Update an der zentralen Vorlage muss gegen alle Projekte geprüft werden, die sie nutzen, sonst bricht die Pipeline woanders unbemerkt.
Wo GitOps eine Pipeline ablöst
Alles bisher Beschriebene ist imperativ: Deine Pipeline führt aktiv kubectl apply oder einen vergleichbaren Befehl aus, sobald ein Merge passiert. Bei GitOps dreht sich die Richtung um. Ein Controller im Cluster beobachtet dein Repository selbst und gleicht den tatsächlichen Zustand des Clusters kontinuierlich mit dem ab, was in Git steht, statt auf einen Push aus der Pipeline zu warten.
Der Unterschied ist mehr als technische Spielerei. Eine Pipeline, die aktiv deployt, braucht einen technischen Nutzer mit Schreibrechten auf den Cluster, der irgendwo in der Pipeline-Konfiguration liegt. Der GitOps-Controller braucht diese Rechte nicht, weil er sich die Änderungen selbst aus dem Repository holt. Zudem erkennt er Abweichungen, die jemand händisch mit kubectl edit in den Cluster gebracht hat, und dreht sie automatisch zurück auf den Stand in Git. Wie du das mit Argo CD konkret aufsetzt, Repo-Struktur inklusive, steht in GitOps mit ArgoCD: Deployments aus Git.
In der Praxis ersetzt GitOps meist nur den letzten Schritt einer Pipeline, nämlich das Ausrollen. Linting, Image-Build und Tests laufen weiterhin in deiner gewohnten CI, nur der Rollout selbst wandert vom kubectl apply in der Pipeline zu einem Commit, den der Cluster-Controller aufgreift.
Häufige Fragen
Wie richte ich eine Kubernetes-Pipeline in GitLab CI ein?
Du brauchst mindestens zwei Stages: eine für die Validierung deiner Manifeste, etwa mit Kubeconform, und eine für den eigentlichen Rollout mit kubectl apply oder helm upgrade. Die Validierung sollte schon bei jedem Merge Request laufen, der Rollout meist erst nach dem Merge in den relevanten Branch.
Welche Tools brauche ich für eine Kubernetes-CI/CD-Pipeline?
Im Kern reichen ein Linter wie Kubeconform für die Syntaxprüfung, ein Testwerkzeug wie Kuttl für Prüfungen nach dem Rollout, und ein CI-System deiner Wahl, etwa GitLab CI, GitHub Actions oder Jenkins. Für das eigentliche Deployment kommt je nach Setup Helm, Kustomize oder pures kubectl dazu.
Was sind die wichtigsten Best Practices für Kubernetes CI/CD?
Aus meiner Erfahrung zählen vier Punkte am meisten: Manifeste vor dem Merge linten, Tests nach dem Deploy statt nur davor, eine bewusste Entscheidung für eine Repository- und Branching-Struktur, und eine klare Trennung zwischen automatischem Deploy in Staging und freigabepflichtigem Deploy in Produktion.
Brauche ich noch eine CI-Pipeline, wenn ich GitOps nutze?
Ja, meistens schon. GitOps übernimmt typischerweise nur den letzten Schritt, das Anwenden der Manifeste im Cluster. Linting, Image-Build und Tests vor dem Rollout laufen weiterhin in einer klassischen Pipeline, die am Ende nur noch einen Commit in dein GitOps-Repository schreibt statt selbst zu deployen.
Wie du weitermachst
Bevor du eine Pipeline baust, lohnt sich ein Blick auf die Manifeste selbst: Welche Felder wirklich nötig sind und wo typische Fehler entstehen, steht in Kubernetes YAML: Manifeste verstehen. Sobald mehrere Umgebungen mit ähnlichen, aber nicht identischen Werten ins Spiel kommen, lohnt sich außerdem Helm Charts erklärt: Pakete für Kubernetes, weil Helm dir die Parametrisierung abnimmt, die du sonst in der Pipeline nachbauen müsstest.
Mein Vorschlag für den Einstieg: Bau dir zuerst den Lint-Schritt in deine bestehende Pipeline ein, bevor du dich um Testwerkzeuge oder GitOps kümmerst. Der Schritt kostet dich wenige Minuten Einrichtung und fängt schon die Fehler ab, die am häufigsten passieren.
Ausführlich mit allen Beispielen, von der Ordnerstruktur bis zu den Pipeline-Architekturen, steht das in Kapitel 4 meines Kubernetes-Praxisbuchs bei Rheinwerk.