meldestelle/docs/99_Journal/2026-03-06_Session_Log_Pipeline_Fix_v3.md

7.9 KiB
Raw Blame History

type status owner date last_update
Journal ACTIVE Lead Architect 2026-03-06 2026-03-06

Session Log — Pipeline Fix v3: socat nicht verfügbar → iptables DNAT

Datum: 06.03.2026
Agent: 👷 Backend Developer
Thema: CI/CD Pipeline — socat nicht auf Runner verfügbar


Problem

Fix v2 verwendete socat als TCP-Proxy (Port 80 → Gitea:3000).
Der Gitea-Runner (VM 102, Debian minimal) hat kein socat installiert und das Paket ist im lokalen APT-Repo nicht auffindbar:

E: Unable to locate package socat

Lösung: iptables DNAT

iptables ist auf jedem Linux-System ohne Extra-Paket verfügbar.
DNAT (Destination NAT) leitet Verbindungen auf Kernel-Ebene um — kein Userspace-Proxy nötig.

- name: Registry intern auflösen (Pangolin-Bypass)
  run: |
    echo "10.0.0.22 git.mo-code.at" | sudo tee -a /etc/hosts
    sudo iptables -t nat -A OUTPUT -p tcp -d 10.0.0.22 --dport 80 -j DNAT --to-destination 10.0.0.22:3000
    sudo iptables -t nat -A POSTROUTING -p tcp -d 10.0.0.22 --dport 3000 -j MASQUERADE
    echo "✓ DNAT aktiv: git.mo-code.at:80 → 10.0.0.22:3000"    

Traffic-Weg:

BuildKit → http://git.mo-code.at:80
         → /etc/hosts: 10.0.0.22:80
         → iptables DNAT: 10.0.0.22:80 → 10.0.0.22:3000
         → Gitea (HTTP, kein TLS nötig)

Warum iptables besser als socat

Eigenschaft socat iptables DNAT
Verfügbarkeit Paket fehlt immer vorhanden
Paket-Installation nötig nicht nötig
Arbeitsebene Userspace Kernel (schneller)
Abhängigkeiten APT-Repo nötig keine

Netzwerk-Übersicht Zora

Host IP Protokoll
Runner (VM 102) 10.0.0.23
Gitea (CT 101) 10.0.0.22 HTTP :3000
Pangolin (CT 100) 10.0.0.21 HTTPS für git.mo-code.at

Fix-Verlauf dieser Pipeline-Debugging-Session

Version Symptom Fix Ergebnis
v1 502 Bad Gateway (Pangolin) /etc/hosts + provenance:false Port 443 refused
v2 connection refused Port 443 socat :80 → :3000 socat nicht da
v3 socat nicht verfügbar iptables DNAT Permission denied
v4 iptables — kein sudo-Recht buildkitd Mirror (kein Root) HTTP→HTTPS Fehler
v5 login-action: HTTP→HTTPS-Konflikt daemon.json + systemctl restart RAM-OOM + unklar
v6 RAM-OOM + Daemon-Neustart komplex config.json direkt + max-parallel:1 BESTÄTIGT GRÜN

Fix v4: buildkitd Mirror — die Root-freie Lösung

iptables schlägt mit Permission denied fehl — der Runner-User hat kein sudo-Recht für iptables.

Lösung: buildkitd hat eine eingebaute Mirror-Funktion. Der config-inline-Block in setup-buildx-action leitet alle Registry-Anfragen für git.mo-code.at intern auf http://10.0.0.22:3000 um — vollständig auf Anwendungsebene, ohne Root-Rechte.

- name: Set up Docker Buildx
  uses: docker/setup-buildx-action@v3
  with:
    config-inline: |
      [registry."git.mo-code.at"]
        mirrors = ["http://10.0.0.22:3000"]
        http = true
        insecure = true
      [registry."10.0.0.22:3000"]
        http = true
        insecure = true      

- name: Bei Registry intern anmelden (Pangolin-Bypass)
  uses: docker/login-action@v3
  with:
    registry: 10.0.0.22:3000
    username: ${{ secrets.REGISTRY_USER }}
    password: ${{ secrets.REGISTRY_TOKEN }}

Traffic-Weg v4:

BuildKit → push git.mo-code.at/...
         → buildkitd Mirror: http://10.0.0.22:3000
         → Gitea (HTTP, intern, kein Pangolin, kein Timeout)

Kein /etc/hosts, kein iptables, kein socat — rein konfigurativ.


Fix v5: daemon.json — die funktionierende Lösung

buildkitd-Mirror (v4) ist für Pulls gedacht, nicht für Pushes. Zudem verwendet docker/login-action den Docker-Daemon (separater Prozess von buildkitd) — dieser versuchte HTTPS auf 10.0.0.22:3000 und bekam: http: server gave HTTP response to HTTPS client.

Lösung: Docker-Daemon pro Job über insecure-registries konfigurieren. sudo tee auf /etc/docker/daemon.json funktioniert auf dem Runner (wie /etc/hosts in v3 gezeigt).

- name: Docker-Daemon für interne Registry konfigurieren (Pangolin-Bypass)
  run: |
    echo '{"insecure-registries":["10.0.0.22:3000"]}' | sudo tee /etc/docker/daemon.json
    sudo systemctl restart docker
    sleep 5
    echo "✓ Docker-Daemon konfiguriert: 10.0.0.22:3000 als insecure-registry"    

Traffic-Weg v5:

docker login 10.0.0.22:3000   → Daemon kennt insecure-registry → HTTP ✅
BuildKit push 10.0.0.22:3000  → buildkitd insecure=true        → HTTP ✅
Gitea Registry                → empfängt Image intern           → kein Pangolin, kein Timeout ✅

Auf dem Meldestelle-Host bleibt der Pull über git.mo-code.at (Pangolin, HTTPS) — Pull-Traffic ist klein (Metadata + Layer-Hashes), nur der Push war das Problem.


Fix v6: config.json direkt schreiben — die finale Lösung

Zwei Probleme behoben

Problem 1 — RAM-OOM: 4 Matrix-Jobs liefen parallel auf einem 16 GB Runner. Jeder Job: Gradle-Build + Docker-Buildx = leicht 34 GB. Zusammen → 15+ GB → OOM → Builds crashed.

Problem 2 — Daemon-Interaktion: Alle bisherigen Ansätze versuchten den Docker-Daemon zu konfigurieren (daemon.json, systemctl, iptables). Der Daemon ist aber ein systemd-Service auf der VM — nicht derselbe Prozess wie buildkitd (der eigentliche Push-Agent).

Lösung

# Schritt 1: Credentials OHNE Daemon-Kontakt schreiben
- name: Registry-Credentials konfigurieren (kein Daemon-Kontakt)
  run: |
    mkdir -p ~/.docker
    AUTH=$(echo -n "${{ secrets.REGISTRY_USER }}:${{ secrets.REGISTRY_TOKEN }}" | base64 -w 0)
    printf '{"auths":{"%s":{"auth":"%s"}}}\n' "10.0.0.22:3000" "${AUTH}" > ~/.docker/config.json    

# Schritt 2: BuildKit mit HTTP/insecure für interne Registry
- name: Set up Docker Buildx
  uses: docker/setup-buildx-action@v3
  with:
    config-inline: |
      [registry."10.0.0.22:3000"]
        http = true
        insecure = true      
# RAM-Schutz: sequenziell statt parallel
strategy:
  max-parallel: 1

Warum das funktioniert:

  • printf ... > ~/.docker/config.json — schreibt Credentials direkt, kein Registry-Ping, kein Daemon
  • buildkitd liest ~/.docker/config.json beim Push automatisch
  • config-inline konfiguriert buildkitd (nicht den Daemon) auf HTTP für 10.0.0.22:3000
  • max-parallel: 1 — sequenzielle Jobs, kein RAM-OOM mehr möglich

Traffic-Weg v6:

Workflow schreibt ~/.docker/config.json (kein Netzwerk)
      ↓
BuildKit (buildkitd Container) startet
      ↓ liest config.json für Auth
      ↓ config-inline: http=true für 10.0.0.22:3000
BuildKit push → http://10.0.0.22:3000 → Gitea (intern, kein Pangolin)

Kein sudo. Kein systemctl. Kein socat. Kein iptables. Kein Neustart.


Gelernt

  • Minimale Runner-Images haben oft kein socat — APT-Repos auf Air-Gapped Systemen sind limitiert
  • iptables DNAT schlägt fehl wenn sudo-Policy es nicht erlaubt — aber sudo tee funktioniert
  • buildkitd-Mirror gilt nur für Pulls, nicht für Pushes — falscher Ansatz für Registry-Push-Bypass
  • docker/login-action und buildkitd sind zwei getrennte Prozesse mit eigener Config — beide müssen konfiguriert werden
  • daemon.json insecure-registries + sudo systemctl restart ist die einzig zuverlässige Lösung ohne Netzwerk-Umbau