Kai Ole Hartwig
4 Min. Lesezeit
Von

Eine Runner-Flotte auf Dual-Stack-IPv6 migrieren: was kaputtging

Das Cluster auf Dual-Stack umzustellen ist eine Netzwerk-Änderung. Sie sickerte an drei Stellen in die CI durch, an keiner davon habe ich es erwartet — alle drei sind offen statt mit einem Fehler ausgefallen. Teil 3 von „The Ops Log“.

TL;DR

TL;DR: Eine geleerte Umgebungsvariable machte aus einer Image-Referenz /node:… („invalid reference format“). Job-Token-Allowlists setzten sich auf „nur sich selbst“ zurück → 403 bei projektübergreifenden Pulls. Und Component-Includes werden bei Pipeline-Erstellung aufgelöst — ein Retry alter Pipelines nutzt die kaputte Auflösung weiter; man muss frische Pipelines auslösen.

Was passiert ist

Bruch 1 — die leere Variable, die zur kaputten Referenz wurde. Release-Jobs starben, bevor auch nur eine Zeile Skript lief:

release:semver

 

Using docker image /node:24-alpine@sha256:7fdd…
ERROR: failed to pull image "/node:24-alpine@…":
        invalid reference format

 

Das Image war als ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/node:… geschrieben. Mitten in der Migration war diese Dependency-Proxy-Variable leer, also löste sich die Referenz zu einem Nonsense-String mit führendem Schrägstrich auf. Es scheiterte nicht als „unset“ — es fiel offen in eine syntaktisch ungültige Referenz. Der Fix war, für diesen Fall nicht mehr von der Proxy-Variable abzuhängen und den Mirror-Registry-Pfad explizit zu pinnen. Erkenntnis: Eine ungesetzte CI-Variable, die in eine URL oder Image-Referenz interpoliert wird, ist ein latenter Malformed-String-Bug, kein Missing-Value-Fehler.

Bruch 2 — Job-Token-Allowlists setzten sich auf „selbst“ zurück. Projektübergreifende Pulls (ein geteiltes PHP-Runtime-Image aus einem anderen Projekt) begannen mit 403 zu antworten. Die eingehende Job-Token-Allowlist hatte sich während der Rekonfiguration auf „nur sich selbst“ zurückgesetzt, sodass Tokens, die ein Projekt ausgestellt hatte, vom anderen nicht mehr akzeptiert wurden:

Group-Allowlist neu gewähren

 

$ glab api --method POST \
    projects/713/job_token_scope/groups_allowlist \
    -F target_group_id=175
{ "source_project_id": 713, "target_group_id": 175 }

 

Bruch 3 — der subtile: Retry löst nicht neu auf. Nach der Behebung der Component-Referenz habe ich bei den fehlgeschlagenen Pipelines auf „Retry“ geklickt. Immer noch kaputt. GitLab löst include: component@version bei Pipeline-Erstellung auf und friert es in dieser Pipeline ein. Ein Retry eines Jobs führt ihn gegen die alte, eingefrorene Auflösung erneut aus — die kaputte. Der einzige Weg, einen Component-Fix zu übernehmen, ist eine brandneue Pipeline:

frisch auslösen, nicht retryen

 

$ glab api --method POST "projects/599/pipeline?ref=main"
# component@main löst sich neu zum gefixten Commit auf — ein Retry hätte das nie getan

 

Unter allen drei Brüchen: Erreichbarkeit. Der praktische Mirror war IPv4-only, während die Pods IPv6-first hochkamen — alles, was auf ihn zeigte, hing fest, bis er auf den Dual-Stack-Mirror umgezogen wurde. Sobald man akzeptiert, dass eine „Netzwerk-Migration“ leise Image-Auflösung, Token-Vertrauen und Pipeline-Caching berührt, hören die Überraschungen auf, überraschend zu sein.

Die Lehre

Infra-Migrationen fallen in CI offen aus: leere Variablen werden zu kaputten Referenzen, zurückgesetzte Allowlists werden zu 403ern, und eingefrorene Component-Auflösung macht „Retry“ zur Falle. Wenn eine Component gefixt ist, eine frische Pipeline auslösen — die alte niemals retryen.

Häufige Fragen

Was hat Job-Token-Allowlists konkret zurückgesetzt?+

Die Rekonfiguration im Rahmen der Dual-Stack-Migration — die Allowlist fiel auf ihren Default (nur das eigene Projekt) zurück und musste für die Gruppe, aus der projektübergreifend gepullt wird, explizit neu gewährt werden.

Warum reicht ein Retry nach einem Component-Fix nicht?+

Weil GitLab include: component@version bei Pipeline-Erstellung auflöst und in dieser Pipeline einfriert. Retry führt Jobs gegen dieselbe eingefrorene Auflösung erneut aus; nur eine neu erstellte Pipeline löst die Component-Referenz neu auf.

Warum scheitert eine leere CI-Variable nicht mit einem klaren Fehler?+

Weil sie oft in einen String interpoliert wird (hier: eine Image-Referenz), bevor irgendetwas sie auf Gültigkeit prüft — das Ergebnis ist ein syntaktisch ungültiger, aber technisch vorhandener String, kein „unset“-Fehler.

Fazit

Eine Netzwerk-Migration bleibt selten bei der Netzwerkschicht — sie berührt Image-Auflösung, Token-Vertrauen und Pipeline-Caching auf Wegen, die erst beim Ausprobieren sichtbar werden. Alle drei Brüche fielen offen aus statt mit einem klaren Fehler, was sie schwerer zu diagnostizieren machte als nötig.

Ich begleite Ihre Dual-Stack-IPv6-Migration auf Cluster- und CI-Ebene — inklusive der Stellen, an denen Netzwerk-Änderungen sich in CI/CD verstecken.

Von Image-Auflösung über Job-Token-Vertrauen bis zu Component-Include-Caching: Ich prüfe, wo eine Netzwerk-Migration in Ihre CI durchsickert, bevor sie es tut.

Plattform-Betrieb statt Beratung auf Papier: Ich baue und härte Ihre Infrastruktur 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.