In der heutigen Softwareentwicklung sind Continuous Integration (CI), Continuous Delivery (CD) und Continuous Deployment (CD) von zentraler Bedeutung, um Code schnell und zuverlässig bereitzustellen. Dabei spielen grafische und textuelle Pipelines eine zentrale Rolle, denn eine Pipeline ist eine Art Produktionsstraße in einer Fabrik. So wie in einer Fabrik Rohmaterialien durch verschiedene Stationen geführt werden, um am Ende ein fertiges Produkt zu erhalten, durchläuft der Code in einer Pipeline verschiedene Schritte bis hin zum Verpacken und Bereitstellen. Jede Station in der Fabrik hat eine spezifische Aufgabe, genau wie jede Phase in der Pipeline. Am Ende der Pipeline steht ein fertiges, überprüftes und bereitgestelltes Softwareprodukt, bereit für den Einsatz.
Was genau ist eine CI/CD-Pipeline?
Eine CI/CD-Pipeline ist eine automatisierte Folge von Prozessen, die den Weg von der Code-Änderung bis zur Bereitstellung in die Produktionsumgebung abbilden. Die Pipeline besteht aus verschiedenen Phasen, die sicherstellen, dass der Code kontinuierlich integriert, getestet und bereitgestellt wird. Der Hauptzweck einer CI/CD-Pipeline ist es, die Qualität und Geschwindigkeit von Software-Releases zu verbessern, indem manuelle Eingriffe minimiert und Automatisierungen maximiert werden. Die Pipeline selbst ist nicht dafür verantwortlich, was genau in den einzelnen Phasen passiert. Sie stellt lediglich das Grundgerüst für die Modellierung des Ablaufs sowie verschiedene Auswertungsmöglichkeiten bereit. Der eigentliche Build-Prozess, das Ausführen einer Testsuite oder die Code-Analyse werden nicht von der Pipeline selbst durchgeführt. Stattdessen steuert die Pipeline die entsprechenden Werkzeuge an, die diese Aufgaben übernehmen, wie beispielsweise Maven oder Gradle. Folgende Phasen sollten immer in einer Pipeline vorhanden sein, können jedoch nach Bedarf erweitert werden:
- Quellcode: Über eine Schnittstelle zum Versionskontrollsystem wie z.B. Git, wird die Pipeline den Quellcode abholen, welcher unser Produkt definiert. Klar, denn irgendwo muss der Code für ein Build herkommen. Stellvertretend kann hier auch Bitbucket oder ein anderes Codemanagment-System gesehen werden.
- Build: Der vom VCS System ausgecheckte Quellcode wird basierend auf den hinterlegten Build-Tools (z.B. Maven, Gradle, usw.) kompiliert und alle notwendigen Abhängigkeiten integriert.
- Testen: Der Code durchläuft verschiedene Teststufen einer Testsuite, von Unit Tests, über Performance-Tests bis hin zu UI-Tests oder Freigabetests. Wie viele Tests und welche Art von Tests hängt häufig von der Länge der Pipeline ab. Wird die Pipeline bis zum Kunden gelegt, dann wird die Qualität natürlich intensiver getestet, als wenn es nur um Continuous Integration geht. Darauf gehe ich gleich im Detail ein.
- Code-Analyse: Automatisierte Überprüfung des Quellcodes in Bezug auf Stil, Qualität und potenzielle Sicherheitsprobleme, um sicherzustellen, dass der Code den festgelegten Standards entspricht und mögliche Fehler frühzeitig erkannt werden. So werden hier oft Tools wie z.B. OWASP, Checkstyle, SonarQube oder Snyk angesteuert.
- Deployment: Nach erfolgreicher automatisierter QA werden die aus dem Build-Prozess resultierenden Artefakte (z.B. JAR-Dateien, Docker-Images), in einem Artefakt Management Tool (z.B. Nexus, Artifactory oder auch DockerHub) zur Verfügung gestellt oder sogar direkt in einer Staging-Umgebung bzw. Production-Umgebung bereitgestellt.
- Benachrichtigungen: Ziel einer Pipeline ist es zudem, das Team über den Status und die Ergebnisse der verschiedenen Schritte zu informieren, einschließlich Erfolgsmeldungen, Fehlermeldungen und Status-Updates. Diese Benachrichtigungen erfolgen über verschiedene Kanäle wie E-Mail, Chat-Tools, Dashboard-Anzeigen oder SMS/Push-Benachrichtigungen, um sicherzustellen, dass alle Teammitglieder sofort informiert sind und schnell auf Probleme reagieren können.
Was ist der Unterschied zwischen einer Continuous Integration, Continuous Delivery und Continuous Deployment?
Zunächst müssen wir uns die Konzepte von Continuous Integration, Continuous Delivery und Continuous Deployment ansehen und ihre Unterschiede verstehen. Dadurch können wir erkennen, welche Aufgaben eine Pipeline in den jeweiligen Anwendungsfällen erfüllen muss. Alle drei Pipeline-Arten bauen in folgender Weise aufeinander auf:
Continuous Integration (CI)
Bei Continuous Integration stehen meistens einzelne Codeänderungen im Fokus. Diese Änderungen werden häufig in ein zentrales Git Repository überführt und somit in eine bestehende Codebasis integriert. Dieser Codestand wird nun automatisch gebaut, getestet und der Code selbst qualitativ bewertet. Dies geschieht typischerweise mehrmals am Tag, immer dann, wenn sich Code ändert. Konkret werden also mehrere Codeänderungen pro Tag mit z.B. Git commited und mit einem Push innerhalb eines Branches veröffentlicht. Sobald ein Commit veröffentlicht wird, wird ein CI-System benachrichtigt, welches dann verschiedene Schritte ausführt, um die Software zu bauen und zu bewerten. Ein CI-System führt hierzu eine Pipeline aus, in welcher Schritte zum Bauen und zum Testen definiert wurden. Bildlich kann diese Pipeline mit einer Wasser-Pipeline verglichen werden, in der statt Wasser mehrere Commits von A nach B gepumpt werden und dabei verschiedene Phasen durchlaufen. Bei Continuous Integration ist jedoch nicht das Ziel der Pipeline von Interesse, sondern nur die qualitätssichernden Maßnahmen, die innerhalb der Pipeline definiert wurden. Ziel dieser Pipeline ist es, Commits durch diese vielen Phasen zu pumpen, damit Probleme schnellstmöglich erkannt werden und somit ein Entwicklungsteam unterstützt. Die Persona, die hinter Continuous Integration steht, ist also die Entwicklung bzw. das Entwicklungsteam. Durch die Eigenschaften einer Pipeline ist sie jedoch nicht nur ein qualitätssichernder Ablauf, sondern bietet zudem Komfort und macht durch seine didaktischen Fähigkeiten (z.B. das Auffinden von Code-Smells) das Team besser. Da eine Continuous Integration Pipeline idealerweise jedes Commit eines Teams verarbeiten soll, muss sie äußerst performant sein. Besonders bei größeren Teams entstehen viele Commits, die das CI-System bewältigen muss. Wenn die Verarbeitung eines Commits in der Pipeline länger dauert als das Erzeugen neuer Commits, kommt es zu einem Rückstau. Daher werden bei der Qualitätssicherung nicht alle Tests ausgeführt, sondern nur der wichtigste Teilbereich, was auch einer der großen Unterschiede zu Continuous Delivery ist. Typischerweise wird in der Softwareentwicklung eine solche Pipeline auf einem Feature-Branch Workflow angewandt, da die gesammelten Informationen direkt in einem Pull-Request für die Definition eines Quality Gates verwendet werden können.
Typische Aufgaben in der CI-Pipeline: Software-Build, Unit Tests, Integration Tests, Code Analyse, Analyse Sicherheitslücken und Code-Coverage.
Continuous Delivery (CD)
Continuous Delivery erweitert Continuous Integration, indem es sicherstellt, dass der Code jederzeit releasebereit ist und in eine Produktionsumgebung ausgeliefert werden kann (nicht wird, sondern kann). Da in der CI-Pipeline nicht alle qualitätssichernden Maßnahmen durchgeführt wurden, müssen diese im Rahmen von Continuous Delivery unbedingt erfolgen. Die Pipeline wird um den gesamten formellen Freigabeprozess eines Releases erweitert, sodass sie in der Lage ist, eine Freigabe zu automatisieren. Zwar kann eine solche Pipeline auch im Rahmen von Continuous Integration verwendet werden, jedoch würde dies einen erheblichen Overhead darstellen und zu einem Rückstau von Commits in der Pipeline führen. Der Fokus einer Continuous Delivery Pipeline liegt nicht mehr auf einzelnen Commits oder Codeänderungen, sondern auf einem Softwarestand, der potenziell als Release veröffentlicht werden kann. Dieser Softwarestand wird häufig durch spezielle Branches in Git, wie den Release- oder Master-Branch, repräsentiert. Die Pipeline ist daher darauf ausgerichtet, diese Branches zu verarbeiten. Obwohl auch einzelne Commits, wie Hotfixes, zu einem Release führen können, steht dabei die Freigabe des gesamten Softwarestandes im Vordergrund und nicht die alleinige Bewertung des Hotfixes. Im Gegensatz zur CI-Pipeline, die oft keinen festen Endpunkt hat, besitzt eine Continuous Delivery (CD) Pipeline meist ein klares Ziel: eine Testumgebung, die der Produktionsumgebung möglichst nahe kommt. Softwarestände, die durch eine CD-Pipeline laufen, werden daher häufig in einer Test- oder Staging-Umgebung bereitgestellt. Diese Umgebungen und der entsprechende Softwarestand sind jedoch nicht öffentlich, und Kunden erhalten normalerweise keinen Zugriff darauf, da das Release noch nicht offiziell freigegeben wurde. Die wirkliche Freigabe an den Kunden wird nun manuell oder teilautomatisiert als Folgeschritt realisiert. Oft liegt am Ende der Pipline auch “nur” ein Artefaktmanagementsystem (z.B. Nexus oder Artifactory) in dem eine potenzielle Release gespeichert wird. Da der Softwarestand durch Branches wie den Release- oder Master-Branch repräsentiert wird, ist eine solche Pipeline häufig auch oft nur an diese Branches gebunden. Sobald ein neues Feature oder ein Bugfix basierend auf dem Feature-Branch Workflow integriert wird, wird die Continuous Delivery Pipeline aktiviert und das Produkt in einer gewissen Staging/Test Umgebung bereitgestellt.
Erweiterung der Continuous Integration Pipeline um folgende qualitätssichernden Maßnahmen: Acceptance Tests, Performance Tests, User Interface Tests, End-to-End Tests
Continuous Deployment (CD)
Continuous Deployment geht noch einen Schritt weiter als Continuous Delivery, indem es die manuelle Freigabe vollständig eliminiert. Jedes Mal, wenn der Code den gesamten automatisierten Testprozess besteht (also den Anteil der Continuous Integration und Continuous Delivery Pipeline durchlaufen hat), wird er automatisch in die Produktionsumgebung deployed. Oft werden solche Produktionsumgebungen auch wiederum durch einen Branch in Git repräsentiert (z.B. Production-Branch). Sobald also ein Release-Branch (wir erinnern uns: geschützt durch die Continuous Delivery Pipeline) in den Production-Branch integriert wird, wird nun die Continuous Deployment Pipeline aktiviert und das Produkt in einer Production-Umgebung bereitgestellt.
Erweiterung der Continuous Delivery Pipeline um folgende Maßnahmen: Deployment
Wie erzeuge ich eine Pipeline?
Generell hängt dies natürlich von dem Tool ab, welches ich einsetze (z.B. Jenkins oder Bamboo), denn unterschiedliche Systeme gleich unterschiedliche Herangehensweisen. Jedoch kann eine Pipeline generell auf zwei verschiedene Arten erzeugt werden. Zum einen über die UI und zum anderen via Code.
Via UI
Eine Pipeline via UI wird in einer grafischen Benutzeroberfläche erstellt, was eine benutzerfreundliche und visuelle Methode darstellt, jedoch weniger Flexibilität und Wiederverwendbarkeit bietet. Resultierend ist aber die Lernkurve zur Erstellung einer Pipeline relativ flach, da das Prinzip “What you click is what you get” gilt. Das Tooling führt dich mithilfe von Wizards, Tooltips usw. durch den Prozess der Pipeline-Erstellung, was besonders für Anfänger sehr angenehm ist, denn die Erstellung ist fehlerresistenter und man benötigt weniger Erfahrung in Programmierung oder Skripting. Solche Pipelines werden in den vom System bereitgestellten Datenbanken gespeichert, wodurch der Änderungsverlauf an der Pipeline nur schwer ersichtlich wird, denn nur Informationen, die in der UI abgebildet werden, können auch von uns interpretiert werden.
As Code
Im Gegensatz dazu wird eine “Pipeline as Code” durch Skripte oder domänenspezifische Sprachen textuell abgebildet bzw. programmiert und in einem Versionskontrollsystem (z.B. in Git) zusammen mit unserem Produkt-Code gespeichert. Diese Methode ermöglicht eine bessere Wartbarkeit und Integration in den gesamten Entwicklungsprozess, denn die Pipeline wird identisch zum eigentlichen Produkt-Code entwickelt. Nicht selten werden Änderungen an der Pipeline in einem eigenen Jira-Issue beschrieben, in einem eigenen Feature Branch in Git umgesetzt und die Qualität der Pipeline über ein Pull-Request (z.B. in Bitbucket) sichergestellt. Der Pull-Request dient also als Quality Gate nicht nur für den Produkt-Code, sondern auch für die Pipeline, denn zum einen kann der Pipeline Code gereviewed werden und zum anderen wird jede Änderung an der Pipeline auch vom CI System ausgewertet und innerhalb eines Build zusammen mit dem Produktcode getestet.
Die Besonderheiten bei Pipeline as Code
Die größten Unterschiede zwischen den verschiedenen Tools wie Jenkins, Bamboo, GitLab Runner und GitHub Actions finden sich bei der Implementierung von Pipelines. Diese Pipelines unterscheiden sich unter anderem in den unterstützten Programmiersprachen und Konzepten. Während einige Tools auf Groovy oder Java mit entsprechenden APIs setzen, verwenden andere domänenspezifische Sprachen oder deklarative Formate wie YAML. Aber auch Unterschiede im Konzept sind zu finden. Bei einigen ist der entsprechende Code tatsächlich die Pipeline (z.B. Jenkins) bei anderen ist es ausschließlich die Konfiguration der Pipeline (z.B. Bamboo). Diese Unterschiede können die Wahl des Tools beeinflussen, doch der Funktionsumfang sollte letztlich entscheidend sein, nicht die Pipeline-Sprache. Mehr dazu vielleicht in einem anderen Blog.
Pipeline as Code vs. Pipeline Configuration as Code
Der Unterschied zwischen “Pipeline as Code” und “Pipeline Configuration as Code” liegt in der Art und Weise, wie die Pipelines definiert und ausgeführt werden. Diese beiden Konzepte sind wesentliche Bestandteile moderner CI/CD-Systeme, die es Teams ermöglichen, Software schneller und zuverlässiger zu entwickeln und bereitzustellen.
- Pipeline as Code: In diesem Ansatz wird der Pipeline-Code wie ein Build-Skript bei jedem Build durchlaufen. Der Code selbst bildet die Pipeline und steuert deren Ablauf bei jeder Ausführung. Dies bietet hohe Flexibilität und Anpassungsfähigkeit, kann aber komplexer und programmierintensiver sein.
- Pipeline Configuration as Code: Hier ist der Code nicht die eigentliche Pipeline, sondern definiert die Konfiguration, aus der die Pipeline erzeugt wird. Der Pipeline-Code wird nur ausgeführt, wenns ich die Konfiguration selbst ändert, und nicht bei jedem Build. Dies ist oft einfacher zu handhaben und weniger programmieraufwendig, bietet jedoch weniger Flexibilität.
Beide Ansätze haben ihre Vor- und Nachteile, und die Wahl hängt oft von den spezifischen Anforderungen und Präferenzen des Entwicklungsteams ab. Ich bin jedoch der Meinung, dass eine Pipeline meistens von Softwareentwicklern entwickelt wird, die viel lieber programmieren als konfigurieren. Zumindest sollte meine Präferenz jetzt klar sein.
Fazit – Pipeline
CI/CD-Pipelines sind ein unverzichtbares Werkzeug in der modernen Softwareentwicklung, das Teams dabei hilft, effizienter und zuverlässiger zu arbeiten. Für Einsteiger kann die Implementierung zunächst komplex erscheinen, doch die Vorteile sind enorm: schnellere Release-Zyklen, verbesserte Codequalität und frühzeitige Fehlererkennung. Durch den Einsatz von Tools wie Jenkins, Bamboo, GitLab Runner oder GitHub Actions können Entwickler kontinuierliche Integration und Bereitstellung automatisieren, wodurch mehr Zeit für Innovation und Entwicklung bleibt. Der Schlüssel zum Erfolg liegt darin, die Grundlagen zu verstehen, klein anzufangen und kontinuierlich zu optimieren. So können auch Neulinge schnell die Vorteile von CI/CD-Pipelines voll ausschöpfen und ihre Entwicklungsprozesse nachhaltig verbessern. Weiterführend kannst du dir auch folgenden Blogeintrag durchlesen: DevOps ist kein Feature und kostet nur Geld
Gerne kannst du auf unserer Seite bleiben und mehr über unsere Schulungen, unser Konzept und unser Team erfahren. Wir freuen uns darauf, dich kennenzulernen!