Kai Ole Hartwig
5 Min. Lesezeit
Von

Man kann CI-Jobs nicht einfach blind zusammenlegen

Acht Lint-Jobs zu einem zusammenzulegen spart Runner-Slots auf einem kleinen Pool — verlockend, aber es wirft still Job-für-Job-Verhalten weg, von dem man gar nicht wusste, dass man sich darauf verlassen hatte. Teil 2 von „The Ops Log“.

TL;DR

TL;DR: N Jobs zu einem zusammenzulegen verliert per-Job allow_failure, per-Job Artefakte/JUnit und Rot/Grün pro Check in der UI. Den zusammengelegten Job additiv aufbauen und differenziell validieren — neu gegen alt auf einem echten Repo laufen lassen und die Verdikte vergleichen — bevor irgendetwas fleet-weit getauscht wird.

Was passiert ist

Die Lint-Stage lief mit acht separaten Jobs auf demselben PHP-Image: composer normalize / validate / psr, php-lint, json, yaml, typoscript, commit-msg. Acht Jobs bedeuten acht Pulls desselben Images und acht Runner-Slots. Auf einem Spot-Pool, der bei sechzehn gleichzeitigen Jobs deckelt, ist das der Flaschenhals. Sie zu einem Job zu falten → ein Pull, ein Slot. Klingt nach einem leichten Gewinn.

Was „ein Job“ leise kostet, fällt erst auf, wenn es ein konkretes Repo trifft:

Der zusammengelegte Job muss also die Semantik, die er einzieht, neu implementieren. Change-Gating wandert ins Skript (jeder Check läuft nur, wenn Dateien betroffen sind, die ihn angehen — fail-safe: Kann die Diff-Basis nicht aufgelöst werden, läuft alles statt still grün zu überspringen). Und allow_failure kommt als per-Check-soft-checks-Input zurück:

soft-checks — allow_failure pro Check

 

run() { # $1=key $2=label $3=fn
  if ! ( set -eu; "$3" ); then
    case " $SOFT " in
      *" $1 "*) echo "⚠ $2 fehlgeschlagen — soft, blockiert den Job nicht" ;;
      *)        echo "✗ fehlgeschlagen: $2"; FAILED=1 ;;
    esac
  fi
}

 

Dann — und das ist der eigentliche Punkt — nicht darauf vertrauen, weil das YAML lintet und bash -n durchläuft. Differenziell validieren: den neuen Job additiv ausliefern (die alten acht unangetastet lassen), ihn bei einem echten Consumer neben die Originale stellen, und die Verdikte Check-für-Check auf demselben Commit vergleichen.

Genau dieser eine Test hat drei Dinge aufgedeckt, die keine noch so gründliche Lektüre gefunden hätte:

differenzieller Lauf — erster Consumer

 

lint:composer:normalize   failed   (allow_failure — toleriert)
lint:composer:validate    success
lint:composer:psr-verify  success
lint:php:all              failed   ← warum?
  ── composer normalize   ⚠ soft   soft-checks funktioniert ✓
  ── composer validate    ok
  ── json lint            ✗       jsonlint-Binary fehlt → siehe Teil 1

Nichts davon ist der Happy Path. Eine grüne Pipeline auf dem neuen Job hätte „bestanden“, ohne irgendetwas über Äquivalenz zu beweisen. Die Schlussfolgerung, die ich mir immer wieder neu erarbeite: Die zusammengelegte Abstraktion bleibt Opt-in für Repos, die sauber durchlaufen, und der flottenweite Swap wird zu einer geplanten Migration — weil jeder Consumer, der eine Per-Job-Einstellung überschrieben hat, kartiert werden muss, nicht angenommen.

Die Lehre

Eine grüne Pipeline beweist den Happy Path, nicht dass Ihr neues Ding äquivalent zum alten ist. Differenzielle Validierung an einem echten Consumer ist die günstigste Versicherung, die Sie vor einer flottenweiten Änderung kaufen können.

Häufige Fragen

Warum wird die Migration pro Consumer statt fleet-weit auf einmal gemacht?+

Weil jeder Consumer, der eine Job-Einstellung individuell überschrieben hat (z. B. allow_failure), kartiert werden muss — ein blinder flottenweiter Swap würde genau diese Anpassungen verlieren.

Reicht ein grüner Testlauf auf einem Repo als Beweis?+

Nein — ein einzelner grüner Lauf zeigt nur den Happy Path. Erst der Vergleich Verdikt-für-Verdikt gegen die alten Jobs auf demselben Commit deckt Abweichungen wie fehlende allow_failure-Toleranz auf.

Wann lohnt sich Job-Konsolidierung trotzdem?+

Wenn der Runner-Pool tatsächlich der Flaschenhals ist (hier: 16 gleichzeitige Slots) und die verlorene Semantik — per-Job allow_failure, Artefakte, Parallelität — im zusammengelegten Job bewusst nachgebaut wird, statt stillschweigend zu verschwinden.

Fazit

Job-Konsolidierung spart Runner-Slots, aber nur auf dem Papier umsonst. Jede per-Job-Einstellung, die verschwindet, muss im zusammengelegten Job bewusst nachgebaut werden — und der einzige verlässliche Nachweis, dass das gelungen ist, ist ein differenzieller Lauf gegen einen echten Consumer, nicht ein grüner YAML-Lint.

Ich baue und validiere Ihre CI-Pipeline-Konsolidierung — additiv, differenziell getestet, ohne stille Verhaltensverluste.

Von der Job-Analyse (welche Per-Job-Einstellungen existieren wirklich?) über additive Migration bis zur differenziellen Validierung gegen reale Consumer, bevor irgendetwas flottenweit getauscht wird.

Plattform-Betrieb statt Beratung auf Papier: Ich baue und pflege Ihre CI/CD-Pipelines laufend.

Termin buchen →

Über den Autor

Foto von Kai Ole Hartwig.

Kai Ole Hartwig

Freiberuflicher DevSecOps-Berater · OnlyOle Consulting

Programmiert seit 2002 – autodidaktisch gelernt, 2012 mit KO-Web selbständig gemacht. Über 100 Projekte, Fokus auf Security, Performance, Automatisierung und Qualität. Heute freiberuflich: DevSecOps-Beratung, Schulungen und Softwareentwicklung.