Kontrollierte Freigaben statt automatischer Deploys
when: manual und allow_failure loesen zwei unterschiedliche Probleme in GitLab CI: das eine steuert, wer einen Schritt auslösen darf, das andere, ob ein Fehlschlag die Pipeline blockiert. Richtig kombiniert entstehen daraus sichere Rollout-Gates fuer Produktions-Deploys und optionale Zusatzschritte, die niemanden aufhalten.
Inhaltsverzeichnis
- 1. Warum nicht jeder Job automatisch weiterlaufen soll
- 2. when: manual Grundlagen: Jobs die auf einen Klick warten
- 3. allow_failure Grundlagen: optionale Schritte ohne Blockade
- 4. when: manual und allow_failure zusammen fuer optionale, manuelle Schritte
- 5. Produktions-Deploy als Rollout-Gate mit protected environments
- 6. Manuelle Rollback-Jobs die die Pipeline nicht verschmutzen
- 7. Unterschied zwischen top-level when: manual und rules: mit when: manual
- 8. Timeout und expire_in fuer manuelle Jobs sinnvoll setzen
- 9. Fazit: Freigabelogik bewusst gestalten statt zufaellig kombinieren
- 10. Zusammenfassung
- 11. FAQ
1. Warum nicht jeder Job automatisch weiterlaufen soll
Eine Standard-Pipeline in GitLab CI laeuft von Stage zu Stage durch, sobald der vorherige Job erfolgreich war. Fuer Build- und Test-Schritte ist das genau das gewuenschte Verhalten: Jeder Commit soll automatisch geprueft werden, ohne dass jemand einen Knopf druecken muss. Sobald es aber um Schritte mit echten, oft irreversiblen Auswirkungen geht, etwa das Deployment auf einen Produktionsserver oder das Loeschen von Ressourcen, ist ein vollautomatischer Ablauf riskant. Ein fehlerhafter Merge, der zufaellig alle Tests besteht, wuerde sonst ungebremst live gehen.
GitLab CI bietet dafuer zwei unabhaengige, aber oft gemeinsam genutzte Steuerelemente: when: manual haelt einen Job an, bis ihn jemand explizit in der Pipeline-Ansicht startet, und allow_failure entkoppelt den Erfolg eines Jobs vom Gesamtstatus der Pipeline. Beide Schluesselwoerter loesen unterschiedliche Probleme, ergaenzen sich aber in der Praxis haeufig zu robusten Freigabe- und Rollout-Mustern.
2. when: manual Grundlagen: Jobs die auf einen Klick warten
Mit when: manual wird ein Job aus dem automatischen Ablauf herausgenommen. Er erscheint im Pipeline-Graph als Play-Button-Symbol und wartet, bis ein Nutzer mit ausreichender Berechtigung ihn manuell startet. Bis dahin bleibt die Pipeline in diesem Zweig auf pending stehen, blockiert aber nachfolgende automatische Jobs in anderen Stages nicht zwangslaeufig, sofern diese nicht per needs: explizit vom manuellen Job abhaengen.
Im Beispiel ist deploy-production ein klassischer manueller Job: Er laeuft nur in der deploy-Stage, ist an ein environment: production gebunden und wird nicht automatisch nach erfolgreichen Tests ausgeloest. Erst ein Teammitglied, das die Aenderungen fuer freigabewuerdig haelt, klickt in der GitLab-Oberflaeche auf Play. Das schafft einen bewussten menschlichen Kontrollpunkt genau dort, wo automatische Sicherheit allein nicht ausreicht.
deploy-production:
stage: deploy
environment:
name: production
url: https://shop.example.com
script:
- ./deploy.sh production
when: manual
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
3. allow_failure Grundlagen: optionale Schritte ohne Blockade
allow_failure: true kehrt die Wirkung eines fehlgeschlagenen Jobs um: Statt die gesamte Pipeline auf failed zu setzen, markiert GitLab den Job selbst mit einem gelben Warnsymbol, waehrend die Pipeline insgesamt weiterhin als erfolgreich gilt und nachfolgende Stages normal starten. Das ist ideal fuer Schritte, deren Ergebnis wichtig, aber nicht blockierend sein soll, etwa ein Security-Scan, dessen Findings manuell nachbearbeitet werden, oder ein Linter, dessen Warnungen das Team bewusst nicht als harten Gate-Kriterium behandelt.
Im Beispiel darf der security-scan-Job fehlschlagen, ohne dass der Merge-Request-Status rot wird. Das Team sieht die Warnung dennoch deutlich im Pipeline-Widget und kann sie bewusst pruefen, statt dass jeder gefundene Hinweis automatisch jeden Merge verhindert. Wichtig ist, allow_failure: true gezielt einzusetzen und nicht als generelle Loesung fuer instabile Tests, da es sonst echte Fehler unbemerkt durchrutschen laesst.
security-scan:
stage: test
script:
- composer audit --format=json
allow_failure: true
artifacts:
reports:
dependency_scanning: gl-dependency-scanning-report.json
4. when: manual und allow_failure zusammen fuer optionale, manuelle Schritte
Kombiniert man beide Schluesselwoerter in einem Job, entsteht ein Schritt, der weder automatisch startet noch, falls er ausgefuehrt wird und fehlschlaegt, die Pipeline blockiert. Das eignet sich fuer Zusatzschritte, die nur bei Bedarf laufen sollen, etwa ein aufwendiger Performance-Test vor einem grossen Release, den niemand bei jedem gewoehnlichen Merge automatisch anstossen moechte, dessen Fehlschlag aber auch kein Grund sein soll, den Merge zu verhindern.
Im Beispiel wartet performance-test darauf, manuell gestartet zu werden, und selbst ein Fehlschlag faerbt die Pipeline nicht rot. Das ist bewusst anders als bei deploy-production: Dort soll ein Fehlschlag durchaus sichtbar rot werden, weil ein missgluecktes Deployment ein echtes Problem ist. Die Entscheidung, ob allow_failure zu einem manuellen Job passt, haengt also davon ab, ob ein Fehlschlag dieses konkreten Schritts wirklich ein Blocker sein soll.
performance-test:
stage: test
script:
- k6 run --vus 50 --duration 2m loadtest.js
when: manual
allow_failure: true
5. Produktions-Deploy als Rollout-Gate mit protected environments
Fuer echte Produktions-Deploys reicht when: manual allein oft nicht aus, weil GitLab standardmaessig jedem Nutzer mit Developer-Rolle erlaubt, manuelle Jobs auszuloesen. Die Ergaenzung ist ein protected environment: In den Projekteinstellungen unter CI/CD Settings > Protected Environments laesst sich festlegen, dass nur Nutzer mit Maintainer-Rolle oder einer explizit genannten Gruppe den Job fuer environment: production starten duerfen. So bleibt when: manual technisch weiterhin einfach, die Zugriffskontrolle wird aber ueber die GitLab-Berechtigungen erzwungen statt nur ueber Konvention.
Ergaenzt man needs:, laesst sich zusaetzlich erzwingen, dass ein manueller Deploy-Job erst startbar ist, nachdem bestimmte vorherige Jobs erfolgreich durchgelaufen sind, etwa ein Smoke-Test auf der Staging-Umgebung. Damit entsteht ein mehrstufiges Gate: automatische Tests muessen gruen sein, danach entscheidet ein Mensch mit ausreichender Berechtigung ueber den finalen Schritt.
smoke-test-staging:
stage: verify
environment:
name: staging
script:
- ./smoke-test.sh staging
deploy-production:
stage: deploy
needs: ["smoke-test-staging"]
environment:
name: production
url: https://shop.example.com
script:
- ./deploy.sh production
when: manual
6. Manuelle Rollback-Jobs die die Pipeline nicht verschmutzen
Ein Rollback-Job sollte in jeder Pipeline vorhanden, aber praktisch nie ausgeloest sein. Mit when: manual bleibt er ungenutzt im Hintergrund verfuegbar, ohne den normalen Ablauf zu stoeren. Wird er dennoch gebraucht, etwa weil ein Deployment unerwartete Fehler in Produktion verursacht hat, kann ein Teammitglied ihn direkt aus derselben Pipeline heraus starten, ohne eine neue Pipeline anlegen oder einen alten Commit erneut deployen zu muessen.
allow_failure: true ist hier ebenfalls sinnvoll, allerdings aus einem anderen Grund als beim Performance-Test: Wenn der Rollback-Job nie ausgeloest wird, bleibt er im Pipeline-Status als "skipped" oder "manual" stehen und beeinflusst den Gesamtstatus ohnehin nicht negativ. Wird er jedoch ausgeloest und schlaegt selbst fehl, etwa weil das vorherige Deployment-Artefakt nicht mehr verfuegbar ist, soll das nicht die ganze Pipeline nachtraeglich als fehlgeschlagen markieren, waehrend das Team bereits manuell am eigentlichen Problem arbeitet.
rollback-production:
stage: deploy
environment:
name: production
action: stop
script:
- ./deploy.sh production --rollback-to=$PREVIOUS_STABLE_TAG
when: manual
allow_failure: true
7. Unterschied zwischen top-level when: manual und rules: mit when: manual
Wird when: manual direkt auf Job-Ebene gesetzt, gilt es fuer jede Pipeline, in der der Job ueberhaupt laeuft, unabhaengig vom Branch oder Auslöser. In vielen Faellen ist aber gewuenscht, dass ein Job auf main automatisch laeuft, aber auf Feature-Branches manuell bleibt, oder umgekehrt bei geplanten Pipelines gar nicht erst erscheint. Das laesst sich nur ueber rules: mit bedingtem when: manual abbilden, da rules: pro Bedingung ein eigenes when: erlaubt.
Im Beispiel bekommt deploy-production nur auf dem main-Branch ueberhaupt die Chance, manuell ausgeloest zu werden; auf allen anderen Branches taucht der Job in der Pipeline gar nicht auf, weil keine rules:-Bedingung zutrifft. Das ist praeziser als ein pauschales when: manual auf Job-Ebene, das den Job auf jedem Branch anzeigen wuerde, selbst dort, wo ein Produktions-Deploy fachlich nie sinnvoll ist.
deploy-production:
stage: deploy
environment:
name: production
script:
- ./deploy.sh production
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
- when: never
8. Timeout und expire_in fuer manuelle Jobs sinnvoll setzen
Manuelle Jobs koennen theoretisch beliebig lange auf ihren Trigger warten, was bei artifacts: problematisch wird, wenn die Build-Ergebnisse eines vorherigen Jobs laengst durch expire_in geloescht wurden, bevor jemand den manuellen Deploy startet. Ein zu kurzes expire_in fuehrt dazu, dass ein Deploy-Job fehlschlaegt, weil das benoetigte Artefakt nicht mehr existiert; ein zu langes expire_in verschwendet unnoetig Speicherplatz im Projekt.
Fuer Produktions-Deploy-Artefakte hat sich ein expire_in von mehreren Tagen bis zwei Wochen bewaehrt, je nachdem, wie lange realistischerweise zwischen erfolgreichem Build und tatsaechlicher Freigabe vergehen kann. Zusaetzlich laesst sich pro Job ein eigenes timeout: setzen, falls das Skript selbst haengen bleiben koennte, etwa bei einem Deployment, das auf eine nicht erreichbare externe Ressource wartet.
9. Fazit: Freigabelogik bewusst gestalten statt zufaellig kombinieren
when: manual und allow_failure beantworten zwei getrennte Fragen: Wer darf einen Schritt ausloesen, und blockiert ein Fehlschlag die Pipeline. Beide gedankenlos auf jeden kritischen Job zu setzen, ohne die jeweilige Konsequenz zu durchdenken, fuehrt entweder zu unnoetig blockierten Pipelines oder zu stillschweigend uebersehenen Produktionsfehlern.
Der sichere Ansatz ist, fuer jeden Job explizit zu entscheiden: Soll er automatisch oder manuell laufen, und soll ein Fehlschlag sichtbar rot werden oder nur als Warnung erscheinen. Kombiniert mit protected environments und needs:-Abhaengigkeiten entstehen daraus Rollout-Gates, die echte menschliche Kontrolle an den richtigen Stellen erzwingen, ohne den restlichen Automatisierungsgrad der Pipeline zu verlieren.
| Kombination | Pipeline-Status bei Fehlschlag | Startverhalten | Typischer Einsatz |
|---|---|---|---|
| when: on_success (Default) | Pipeline schlaegt fehl | Automatisch nach Vorstufe | Build, Unit-Tests |
| when: manual, kein allow_failure | Pipeline schlaegt fehl | Wartet auf manuellen Start | Produktions-Deploy |
| when: manual + allow_failure: true | Pipeline bleibt gruen | Wartet auf manuellen Start | Optionale Tests, Rollback |
| allow_failure: true, kein manual | Pipeline bleibt gruen | Automatisch nach Vorstufe | Security-Scan, Linter |
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
when: manual und allow_failure: Das Wichtigste auf einen Blick
when: manual
Haelt einen Job an, bis ihn ein berechtigter Nutzer explizit in der Pipeline-Ansicht startet.
allow_failure
Entkoppelt einen fehlgeschlagenen Job vom Gesamtstatus der Pipeline, die trotzdem als erfolgreich gilt.
Protected environments
Erzwingen, dass nur berechtigte Rollen manuelle Deploy-Jobs fuer geschuetzte Environments ausloesen duerfen.
rules: statt Job-Level
Bedingtes when: manual pro Branch oder Auslöser ist praeziser als eine pauschale Job-Einstellung.