Was eine CI/CD-Pipeline wirklich können muss (jenseits des Hypes)
Du pushst Code, die Pipeline wird grün, das Deployment läuft. Doch wenn etwas schiefgeht, merkst du: Die Pipeline wurde nie dafür ausgelegt. Die Datenbankmigration lief in der falschen Reihenfolge. Das Artefakt aus dem Staging unterscheidet sich von dem, das in Produktion deployed wurde. Und ein Rollback? Dafür hatte niemand einen Plan.
Das ist die Lücke zwischen dem Besitz einer Pipeline und dem Besitz einer Pipeline, die tatsächlich funktioniert. Tools wie Jenkins, GitHub Actions, GitLab CI oder ArgoCD versprechen alle, Auslieferung zu lösen, aber das Tool selbst ist nie das Problem. Das Problem sind fehlende Fähigkeiten. Wenn deine Pipeline nicht die richtigen Bausteine hat, kann kein Tool das beheben.
Hier sind die sechs grundlegenden Fähigkeiten, die jede CI/CD-Pipeline haben muss. Keine Nice-to-haves. Keine Funktionen, die man hinzufügt, wenn man Zeit hat. Das sind die Mindestanforderungen, um Änderungen sicher vom Code in die Produktion zu bringen.
Build: Code in etwas Ausführbares verwandeln
Jedes Mal, wenn ein Entwickler eine Änderung pusht, muss die Pipeline diesen Code in etwas umwandeln, das tatsächlich laufen kann. Für kompilierte Sprachen wie Go, Rust oder Java bedeutet das, den Quellcode in Binärdateien zu übersetzen. Für interpretierte Sprachen wie Python oder JavaScript bedeutet Build: Syntax prüfen, Module bündeln, Abhängigkeiten auflösen und die Laufzeitumgebung vorbereiten.
Build ist das erste Tor. Wenn der Code nicht baut, ist alles andere egal. Die Pipeline sollte hier schnell fehlschlagen und keine Zeit damit verschwenden, Tests auf Code auszuführen, der nicht einmal kompiliert werden kann.
Ein häufiger Fehler ist, Build als einen einfachen Schritt zu betrachten, der immer funktioniert. Aber Build-Umgebungen unterscheiden sich. Ein Build, der auf dem Laptop eines Entwicklers gelingt, kann in der Pipeline fehlschlagen – wegen fehlender Systembibliotheken, unterschiedlicher Tool-Versionen oder Umgebungsvariablen. Der Build-Schritt der Pipeline muss reproduzierbar und isoliert sein, sodass das, was in CI funktioniert, überall funktioniert.
Test: Probleme abfangen, bevor sie Nutzer erreichen
Nach einem erfolgreichen Build muss die Pipeline automatisierte Tests ausführen. Das sind nicht nur Unit-Tests, die in Millisekunden durchlaufen. Eine gesunde Pipeline führt mehrere Testebenen aus:
- Unit-Tests, die einzelne Verhaltensweisen überprüfen
- Integrationstests, die prüfen, wie Komponenten zusammenarbeiten
- End-to-End-Tests, die reale Benutzerszenarien simulieren
Jede Ebene fängt andere Arten von Problemen. Unit-Tests finden Logikfehler. Integrationstests finden Inkonsistenzen zwischen Diensten. End-to-End-Tests finden Workflow-Fehler, die mehrere Systeme betreffen.
Der Schlüssel ist Automatisierung. Tests müssen ohne menschliches Eingreifen laufen. Wenn jemand Tests manuell auslösen oder Ergebnisse interpretieren muss, verliert die Pipeline ihren primären Wert: Geschwindigkeit und Konsistenz. Jeder Test, der automatisch läuft, ist eine Sache weniger, an die sich ein Mensch erinnern muss.
Package: Ein versioniertes, deploybares Artefakt erstellen
Sobald der Code gebaut ist und die Tests bestanden sind, muss die Pipeline das Ergebnis in ein Artefakt verpacken, das deployed werden kann. Das Artefaktformat hängt davon ab, was du auslieferst:
- Ein Container-Image für Microservices
- Eine Binärdatei für Desktop-Anwendungen
- Eine APK oder IPA für mobile Apps
- Ein Zip-Archiv für serverlose Funktionen
- Ein Helm-Chart für Kubernetes-Deployments
Jedes Artefakt muss eine eindeutige Version haben. Nicht nur einen Zeitstempel oder eine Build-Nummer, sondern eine Version, die auf den genauen Commit, den Pipeline-Lauf und die Testergebnisse zurückverweist. Diese Rückverfolgbarkeit ist es, die dir sagt, was genau in Produktion läuft und was sich zwischen den Versionen geändert hat.
Das Artefakt muss in einer zentralen Registry oder einem Repository gespeichert werden, auf das die Deployment-Stufe zugreifen kann. Wenn du das Artefakt zur Deployment-Zeit neu baust, verlierst du die Konsistenz. Das Artefakt, das die Tests bestanden hat, muss genau dasselbe Artefakt sein, das deployed wird.
Deploy: Das Artefakt in der Zielumgebung platzieren
Deployment ist mehr als nur Dateien kopieren. Es ist der Prozess, eine neue Version in einer Umgebung zu platzieren und sie Traffic bedienen zu lassen. Für Staging bedeutet Deployment, die neue Version zum Testen zu installieren. Für Produktion bedeutet es, die laufende Version zu ersetzen, ohne die Nutzer zu stören.
Verschiedene Deployment-Strategien existieren für unterschiedliche Risikostufen:
- Rolling Update: Instanzen nacheinander ersetzen
- Blue-Green: Traffic zwischen zwei identischen Umgebungen umschalten
- Canary: Einen kleinen Prozentsatz des Traffics zuerst an die neue Version senden
- Feature Flags: Den Code deployen, aber hinter einem Schalter versteckt halten
Die Pipeline muss die richtige Strategie für jede Umgebung unterstützen. Staging kann einen einfachen Ersatz verwenden. Produktion benötigt oft eine schrittweise Einführung mit Monitoring. Die Pipeline sollte den gesamten Prozess automatisieren, nicht nur das Kopieren der Dateien.
Migrate: Datenbankänderungen sicher handhaben
Wenn deine Anwendung eine Datenbank verwendet, muss die Pipeline Schema-Migrationen handhaben. Das Hinzufügen einer Spalte, das Ändern eines Datentyps oder das Erstellen einer neuen Tabelle erfordert das Ausführen von Migrationsskripten in einer bestimmten Reihenfolge. Diese Migrationen können nicht zufällig mit Anwendungs-Deployments vermischt werden.
Der knifflige Teil ist die Reihenfolge. Manchmal muss die Migration vor dem Deployment des neuen Anwendungscodes laufen. Zum Beispiel das Hinzufügen einer nullable-Spalte, die der neue Code verwenden wird. Ein anderes Mal muss die Migration nach dem Deployment des neuen Codes laufen. Zum Beispiel das Entfernen einer alten Spalte, auf die der alte Code noch verweist.
Die Pipeline muss diese Reihenfolge kennen und korrekt ausführen. Eine Migration, die zur falschen Zeit läuft, kann Ausfallzeiten, Datenverlust oder beides verursachen. Dies ist eine der am meisten übersehenen Fähigkeiten in CI/CD-Pipelines und eine der gefährlichsten, wenn sie falsch gemacht wird.
Rollback: Rückgängig machen, wenn etwas schiefgeht
Nicht jedes Deployment ist erfolgreich. Wenn eine neue Version Fehler, Leistungseinbußen oder Datenkorruption verursacht, muss die Pipeline in der Lage sein, zur vorherigen Version zurückzukehren. Rollback ist nicht nur das erneute Deployen des alten Artefakts. Es umfasst:
- Zurücksetzen der Anwendung auf die vorherige Version
- Ausführen von Reverse-Migrationen auf der Datenbank
- Wiederherstellen der Infrastrukturkonfiguration
- Überprüfen, ob das Rollback tatsächlich funktioniert hat
Rollback muss vor dem ersten Deployment geplant werden. Wenn du die Pipeline entwirfst, ohne zu bedenken, wie eine Änderung rückgängig gemacht wird, wirst du dich dabei wiederfinden, Rollback-Skripte zu schreiben, während die Produktion down ist. Das ist der schlechteste Zeitpunkt, um das herauszufinden.
Für Datenbank-Migrationen bedeutet Rollback, Down-Migrationen zu haben, die die Up-Migrationen rückgängig machen. Für Infrastruktur bedeutet es, vorherige Zustandsdateien zu behalten oder Infrastructure-as-Code-Tools zu verwenden, die Zustands-Rollback unterstützen. Für Anwendungen bedeutet es, das vorherige Artefakt verfügbar zu halten und eine Deployment-Strategie zu haben, die sofortiges Umschalten unterstützt.
Alles zusammengebracht
Diese sechs Fähigkeiten – Build, Test, Package, Deploy, Migrate und Rollback – bilden die Grundlage jeder echten CI/CD-Pipeline. Abhängig davon, was du auslieferst, können einige Fähigkeiten anders aussehen. Infrastruktur-Pipelines könnten Build und Package durch Konfigurationsvalidierung und Zustandsvorbereitung ersetzen. Mobile Pipelines könnten Code-Signing und App-Store-Einreichung hinzufügen. Aber die Kernfunktionen bleiben gleich.
Hier ist eine minimale GitLab-CI-Pipeline, die jede Fähigkeit einer Stufe zuordnet:
stages:
- build
- test
- package
- deploy
- migrate
- rollback
build:
stage: build
script:
- go build -o app
test:
stage: test
script:
- go test ./...
package:
stage: package
script:
- docker build -t myapp:$CI_COMMIT_SHA .
- docker push registry.example.com/myapp:$CI_COMMIT_SHA
deploy:
stage: deploy
script:
- kubectl set image deployment/myapp myapp=registry.example.com/myapp:$CI_COMMIT_SHA
migrate:
stage: migrate
script:
- ./run_migrations up
rollback:
stage: rollback
script:
- ./run_migrations down
- kubectl rollout undo deployment/myapp
when: manual
Das folgende Flussdiagramm zeigt, wie diese sechs Fähigkeiten in einer typischen Pipeline verbunden sind:
Bevor du ein CI/CD-Tool auswählst oder deine Pipeline neu gestaltest, kartiere, welche dieser Fähigkeiten du hast und welche fehlen. Ein Tool, das alles verspricht, aber keine Datenbank-Migrationen oder Rollback-Planung handhabt, wird dich angreifbar machen.
Kurze Fähigkeiten-Checkliste
- Build läuft in einer isolierten, reproduzierbaren Umgebung
- Tests laufen automatisch auf mehreren Ebenen
- Artefakte sind versioniert und in einer zentralen Registry gespeichert
- Deployment unterstützt die richtige Strategie für jede Umgebung
- Datenbank-Migrationen sind korrekt in Bezug auf Anwendungs-Deployments geordnet
- Rollback ist getestet und funktioniert für Anwendung, Datenbank und Infrastruktur
Die konkrete Erkenntnis
Eine Pipeline ist keine Sammlung von Schritten. Sie ist ein System, das den gesamten Lebenszyklus einer Änderung handhaben muss: vom Code zum laufenden Dienst und zurück, falls nötig. Wenn deine Pipeline nicht bauen, testen, packen, deployen, migrieren und zurücksetzen kann, ist sie unvollständig. Beginne damit, die fehlende Fähigkeit zu füllen, nicht damit, das Tool zu wechseln.