Die beschriebene Situation kann häufig beobachtet werden. Firmen brechen alte, schwerfällige Monolithen in Microservices auf und deployen diese auf Kubernetes. Dabei wird oft die alte CI/CD-Infrastruktur beibehalten. Diese passt jedoch nicht mehr wirklich zur neuen Realität: Zwar können mit aktuellen Jenkins-Versionen Pipelines beschrieben werden. Unit Tests können auch in Bamboo ausschließlich in Containern ausgeführt werden. Allerdings wurden diese Systeme ursprünglich mit einer komplett anderen Philosophie entwickelt und passen dementsprechend schlecht zu modernen Anforderungen.

Eine Anpassung der alten Systeme gestaltet sich müßig: Maven- oder Java-Plugins sind unnötig, wenn zum Bauen von Applikationen einfach ein Docker-Container genutzt werden kann. Kubernetes-Deployments können mit Tools wie kubectl oder helm in einem Container implementiert werden, statt dafür ein Plugin für das CI/CD-System zu nutzen. Die Anforderungen an Funktionalitäten, die CI/CD-Systeme selbst zur Verfügung stellen müssen, sinken durch den Einsatz von Docker.

Ein weiterer Anachronismus in Zeiten von „Configuration as Code“ (siehe unten) ist die Konfiguration der Pipelines über eine grafische Benutzeroberfläche. Eine solche Konfiguration ist mühsam und die Fehlersuche gestaltet sich schwierig. Aktuelle Systeme nutzen deklarative, textuelle Beschreibungen von Pipelines. Diese können einfach in Versionsverwaltungssystemen, zusammen mit der eigentlichen Applikation, hinterlegt werden. So konfigurierte Pipelines können schnell wiederhergestellt werden, sollte das CI/CD-System neu aufgesetzt werden. Die Benutzeroberflächen moderner Systeme bieten nur noch eine Sicht auf den Zustand von Pipelines und ermöglichen das manuelle Starten einzelner Schritte.

Ganz neue Lösungen verzichten nicht nur auf das Konzept der Plugins, sondern versuchen noch mehr Kernfunktionalitäten von CI/CD-Systemen auszulagern. Kubernetes löst beispielsweise viele Aufgaben, die ehemals von jedem CI/CD-System selbst implementiert wurden. Dazu gehören zum Beispiel das Starten, Stoppen und Verwalten von Containern.

Seit der Einführung des ersten, populären CI-Systems CruiseControl im Jahr 2001 hat sich CI/CD mehrmals neu erfunden. Die neueste Generation von Systemen ist gerade dabei, Produktionsreife zu erlangen.

Drei Generationen von CI/CD Systemen

1. Generation

CI als Idee ist mit der agilen Bewegung Anfang der 2000er populär geworden. In diesem Zusammenhang entstanden erste Tools wie CruiseControl. Anfangs waren diese Systeme noch recht einfach, entwickelten sich aber schnell zu sehr umfangreichen Lösungen weiter. Zu dieser ersten Generation von Systemen gehören Jenkins, Bamboo und Teamcity von Jetbrains. Ein wesentliches Merkmal dieser Systeme ist, dass sie sämtliche Funktionalität selbst implementieren. Sie unterstützen Build-Tools verschiedener Programmiersprachen durch Plugins. Um Lasten zu verteilen, unterstützen viele Lösungen die Verteilung auf mehrere Hosts. Auf diesen laufen Agents, die jeweils bestimmte Schritte in einer Pipeline ausführen können. Das Managen ganzer Cluster, auf denen solche Agents laufen, ist oft Teil des CI/CD-Systems. Agents können spezifisch konfiguriert werden, um auch exotische Anforderungen zu erfüllen. Um diese Features zu bedienen, sind sehr umfangreiche und komplexe Web-Oberflächen nötig. Mit dem Aufkommen von Continuous Delivery wurde noch die Fähigkeit zur Verwaltung von Pipelines in die Tools eingebaut. Die erste Generation bietet also einen großen Funktionsumfang, ohne große Anforderungen an ihr Umfeld zu stellen.. Dafür zahlt man allerdings einen hohen Preis: zunehmende Komplexität. Häufig muss die Konfiguration von Experten oder Expertenteams gepflegt werden.

2. Generation

Die Systeme der zweiten Generation entstanden unter anderem aus dem Wunsch, diese Komplexität zu verringern. Zwei technologische Trends halfen dabei, diesen Wunsch zu erfüllen. Mit Software as a Service (SaaS) wurde es möglich, den Betrieb und damit auch die Komplexität eines CI/CD-System vom Entwicklungsteam fernzuhalten. Es kann sich auf die Entwicklung von Software konzentrieren und beschreibt die Schritte zum Bauen der Software in einer Konfigurationsdatei. Diese wird von einem System gelesen, interpretiert und in ein ausführbares Skript übersetzt, welches anschließend ausgeführt wird. Die Strategie wird “Configuration as Code” genannt und ist eine zentrale Eigenschaft der 2. Generation. Neu ist auch, dass das CI/CD-System nicht auf der firmeneigenen Infrastruktur betrieben werden muss. Travis CI hat diese Philosophie für GitHub populär gemacht.

Der zweite große Trend bei diesen Systemen ist die konsequente Nutzung von Docker-Containern zum Bauen und Testen der Software. Dadurch wird das CI/CD-System nur noch zum Orchestrator. Weder müssen Build-Umgebungen verwaltet werden, noch müssen Technologien verschiedener Programmiersprachen direkt unterstützt werden.

Die Kombination dieser beiden Ansätze findet man in sehr vielen der aktuellen CI/CD-Systeme. Typische Vertreter der zweiten Generation sind Travis CI, Gitlab CI, Codeship und die Build-Systeme großer Cloud-Anbieter wie AWS, Google oder Azure. Einige dieser Systeme werden nicht nur als SaaS-Lösung angeboten sondern können auch in der eigenen Infrastruktur betrieben werden.

Auch diese Generation implementiert das Management der Build-Cluster durch Agenten oft selbst. Einige Systeme, wie zum Beispiel Gitlab CI, können bereits Agenten in Kubernetes-Clustern betreiben.

3. Generation

Die dritte Generation von CI/CD-Systemen ist noch stärker auf Kernfunktionalität reduziert und setzt konsequent auf Kubernetes als Laufzeitumgebung. Sie werden deshalb auch als „Kubernetes-Native“ bezeichnet. Oft gehen sie bei dieser Fokussierung noch weiter und setzen neben Kubernetes noch weitere Infrastruktur voraus. So laufen manche Systeme nur bei bestimmten Cloud-Anbietern oder setzen als Anbieter des Versionsverwaltungssystems Produkte wie GitHub voraus.

Durch diese Fokussierung können sie von vorhandenen Komponenten dieser externen Systeme Gebrauch machen. Kubernetes-Native CI/CD-Systeme nutzen Kubernetes-Konzepte wie Jobs und Pods. Diese werden zur Ausführung von Workern und Build-Schritten genutzt. Worker starten ausschließlich dann, wenn eine Build-Pipeline ausgeführt werden soll. Bei aktivierter Autoskalierung passt Kubernetes automatisch die benötigten Ressourcen an. Es ist also nicht nötig, ständig genügend Hosts für die CI/CD-Infrastruktur vorzuhalten. Die entstehende Verzögerung durch den Skalierungsprozess ist im CI/CD-Kontext zu vernachlässigen. Beendete Pods müssen je nach Lösung teilweise durch Kubernetes Cron-Jobs aufgeräumt werden, um noch benutzen Arbeitsspeicher freizugeben. All diese Aufgaben mussten frühere CI/CD-Systeme selbst ausführen.

Prototypisches Kubernetes-Native CI/CD-System

In einem CI/CD-System der dritten Generation werden Build-Pipelines von Events angestoßen (Event-driven), die meistens durch Webhooks das CI/CD-System erreichen. Das Vorhandensein solcher Webhooks kann inzwischen bei vielen Versionskontrollsystemen vorausgesetzt werden und ist eleganter als ein klassisches Polling. Ein Event kann ein neuer Commit, Pull-Request oder ein neues Container-Image in der Registry sein.

Wie in anderen CI/CD-Systemen auch, stößt eine Modifikation des Code-Repositories die Pipeline an. Der Inhalt des Code-Repositories wird für die Ausführung der einzelnen Build-Schritte heruntergeladen und in dem jeweiligen Container zur Verfügung gestellt. Für die Durchführung der Builds und Tests werden unterschiedliche Container genutzt, die die Funktionalität für den jeweiligen Schritt mitbringen.

Pipeline-Definition werden in Form von Konfigurationsdateien, zusammen mit der betreffenden Applikation, im Code-Repository abgelegt.

Für das Deployment verfolgen Kubernetes-Native CI/CD-Systeme den Ansatz der Pull-basierten Strategie. Statt eine Applikation von außen in den Kubernetes Cluster zu deployen, wird das Deployment innerhalb desselben Clusters ausgeführt, in dem die Applikation laufen soll. Der Service-Account des Pods, welcher das Deployment ausführt, bekommt dazu die Rechte die Applikation entsprechend auszurollen. Fände das Deployment hingegen von einem externen System statt, so müssten Geheimnisse wie SSH-Schlüssel oder Kubernetes-Zertifikate auf diesem System vorgehalten werden. Die sichere Verwaltung solcher Geheimnisse zu realisieren ist aufwendig. Die Konfiguration bei einem Pull-basierten Deployment ist somit einfacher und sicherer.

Beispielhafte Architektur für Kubernetes-native CI/CD-Systeme.
Beispielhafte Architektur für Kubernetes-native CI/CD-Systeme.

Im Folgenden werden exemplarisch typische Vertreter von CI/CD-Systemen der dritten Generation oder deren Vorbilder vorgestellt.

Das Vorbild: Concourse

Concourse hat auf Systeme der dritten Generation einen ähnlichen Einfluss wie Travis CI auf Systeme der zweiten Generation hatte. Auch wenn es selbst kein Kubernetes-Native-System ist, lohnt sich daher eine nähere Betrachtung. Das Projekt wurde Anfang 2014 gestartet und hat von Anfang an alle Aufgaben in Containern ausgeführt.

Concourses Oberfläche ist angenehm anders und ermöglicht die Darstellung sehr komplexer Pipelines.

Eine Pipeline in Concourse. Aktuell ausgeführte Schritte sind animiert. Bei komplexen Pipelines ist es möglich, beliebig hinein und hinaus zu zoomen.
Eine Pipeline in Concourse. Aktuell ausgeführte Schritte sind animiert. Bei komplexen Pipelines ist es möglich, beliebig hinein und hinaus zu zoomen.

Wie bei Systemen der zweiten Generation können Pipelines ausschließlich über Konfigurationsdateien erstellt und aktualisiert werden. In Concourse wird dazu ein Kommandozeilen-Tool genutzt.

Die Entwickler haben interessante Architekturentscheidungen getroffen, die der Zeit voraus waren. Ein zentraler Baustein von Concourse ist die Resource. Concourse-Resources können externe Ein- oder Ausgaben sein. Gibt es eine neue Version einer Concourse-Resource, so kann dies als Trigger für den Start einer Pipeline genutzt werden. Am Ende eines Pipeline-Schrittes wird häufig eine neue Artefaktversion an eine Concourse-Resource übergeben. Diese neue Version kann wiederum ein Trigger für eine weitere Pipeline sein.

Typische Concourse-Resourcen sind ein Git-Repository oder ein Registry-Image. Ein typischer Ablauf mit diesen Concourse-Resources sieht folgendermaßen aus: Bei der Git-Resource wird ein neuer Commit als Trigger für das Starten einer Pipeline genutzt. Das Registry-Image bekommt am Ende des Builds eine neue Version eines Docker-Images. Das neue Docker-Image ist eine neue Version der Registry-Image-Resource und kann somit als Trigger zum Starten einer anderen Pipeline genutzt werden.

Es ist sehr einfach, neue Typen von Resources zu erstellen. Dazu muss ein Docker-Image mit drei verschieden Skripten erstellt werden, welche bestimmten Anforderungen genügen. Die Wahl der Programmiersprache ist dem Entwickler überlassen.

Außer der Resource kennt Concourse noch Tasks und Jobs. Tasks beschreiben die Kommandos, die in der Pipeline ausgeführt werden. Sie laden dazu häufig neue Versionen von Resources herunter, erzeugen neue Artefakte und laden diese in eine andere Concourse-Resource hoch. Jobs verbinden Tasks und Resourcen: Wenn die Concourse-Resource eine neue Version bekommt, triggert dies einen oder mehrere Tasks. Die geringe Anzahl an Bausteinen und die Entkopplung über Resources ermöglicht das Erstellen von sehr komplexen Pipelines.

Concourse kann seine Stärken vor allem bei großen Projekten ausspielen. Es bietet einige Features, die bei einem Vergleich mit anderen System leicht übersehen werden. Es unterstützt eine Vielzahl von Methoden der Authentifizierung und eine Einbindung verschiedener Secrets-Management-Systeme wie zum Beispiel Vault.

Es ist einfach Concourse auf Kubernetes zu betreiben. Concourse macht allerdings keinen Gebrauch von Kubernetes-Funktionalitäten wie Pods oder Jobs, sondern startet lediglich die Web-UI und die Agents in Kubernetes. Die einzelnen Schritte werden durch die Agents zwar in Containern ausgeführt, allerdings nicht über die Kubernetes API.

Obwohl Concourse-Pipelines aus nur drei Komponenten bestehen, ist die Einarbeitung schwieriger, als bei vielen Systemen der zweiten Generation.

Konsequent: Tekton-CD

Tekton CD ist der neue Name von Knative Pipelines, ein Projekt welches Mitte letzten Jahres vorgestellt wurde. Ähnlich wie bei Concourse bestehen Pipelines bei Tekton-CD aus drei Grundkomponenten: Der Resource, dem Task und dem Step. Die Resource entspricht der Concourse-Resource, der Task entspricht dem Job und was Concourse einen Task nennt ist bei Tekton-CD ein Step.

Die einzelnen Komponenten werden bei Tekton-CD ausschließlich mit Kubernetes-Konzepten implementiert. Fehlende Konzepte werden durch Erweiterung der Kubernetes API mithilfe von Custom-Resource-Definitions ergänzt.

Eine Pipeline besteht aus Resources und Tasks. Eine Task kann Teil einer Pipeline sein oder unabhängig ausgeführt werden. Resources können wie bei Concourse Eingaben (Git-Repository) bereitstellen oder Build-Ausgaben entgegennehmen (Docker Image). Resources in Tekton können (noch) nicht als Trigger für Pipelines genutzt werden. Stattdessen muss manuell ein TaskRun oder PipelineRun erstellt werden. Tekton-CD beobachtet die Erstellung neuer Run-Objekte und führt diese aus. Der Status eines Runs kann über die Kubernetes typischen Werkzeuge auf der Kommandozeile mittels kubectl describe abgefragt werden.

Das Projekt ist vielversprechend, aber in einer frühen Phase. Zum jetzigen Zeitpunkt ist es noch notwendig die Erstellung von TaskRuns und PipelineRuns selbst zu automatisieren. Des Weiteren verfügt Tekton-CD über keine UI.

Der Spezialist: Prow

Prow ist eine Kubernetes-Native Lösung, welche erfolgreich für große Projekte wie Kubernetes, Istio und OpenShift genutzt wird. Das Triggern von Jobs erfolgt über GitHub-Events wie Pre- bzw. Post-Commit-Hooks. Eine weitere Möglichkeit Aktionen bei Prow zu triggern sind, spezielle Kommentare in GitHub-Pull-Requests. Je nach Konfiguration ist es beispielsweise möglich, mit /test Tests für ein Projekt zu starten. Diese Strategie ist an ChatOps angelehnt: Steuerung und Rückmeldungen des CI-Systems geschehen über eine Chat-Schnittstelle – in diesem Fall GitHub-Kommentare. Ganz auf eine grafische Benutzeroberfläche verzichtet Prow nicht. Mit dem Prow-Dashboard können Nutzer einen Überblick der ausgeführten Jobs und deren Logs bekommen.

Prow nutzt konsequent Kubernetes-Komponenten und erweitert die Kubernetes-API, wie Tekton-CD, durch Custom-Resource-Definitions wenn nötig. Es wurde speziell für die Anforderungen und die Umgebung von Projekten der Cloud Native Computing Foundation entwickelt und nicht als generisches CI/CD-System. Das heißt unter anderem, dass es nur sinnvoll nutzbar ist, wenn Google Cloud Services genutzt werden und das Projekt auf GitHub gehostet wird – andere Konfigurationen werden zur Zeit nicht unterstützt.

Durch diese Fokussierung dürfte Prow für die meisten Setups keine akzeptable Lösung darstellen, auch wenn die Konzepte überzeugen und Prow beweist, dass es für den Produktiveinsatz bereit ist.

Des Kaisers neue Kleider: Jenkins X

Jenkins X ist eine Jenkins-Variante für Kubernetes. Das Herz von Jenkins X ist weiterhin Jenkins, welches Cloud-Komponenten steuert – die Chance, einen sauberen Neustart zu wagen, wurde von der Jenkins-Community leider nicht wahrgenommen. Es ist schwer zu verstehen, was genau Jenkins X sein möchte. Sehr viele Komponenten, wie Prow, Knative, eine Docker-Registry, ein Helm-Chart-Repository und Nexus werden automatisch mitinstalliert. Es folgt damit nicht dem Trend, kleine, fokussierte Komponenten zu erstellen. Im Vergleich mit anderen Systemen ist der Ressourcenhunger der Grundinstallation hoch. Allerdings zeichnet sich Jenkins X durch ein einfaches Aufsetzen aller Komponenten mit Hilfe des Kommandozeilen-Tools jx aus. Es wird sogar die Provisionierung von Kubernetes-Clustern für Provider wie AWS, EKS, und GKE oder lokale Minikube-Installationen angeboten.

Jenkins X unterstützt weiterhin die Beschreibung der Pipelines mittels Jenkinsfile. Für GitHub-Code-Repositories definiert Jenkins X einen speziell angepassten Deployment-Prozess. In diesem Prozess werden Code-Änderungen in Pull-Requests zur Vorschau in eigene Namespaces deployed. Code-Änderungen innerhalb eines Pull Requests werden automatisch neu deployed. Die URL der deployten App wird als Kommentar in dem Pull-Request gepostet. Des weiteren wird die Applikation automatisch in eine Staging-Umgebung deployed, sobald ein Pull Request in den Master-Branch integriert wird. Hierzu wird ein automatischer Pull Request in einem dedizierten Staging-Repository geöffnet. Ein Staging Repository ist bei Jenkins X eine Sammlung von Helm-Charts für verschiedene Applikationen, die unter einem sogenannten Umbrella-Chart zusammengefasst werden. Mit Hilfe dieses Charts kann ein bestimmter Zustand in einem Kubernetes Cluster erzeugt werden. Änderungen an diesem Repository werden automatisch mittels einer Jenkins-Pipeline im Cluster angewendet.

Jenkins X nennt diesen Prozess des Deployens von Features in den nächst höheren Stage Promotion. Die Promotion in die Produktivumgebung kann ausschließlich über das Kommandozeilen-Tool jx ausgeführt werden, welches wiederum den Helm Chart im Code-Repository für die Produktivumgebung aktualisiert. Damit werden Continuous Deployment-Szenarien mit Jenkins X nicht ohne Weiteres unterstützt. Jenkins X ist ein eher untypischer Vertreter der dritten Generation. Zwar werden einige Kernfunktionalitäten von Kubernetes benutzt, allerdings läuft weiterhin ein voller Jenkins nebenher. Statt ein schlankes System mit Hilfe von Events aufzubauen, wird ein alter All-Rounder mit noch mehr Möglichkeiten ausgestattet.

Zwischenfazit

Von den bisher vorgestellten Kubernetes-Native CI/CD-Systemen kann noch keins bedenkenlos empfohlen werden. Grund sind mangelnde Reife oder eine Abhängigkeit auf externe Produkte wie zum Beispiel GitHub. Vermutlich ändert sich dieser Umstand in den kommenden Monaten, weshalb eine weitere Beobachtung dieser Systeme sinnvoll ist. Aktuell reicht das aber noch nicht aus, um an die einfache Service-Integration anzuknüpfen, die CI/CD-Systeme zweiter Generation bieten. Was also tun, wenn man aktuell ein CI/CD-System für Kubernetes nutzen möchte?

Zwischenlösung: GitLab CI/CD

GitLab CI/CD bietet neben der klassischen Agent-Lösung auch die Ausführung der GitLab-Runner in Kubernetes an. Es spielt keine Rolle, ob GitLab selbst in dem betreffenden Kubernetes-Cluster betrieben wird oder nicht. Die Installation des Runners erfolgt über ein einfaches Helm-Chart. Der Runner kann einer von mehreren Gitlab-CI/CD Runnern sein. Es ist so möglich, diesen ohne viel Aufwand zu testen. Wird der Gitlab Runner zusammen mit dem Kubernetes-Executor betrieben, so startet Gitlab für jeden Schritt der Build-Pipeline einen neuen Pod im Kubernetes Cluster. Es ist so möglich die volle Kubernetes-Flexibilität zu nutzen, ohne auf ein ausgereiftes CI/CD-System zu verzichten.

Architektur mit GitLab CI Runner auf Kubernetes.
Architektur mit GitLab CI Runner auf Kubernetes.

Da alle Pods in Kubernetes mit einem Service-Account gestartet werden, können, via Kubernetes’ Role Based Access Control, Deployments im selben Cluster einfach ermöglicht werden. Es ist dann nicht nötig, Zugriffsdaten zum Kubernetes-Cluster in GitLab zu hinterlegen. Auch Gitlab CI/CD unterstützt sogenannte “Review Apps”. Wird dieses Feature genutzt, so werden Feature-Branches nach erfolgreichen Tests, automatisch in ein eigenes Environment installiert. Ein Environment besteht aus einem dedizierten Namespace, eigenen Abhängigkeiten (zum Beispiel eine Datenbank) und einer Web-Adresse, unter der diese Version der App dann erreicht werden kann. Diese Web-Adresse kann Testern oder Kunden zur Verfügung gestellte werden. Wird der Feature-Branch in den Master-Branch gemerged, so löscht GitLab den Namespace und damit alle Komponenten der App – inklusive der Web-Adresse. Wer GitLab CI/CD und Kubernetes schon heute nutzt, sollte den Kubernetes-Executor ausprobieren. Autoren von Pipelines müssen keinerlei Änderungen vornehmen und alte Runner müssen nicht weiter betrieben werden.

Titelfoto von Joseph Barrientos auf Unsplash

Fazit

Seit CI/CD-Lösungen vor inzwischen fast 20 Jahren populär geworden sind, entwickeln sie sich kontinuierlich weiter, indem sie sich an die jeweiligen Bedürfnisse in Betrieb und Softwareentwicklung anpassen. Die enorme Popularität von Kubernetes hat zur Entstehung einer neuen Generation von CI/CD geführt. Diese muss noch reifen, aber hat ein hohes Potential. Ein Hauptvorteil dieser Systeme ist ihre effiziente Ressourcennutzung. Kubernetes-Native Systeme genießen immer den Vorteil dynamischer Skalierung ohne eigenen Konfigurationsaufwand. Gut gereifte Best-Practices werden durch die neuen Systeme konsequenter angewendet:

Configuration as Code: Pipeline-Konfigurationen und Deployment-Definitionen werden in Code gegossen und sind somit versionierbar.

Pull-basiertes Deployment: Auf den Austausch von Zugangsdaten wie SSH-Keys zur Ausführung von Deployments wird verzichtet. Stattdessen werden Deployments auf der betreffenden Infrastruktur selbst ausgeführt.

Container-Zentrierung: Alle Schritte einer Build-Pipeline werden in Containern ausgeführt. Hierbei werden ausschließlich die Werkzeuge und Berechtigungen benötigt, die für diesen speziellen Schritt erforderlich sind.

Die Architektur dieser neuen Systeme nutzt Strategien der Software-Entwicklung der letzten Jahre: Statt aus einem Monolithen, der jegliche Funktionalität implementiert, bestehen sie aus kleinen, entkoppelten Komponenten, welche über Events miteinander kommunizieren. Das Cluster-Management wird nicht mehr im CI/CD System implementiert, sondern von der genutzten Infrastruktur bereitgestellt. Von den hier betrachteten Systemen sieht Tekton-CD sehr vielversprechend aus, weil es gute Architekturentscheidungen von Concourse auf die Kubernetes-Plattform übertragen hat und gleichzeitig die Konfiguration einfach gestaltet. Prow ist das einzige System der dritten Generation, welches bereits intensiv in Produktion genutzt wird. Es könnte für die Allgemeinheit interessant werden, wenn sich eine Community findet, die das Projekt als unabhängiges Produkt weiterentwickelt. Die Jenkins-Community kann trotz ihres großen Erfahrungsschatzes keine überzeugende Lösung liefern. Die Chance, den Jenkins-Monolithen durch kleine Komponenten zu ersetzen, wird nicht ergriffen. Stattdessen ist Jenkins jetzt Teil eines noch größeren Setups. Die beworbene Flexibilität, alte Jenkins-Komponenten noch nutzen zu können, kann schnell zum Nachteil werden. Letztlich können Systeme der zweiten Generation wie GitLab CI/CD heute schon die Vorteile der dynamischen Skalierung von Kubernetes nutzen, ohne dass der Nutzer einen hohen Aufwand investieren muss. Sie eignen sich daher als gute Zwischenlösung, bis die Kubernetes-Native Generation die notwendige Reife erreicht hat.