fuer groessere Monorepos
Wenn ein Monorepo mehrere weitgehend unabhaengige Teilprojekte in einem einzigen Repository vereint, wird eine einzelne, monolithische .gitlab-ci.yml schnell unuebersichtlich und ineffizient. Parent-Child-Pipelines mit dem Keyword trigger loesen dieses Problem, indem sie jedem Teilprojekt eine eigene, unabhaengig ausfuehrbare Kind-Pipeline geben, die nur laeuft, wenn sich in ihrem Bereich tatsaechlich etwas geaendert hat.
Inhaltsverzeichnis
- 1. Warum eine monolithische Pipeline im Monorepo scheitert
- 2. Wie trigger eine Kind-Pipeline startet
- 3. strategy: depend und der Pipeline-Status
- 4. Dynamisch generierte Child-Pipeline-YAML als Artefakt
- 5. Verschachtelungstiefe und Variablen-Weitergabe
- 6. Multi-Project-Pipelines als verwandtes Konzept
- 7. Praxis-Empfehlungen fuer den Aufbau
- 8. Typische Fallstricke bei Parent-Child-Pipelines
- 9. Fazit: Parent-Child-Pipelines als Skalierungswerkzeug
- 10. Zusammenfassung
- 11. FAQ
1. Warum eine monolithische Pipeline im Monorepo scheitert
Ein Monorepo buendelt mehrere Teilprojekte, etwa mehrere Microservices, ein Frontend und ein Backend oder mehrere Bibliotheken, in einem einzigen Git-Repository, was Code-Sharing und atomare Commits ueber Projektgrenzen hinweg erleichtert. Fuer die CI/CD-Pipeline entsteht daraus aber ein strukturelles Problem: Eine einzelne .gitlab-ci.yml, die alle Jobs fuer alle Teilprojekte enthaelt, wird mit wachsender Anzahl an Teilprojekten immer laenger und unuebersichtlicher, und ohne gezielte Steuerung laeuft bei jedem Commit die komplette Pipeline fuer alle Teilprojekte, selbst wenn sich nur eine einzige Zeile in einem einzigen Microservice geaendert hat.
Dieses Verhalten verschwendet nicht nur CI-Minuten, sondern verlangsamt auch die Feedback-Schleife fuer Entwickler erheblich, weil ein kleiner Fix in einem Teilprojekt trotzdem warten muss, bis Tests fuer voellig unbeteiligte Teilprojekte durchgelaufen sind. Mit wachsender Teamgroesse und zunehmender Anzahl an Teilprojekten wird eine monolithische Pipeline zudem zu einem gemeinsamen Aenderungspunkt, an dem sich mehrere Teams gegenseitig blockieren, weil jede Aenderung an der zentralen .gitlab-ci.yml potenziell alle Teilprojekte betrifft. Parent-Child-Pipelines loesen genau dieses Problem, indem sie die Verantwortung fuer jede Teilpipeline in eine eigene Datei auslagern, die unabhaengig vom Rest gepflegt und ausgeloest werden kann.
2. Wie trigger eine Kind-Pipeline startet
Das Keyword trigger definiert einen speziellen Job-Typ, der statt eines script-Blocks auf eine andere .gitlab-ci.yml-Datei verweist und diese als eigenstaendige Kind-Pipeline startet. Aus Sicht der Parent-Pipeline erscheint der trigger-Job wie ein normaler Job mit eigenem Status, der aber intern eine komplette zweite Pipeline mit eigenen Stages, eigenen Jobs und eigenem Pipeline-Status orchestriert. Die Kind-Pipeline laeuft dabei vollstaendig unabhaengig von den uebrigen Jobs der Parent-Pipeline und kann ihre eigenen Runner, ihre eigenen Variablen und ihre eigene Stage-Struktur haben, ohne dass sich beide Konfigurationen gegenseitig beeinflussen.
Kombiniert mit rules und changes laesst sich damit genau das Monorepo-Problem loesen: Ein trigger-Job pro Teilprojekt bekommt eine changes-Bedingung, die nur greift, wenn sich Dateien im entsprechenden Unterverzeichnis geaendert haben, sodass bei einem Commit, der nur den Payment-Service betrifft, ausschliesslich die Kind-Pipeline fuer Payment gestartet wird, waehrend die Kind-Pipelines fuer alle anderen Microservices komplett uebersprungen werden. Dieses selektive Ausloesen ist der zentrale Effizienzgewinn von Parent-Child-Pipelines gegenueber einer monolithischen Struktur.
trigger_payment_service:
trigger:
include: services/payment/.gitlab-ci.yml
strategy: depend
rules:
- changes:
- services/payment/**/*
trigger_frontend:
trigger:
include: services/frontend/.gitlab-ci.yml
strategy: depend
rules:
- changes:
- services/frontend/**/*
3. strategy: depend und der Pipeline-Status
Ohne zusaetzliche Konfiguration gilt ein trigger-Job standardmaessig als erfolgreich, sobald die Kind-Pipeline erfolgreich gestartet wurde, unabhaengig davon, ob die Kind-Pipeline selbst spaeter fehlschlaegt. Das ist fuer viele Anwendungsfaelle unerwuenscht, weil ein fehlgeschlagener Test in der Kind-Pipeline dann nicht automatisch die Parent-Pipeline als fehlgeschlagen markiert. Mit strategy: depend wird dieses Verhalten geaendert: Der trigger-Job wartet, bis die Kind-Pipeline vollstaendig abgeschlossen ist, und uebernimmt danach deren tatsaechlichen Erfolgs- oder Fehlerstatus, sodass die Parent-Pipeline den echten Zustand aller Kind-Pipelines korrekt widerspiegelt.
Diese Einstellung ist besonders wichtig, wenn nachgelagerte Jobs in der Parent-Pipeline von mehreren Kind-Pipelines abhaengen, etwa ein finaler Deployment-Job, der erst laufen soll, wenn alle betroffenen Teilprojekte erfolgreich getestet wurden. Ohne strategy: depend koennte dieser Deployment-Job faelschlicherweise starten, obwohl eine der Kind-Pipelines im Hintergrund noch laeuft oder sogar fehlgeschlagen ist. In der Praxis sollte strategy: depend deshalb der Standard fuer alle trigger-Jobs sein, bei denen der tatsaechliche Erfolg der Kind-Pipeline fuer die weitere Pipeline-Logik relevant ist, und nur in seltenen Faellen bewusst weggelassen werden, etwa fuer rein informative Kind-Pipelines ohne Blockierwirkung.
4. Dynamisch generierte Child-Pipeline-YAML als Artefakt
Neben statisch im Repository liegenden Kind-Pipeline-Dateien unterstuetzt GitLab auch dynamisch generierte Kind-Pipelines, bei denen ein vorgelagerter Job eine .gitlab-ci.yml-Datei zur Laufzeit erzeugt und als Artefakt bereitstellt, das dann als Grundlage fuer den trigger-Job dient. Das ist besonders wertvoll in Monorepos, in denen sich die Anzahl oder Struktur der Teilprojekte haeufig aendert, weil die Pipeline-Struktur nicht mehr fest im Code hinterlegt werden muss, sondern zur Laufzeit aus dem tatsaechlichen Repository-Zustand abgeleitet werden kann, etwa durch ein Skript, das alle Verzeichnisse mit einer package.json oder composer.json einliest und daraus automatisch einen Job pro Teilprojekt generiert.
Diese Technik wird oft mit dem Feld include: artifact kombiniert, das explizit angibt, dass die referenzierte Datei nicht statisch im Repository liegt, sondern als Artefakt eines vorherigen Jobs erzeugt wurde. Fuer sehr grosse Monorepos mit Dutzenden Teilprojekten ist dieser Ansatz oft die einzige praktikable Loesung, weil eine manuell gepflegte Liste aller Teilprojekte in der Parent-Pipeline schnell veraltet und fehleranfaellig wird, sobald neue Teilprojekte hinzukommen oder alte entfernt werden, waehrend eine generierte Pipeline automatisch den aktuellen Stand widerspiegelt.
generate_pipeline:
stage: prepare
script:
- ./scripts/generate-child-pipeline.sh > generated-pipeline.yml
artifacts:
paths:
- generated-pipeline.yml
trigger_generated:
stage: trigger
needs: [generate_pipeline]
trigger:
include:
- artifact: generated-pipeline.yml
job: generate_pipeline
strategy: depend
5. Verschachtelungstiefe und Variablen-Weitergabe
GitLab erlaubt eine Verschachtelung von Kind-Pipelines bis zu einer festen Tiefe, wobei eine Parent-Pipeline eine Kind-Pipeline ausloesen kann, die wiederum eine eigene Kind-Pipeline ausloest. Diese sogenannten Multi-Project- oder mehrstufigen Pipelines sind hilfreich fuer sehr grosse Organisationen mit hierarchisch organisierten Teilprojekten, sollten aber mit Bedacht eingesetzt werden, weil jede zusaetzliche Verschachtelungsebene die Nachvollziehbarkeit erschwert und Debugging aufwendiger macht, wenn ein Fehler tief in einer verschachtelten Kind-Pipeline auftritt und erst durch mehrere Ebenen zurueckverfolgt werden muss.
Fuer die Weitergabe von Variablen an eine Kind-Pipeline bietet trigger das Unter-Keyword variables, mit dem gezielt Werte an die Kind-Pipeline uebergeben werden koennen, unabhaengig von den global definierten Variablen der Parent-Pipeline. Zusaetzlich kann mit forward gesteuert werden, ob Pipeline-Variablen und YAML-Variablen der Parent-Pipeline automatisch an die Kind-Pipeline weitergereicht werden sollen, was besonders bei dynamisch generierten Pipelines wichtig ist, um zu vermeiden, dass sensible oder projektspezifische Variablen ungewollt in Kind-Pipelines fuer andere Teilprojekte landen.
trigger_service_a:
trigger:
include: services/service-a/.gitlab-ci.yml
strategy: depend
forward:
pipeline_variables: true
yaml_variables: false
variables:
SERVICE_NAME: service-a
DEPLOY_ENV: staging
6. Multi-Project-Pipelines als verwandtes Konzept
Neben Parent-Child-Pipelines innerhalb desselben Repositories unterstuetzt trigger auch das Ausloesen von Pipelines in einem komplett anderen GitLab-Projekt, sogenannte Multi-Project-Pipelines. Statt einer include-Datei wird dabei mit project: der Pfad zu einem anderen Projekt angegeben, wodurch ein Job in Projekt A eine Pipeline in Projekt B startet, etwa wenn ein zentrales Infrastruktur-Repository nach jedem erfolgreichen Build eines Anwendungsrepositories automatisch ein Deployment anstossen soll. Multi-Project-Pipelines sind konzeptionell verwandt mit Parent-Child-Pipelines, unterscheiden sich aber dadurch, dass die beiden beteiligten Pipelines in getrennten Projekten mit eigenen Berechtigungen und eigener Versionierung liegen.
Fuer Monorepos ist die trigger:include-Variante mit Kind-Pipelines im selben Repository meist die passendere Wahl, weil Aenderungen an Teilprojekt-Pipeline und Teilprojekt-Code in einem einzigen Commit landen und dieselbe Versionsgeschichte teilen. Multi-Project-Pipelines eignen sich dagegen besser fuer tatsaechlich getrennte Repositories, die organisatorisch oder aus Sicherheitsgruenden nicht zusammengelegt werden sollen, aber trotzdem eine koordinierte Pipeline-Ausfuehrung benoetigen, etwa zwischen einem separaten Infrastructure-as-Code-Repository und mehreren Anwendungsrepositories.
7. Praxis-Empfehlungen fuer den Aufbau
Fuer den Einstieg empfiehlt sich eine klare Verzeichnisstruktur, in der jedes Teilprojekt seine eigene .gitlab-ci.yml in seinem eigenen Unterverzeichnis pflegt, waehrend die Parent-Pipeline im Repository-Root nur noch aus einer Liste von trigger-Jobs mit passenden changes-Bedingungen besteht. Diese Trennung macht klar, wer fuer welche Pipeline-Logik verantwortlich ist, und erlaubt es Teams, ihre eigene Kind-Pipeline weitgehend unabhaengig von anderen Teams zu aendern, ohne Merge-Konflikte in einer gemeinsamen, riesigen .gitlab-ci.yml zu riskieren.
Wichtig ist ausserdem, gemeinsame Job-Definitionen, etwa fuer Linting oder Security-Scans, die in mehreren Teilprojekten identisch benoetigt werden, in eine zentrale, wiederverwendbare Datei auszulagern und ueber include in jede Kind-Pipeline einzubinden, statt sie in jedem Teilprojekt zu duplizieren. So bleibt die Struktur trotz mehrerer unabhaengiger Kind-Pipelines konsistent, und Aenderungen an gemeinsamen Standards muessen nur an einer Stelle gepflegt werden, was gerade bei vielen Teilprojekten den Unterschied zwischen wartbarer und chaotisch wachsender CI-Konfiguration ausmacht.
8. Typische Fallstricke bei Parent-Child-Pipelines
Ein haeufiger Fehler ist, strategy: depend zu vergessen und sich dann zu wundern, warum die Parent-Pipeline trotz fehlgeschlagener Kind-Pipeline als gruen angezeigt wird. Ein zweiter haeufiger Fehler betrifft changes-Bedingungen, die zu eng oder zu weit gefasst sind: Wird nur genau das Teilprojektverzeichnis in changes eingetragen, aber eine gemeinsam genutzte Bibliothek in einem anderen Verzeichnis geaendert, die das Teilprojekt tatsaechlich beeinflusst, laeuft die entsprechende Kind-Pipeline faelschlicherweise nicht, obwohl sie eigentlich laufen sollte, weil GitLab keine automatische Abhaengigkeitsanalyse ueber Verzeichnisgrenzen hinweg durchfuehrt.
Ein dritter Fallstrick betrifft die Sichtbarkeit von Fehlern: Da eine Kind-Pipeline in der Standardansicht der Parent-Pipeline nur als einzelner Job erscheint, muessen Entwickler aktiv in die Kind-Pipeline hineinklicken, um Details zu einem Fehlschlag zu sehen, was gerade fuer neue Teammitglieder zunaechst unintuitiv sein kann. Es lohnt sich deshalb, in der Job-Beschreibung des trigger-Jobs oder in der Team-Dokumentation kurz zu erklaeren, dass ein roter trigger-Job bedeutet, dass in der verlinkten Kind-Pipeline nachgeschaut werden muss, statt den Fehler direkt im Parent-Log zu suchen.
9. Fazit: Parent-Child-Pipelines als Skalierungswerkzeug
Parent-Child-Pipelines mit trigger sind das zentrale Werkzeug, um Monorepos mit mehreren Teilprojekten in GitLab CI beherrschbar zu halten, weil sie selektives Ausloesen ueber changes-Bedingungen, unabhaengige Kind-Pipeline-Konfigurationen und bei Bedarf dynamisch generierte Pipeline-Strukturen ermoeglichen. Richtig konfiguriert mit strategy: depend und klar getrennten Verantwortlichkeiten pro Teilprojekt skaliert dieser Ansatz auch fuer Monorepos mit Dutzenden Teilprojekten deutlich besser als eine einzelne monolithische Pipeline-Datei.
Die folgende Tabelle vergleicht die wichtigsten Merkmale von monolithischen Pipelines, statischen Parent-Child-Pipelines und dynamisch generierten Child-Pipelines als Entscheidungshilfe fuer die eigene Monorepo-Struktur.
| Merkmal | Monolithische Pipeline | Statische Child-Pipeline | Dynamische Child-Pipeline |
|---|---|---|---|
| Ausloesen bei Aenderung | immer alle Jobs | gezielt per changes-Bedingung | gezielt, zur Laufzeit ermittelt |
| Pflegeaufwand bei neuen Teilprojekten | zentrale Datei waechst | neue Datei plus neuer trigger-Job | automatisch aus Repository-Struktur |
| Fehlerstatus-Weitergabe | direkt sichtbar | nur mit strategy: depend korrekt | nur mit strategy: depend korrekt |
| Geeignet fuer | kleine, einfache Repos | Monorepos mit stabiler Struktur | Monorepos mit haeufig wechselnder Struktur |
Mironsoft
CI/CD-Pipelines, Zero-Downtime-Deployments und Release-Automatisierung
Deployments, die ohne Ausfallzeit und ohne Nervenkitzel laufen?
Wir prüfen bestehende GitLab-Pipelines auf fragile Deployment-Schritte und fehlende Absicherung und bauen daraus einen Release-Prozess mit Zero-Downtime-Deployments, automatisierten Checks und einem Rollback, dem ihr im Ernstfall vertrauen könnt.
Pipeline-Review
Bestehende .gitlab-ci.yml auf Fragilität, fehlende Stages und Sicherheitslücken prüfen.
Zero-Downtime-Deployment
Symlink-Releases, Health-Checks und Rollback-Strategien für Magento-Shops aufbauen.
CI/CD-Automatisierung
Tests, Security-Scans und Deployments zu einer zuverlässigen Pipeline verbinden.
10. Zusammenfassung
Child/Parent-Pipelines: Das Wichtigste auf einen Blick
Kernidee
trigger startet eine eigenstaendige Kind-Pipeline pro Teilprojekt, statt alle Jobs in einer monolithischen Datei zu buendeln.
Selektives Ausloesen
changes-Bedingungen pro trigger-Job sorgen dafuer, dass nur tatsaechlich betroffene Teilpipelines laufen.
Kritische Einstellung
strategy: depend sorgt dafuer, dass die Parent-Pipeline den echten Erfolgs- oder Fehlerstatus der Kind-Pipeline uebernimmt.
Fuer sehr grosse Monorepos
Dynamisch generierte Child-Pipeline-YAML als Artefakt vermeidet manuell gepflegte, schnell veraltende Trigger-Listen.