install-test: k3d im Auftragscontainer erreicht seinen eigenen Cluster nicht (Teil von #225) #15

Closed
opened 2026-09-16 16:03:30 +02:00 by jlab-bot · 1 comment
Member

Migriert aus jk-cluster/helm-charts#227 — urspruenglich angelegt von jk-cluster-bot am 2026-09-02.

Verweise der Form #123 im folgenden Text beziehen sich auf die GitHub-Nummerierung, nicht auf die Nummern in diesem Repo. Geschlossene Issues wurden nicht mitgezogen und sind weiterhin nur auf GitHub lesbar.


Teil von #225, Stufe 2. Die Runner stehen seit
jk-cluster/cluster-config#201; damit ist die dortige Frage „Trägt ein Runner den
Install-Test?" beantwortbar geworden — und die Antwort lautet: noch nicht ganz.

Von den fünf Aufgaben in validate-charts.yml laufen vier ohne jede Änderung.
detect-changed-charts (git/jq), build-chart (helm lint/package),
quantity-render (helm template) und trivy-config brauchen kein Docker. Auch
publish-helm-chart.yml nicht — helm package und ein curl-Upload.

Bleibt install-test. Der legt mit k3d einen k3s-Cluster an, und das ist der
eine Auftrag im ganzen Repository, der einen Docker-Daemon braucht.

Was drüben bereitsteht

Zwei Runner im Namensraum jlab-git, beide mit rootless dind:

Label Socket im Auftragscontainer Grenzen des Daemons
ubuntu-latest, ubuntu-24.04 nein (docker_host: "-") 4 CPU, 6Gi, 20Gi Abbilder
ubuntu-docker, ubuntu-24.04-docker ja (docker_host: "") 6 CPU, 12Gi, 40Gi Abbilder

Damit ist zugleich die unter #225 offene Frage nach den Labels beantwortet.
Kleine Korrektur zur dortigen Annahme: es sind nicht 4 vCPU, der
ubuntu-docker-Runner darf bis 6 CPU und 12Gi ziehen, bei Requests von 500m und
2Gi. Er landet auf jk-worker-1 oder jk-worker-2 (64 bzw. 32 GB), die
Steuerknoten tragen CriticalAddonsOnly.

Das eigentliche Problem: k3d findet seinen eigenen Cluster nicht

Der dind-Container ist ein nativer Sidecar im selben Pod wie der Runner, teilt
sich also dessen Netzwerk-Namensraum. Der Auftragscontainer dagegen ist ein
gewöhnlicher Docker-Container, den dieser Daemon anlegt, und hängt an einer
Bridge im Daemon — nicht am Pod.

k3d veröffentlicht den Port des API-Servers auf dem Docker-Host. Das ist hier
der dind-Container, also der Pod. In die Kubeconfig schreibt k3d entsprechend
https://0.0.0.0:<port> — und aus dem Auftragscontainer heraus zeigt das auf ihn
selbst, wo nichts lauscht. Dazu kommt, dass der Runner pro Auftrag ein eigenes
Docker-Netz anlegt (container.network ist leer), während k3d sich sein eigenes
Netz k3d-install-test baut. Zwei Bridges, keine Verbindung.

Der Cluster entsteht also vermutlich sauber, und der erste kubectl läuft in eine
Zeitüberschreitung.

Gute Nachricht: ci/run-install-test.sh benutzt schlicht den eingestellten
kubectl-Kontext. Die Arbeit liegt vollständig im Workflow, die 179 Zeilen des
Skripts bleiben unberührt.

Ansätze — Spuren, nicht erprobt

Keiner davon ist gemessen; das gehört in den Wegwerf-Workflow aus #225, Stufe 2,
bevor validate-charts.yml angefasst wird.

  1. k3d in das Netz des Auftragscontainers hängen. Der Auftrag hat den Socket,
    kann sich also selbst befragen und das Netz an k3d durchreichen:

    JOB_NET=$(docker inspect -f '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}{{end}}' "$(hostname)")
    k3d cluster create install-test --image "$K3S_IMAGE" --network "$JOB_NET" --wait
    

    Dann liegen k3s und Auftrag auf derselben Bridge, und Dockers eingebautes DNS
    löst die Containernamen auf. Das ist der Ansatz, der ohne Vorbereitung
    auskommt.

  2. Die Kubeconfig umschreiben, von 0.0.0.0 auf
    https://k3d-install-test-serverlb:6443. Zu prüfen ist dabei, ob das
    Zertifikat des API-Servers diesen Namen führt — sonst braucht es zusätzlich
    ein --k3s-arg '--tls-san=k3d-install-test-serverlb@server:*'.

  3. K3D_FIX_DNS und K3D_FIX_MOUNTS. k3d bringt diese Umgebungsvariablen
    ausdrücklich für Läufe innerhalb eines Containers mit. Ob sie hier nötig sind,
    ist offen.

  4. Ein festes container.network am Runner wäre der unbequemere Weg: der
    Runner legt ein benanntes Netz nie selbst an, und der dind-Daemon ist
    flüchtig — das Netz müsste bei jedem Pod-Start erzeugt werden. Nur als
    Rückfallebene.

Ein Missverständnis, dem man zuvorkommen sollte

In der Runner-Konfiguration steht container.privileged: false. Das betrifft
nur die Container, die der Runner selbst anlegt, also den Auftragscontainer.
Die k3s-Knoten legt der Auftrag über den Socket direkt beim Daemon an, und dabei
greift diese Einstellung nicht — k3d bekommt sein --privileged. Wer bei einem
Fehlschlag an dieser Stelle schraubt, ändert nichts an der Ursache und gibt
stattdessen jedem Auftragscontainer mehr Rechte.

Reihenfolge, die man nicht umgehen kann

runs-on: ubuntu-docker bricht die GitHub-Seite, dort gibt es das Label nicht.
JLab-Git liest .gitea/workflows und .github/workflows, beide Systeme lesen
also vorerst dieselbe Datei. Der Wechsel des runs-on gehört damit hinter Stufe 6
aus #225 — Actions auf GitHub abschalten —, oder vorübergehend in eine eigene
Datei unter .gitea/workflows.

Für den Probelauf spielt das keine Rolle: ein Wegwerf-Workflow entsteht ohnehin
neu und kann ubuntu-docker von Anfang an fordern.

Fertig, wenn

  • install-test auf ubuntu-docker einen k3d-Cluster anlegt und kubectl ihn
    erreicht,
  • ein Chart-Install darin innerhalb der 30 Minuten durchläuft,
  • und das über mehrere Läufe stabil ist. Ein sporadisch roter Install-Test ist
    die schlechteste Variante — #225 sagt das zu Recht: man glaubt ihm dann nicht
    mehr.
> Migriert aus [`jk-cluster/helm-charts#227`](https://github.com/jk-cluster/helm-charts/issues/227) — urspruenglich angelegt von **jk-cluster-bot** am 2026-09-02. > > Verweise der Form `#123` im folgenden Text beziehen sich auf die **GitHub-Nummerierung**, nicht auf die Nummern in diesem Repo. Geschlossene Issues wurden nicht mitgezogen und sind weiterhin nur auf GitHub lesbar. --- Teil von #225, Stufe 2. Die Runner stehen seit jk-cluster/cluster-config#201; damit ist die dortige Frage „Trägt ein Runner den Install-Test?" beantwortbar geworden — und die Antwort lautet: noch nicht ganz. Von den fünf Aufgaben in `validate-charts.yml` laufen vier ohne jede Änderung. `detect-changed-charts` (git/jq), `build-chart` (helm lint/package), `quantity-render` (helm template) und `trivy-config` brauchen kein Docker. Auch `publish-helm-chart.yml` nicht — `helm package` und ein `curl`-Upload. Bleibt `install-test`. Der legt mit k3d einen k3s-Cluster an, und das ist der eine Auftrag im ganzen Repository, der einen Docker-Daemon braucht. ## Was drüben bereitsteht Zwei Runner im Namensraum `jlab-git`, beide mit rootless dind: | Label | Socket im Auftragscontainer | Grenzen des Daemons | |---|---|---| | `ubuntu-latest`, `ubuntu-24.04` | **nein** (`docker_host: "-"`) | 4 CPU, 6Gi, 20Gi Abbilder | | `ubuntu-docker`, `ubuntu-24.04-docker` | **ja** (`docker_host: ""`) | 6 CPU, 12Gi, 40Gi Abbilder | Damit ist zugleich die unter #225 offene Frage nach den Labels beantwortet. Kleine Korrektur zur dortigen Annahme: es sind nicht 4 vCPU, der `ubuntu-docker`-Runner darf bis 6 CPU und 12Gi ziehen, bei Requests von 500m und 2Gi. Er landet auf `jk-worker-1` oder `jk-worker-2` (64 bzw. 32 GB), die Steuerknoten tragen `CriticalAddonsOnly`. ## Das eigentliche Problem: k3d findet seinen eigenen Cluster nicht Der dind-Container ist ein nativer Sidecar im selben Pod wie der Runner, teilt sich also dessen Netzwerk-Namensraum. Der **Auftragscontainer** dagegen ist ein gewöhnlicher Docker-Container, den dieser Daemon anlegt, und hängt an einer Bridge im Daemon — nicht am Pod. k3d veröffentlicht den Port des API-Servers auf dem *Docker-Host*. Das ist hier der dind-Container, also der Pod. In die Kubeconfig schreibt k3d entsprechend `https://0.0.0.0:<port>` — und aus dem Auftragscontainer heraus zeigt das auf ihn selbst, wo nichts lauscht. Dazu kommt, dass der Runner pro Auftrag ein eigenes Docker-Netz anlegt (`container.network` ist leer), während k3d sich sein eigenes Netz `k3d-install-test` baut. Zwei Bridges, keine Verbindung. Der Cluster entsteht also vermutlich sauber, und der erste `kubectl` läuft in eine Zeitüberschreitung. Gute Nachricht: `ci/run-install-test.sh` benutzt schlicht den eingestellten kubectl-Kontext. Die Arbeit liegt vollständig im Workflow, die 179 Zeilen des Skripts bleiben unberührt. ## Ansätze — Spuren, nicht erprobt Keiner davon ist gemessen; das gehört in den Wegwerf-Workflow aus #225, Stufe 2, bevor `validate-charts.yml` angefasst wird. 1. **k3d in das Netz des Auftragscontainers hängen.** Der Auftrag hat den Socket, kann sich also selbst befragen und das Netz an k3d durchreichen: ```sh JOB_NET=$(docker inspect -f '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}{{end}}' "$(hostname)") k3d cluster create install-test --image "$K3S_IMAGE" --network "$JOB_NET" --wait ``` Dann liegen k3s und Auftrag auf derselben Bridge, und Dockers eingebautes DNS löst die Containernamen auf. Das ist der Ansatz, der ohne Vorbereitung auskommt. 2. **Die Kubeconfig umschreiben**, von `0.0.0.0` auf `https://k3d-install-test-serverlb:6443`. Zu prüfen ist dabei, ob das Zertifikat des API-Servers diesen Namen führt — sonst braucht es zusätzlich ein `--k3s-arg '--tls-san=k3d-install-test-serverlb@server:*'`. 3. **`K3D_FIX_DNS` und `K3D_FIX_MOUNTS`.** k3d bringt diese Umgebungsvariablen ausdrücklich für Läufe innerhalb eines Containers mit. Ob sie hier nötig sind, ist offen. 4. **Ein festes `container.network` am Runner** wäre der unbequemere Weg: der Runner legt ein benanntes Netz nie selbst an, und der dind-Daemon ist flüchtig — das Netz müsste bei jedem Pod-Start erzeugt werden. Nur als Rückfallebene. ## Ein Missverständnis, dem man zuvorkommen sollte In der Runner-Konfiguration steht `container.privileged: false`. Das betrifft **nur** die Container, die der Runner selbst anlegt, also den Auftragscontainer. Die k3s-Knoten legt der Auftrag über den Socket direkt beim Daemon an, und dabei greift diese Einstellung nicht — k3d bekommt sein `--privileged`. Wer bei einem Fehlschlag an dieser Stelle schraubt, ändert nichts an der Ursache und gibt stattdessen jedem Auftragscontainer mehr Rechte. ## Reihenfolge, die man nicht umgehen kann `runs-on: ubuntu-docker` bricht die GitHub-Seite, dort gibt es das Label nicht. JLab-Git liest `.gitea/workflows` **und** `.github/workflows`, beide Systeme lesen also vorerst dieselbe Datei. Der Wechsel des `runs-on` gehört damit hinter Stufe 6 aus #225 — Actions auf GitHub abschalten —, oder vorübergehend in eine eigene Datei unter `.gitea/workflows`. Für den Probelauf spielt das keine Rolle: ein Wegwerf-Workflow entsteht ohnehin neu und kann `ubuntu-docker` von Anfang an fordern. ## Fertig, wenn - `install-test` auf `ubuntu-docker` einen k3d-Cluster anlegt und `kubectl` ihn erreicht, - ein Chart-Install darin innerhalb der 30 Minuten durchläuft, - und das über mehrere Läufe stabil ist. Ein sporadisch roter Install-Test ist die schlechteste Variante — #225 sagt das zu Recht: man glaubt ihm dann nicht mehr.
Author
Member

Erledigt — der Install-Test laeuft hier

Dieser Vorgang ist die migrierte Fassung des GitHub-Issues #227, das dort bereits mit Begruendung geschlossen wurde. Der Zwilling hier war versehentlich offen geblieben; das wird hiermit nachgezogen.

Zu den drei Abschlusskriterien aus dem Rumpf:

1. install-test legt auf ubuntu-docker einen k3d-Cluster an und kubectl erreicht ihn. Erfuellt. Von den vier im Rumpf skizzierten Ansaetzen trugen zwei: k3d haengt am Netz des Auftragscontainers (ueber die Selbstbefragung per Socket), und die kubeconfig wird von 0.0.0.0 auf https://k3d-<cluster>-serverlb:6443 umgeschrieben. Die dort offen gelassene Folgefrage ist beantwortet und gemessen: das Zertifikat fuehrt den serverlb-Namen bereits, ein --tls-san war nicht noetig. K3D_FIX_DNS/K3D_FIX_MOUNTS wurden nicht gebraucht, und an container.privileged wurde nichts geaendert — die Warnung im Rumpf hat sich als berechtigt erwiesen.

2. Ein Chart-Install laeuft innerhalb der 30 Minuten durch. Erfuellt, mit grossem Abstand. Create k3d cluster 19–25 s, der gesamte Install-Test 210–225 s. Im ersten echten Chart-PR (!25) erreichte freesailarr 10/10 Container Ready hinter echtem Wireguard-Tunnel in 225 s — rund ein Achtel der Grenze, ohne dass INSTALL_TIMEOUT angefasst wurde.

3. Stabil ueber mehrere Laeufe. Erfuellt. Drei vollstaendige Laeufe mit beiden Charts nebenlaeufig: 216 / 212 / 210 s, 6 s Streuung. Gerade die Konstellation, die Sporadik erwarten liess — zwei k3s-Cluster gleichzeitig auf einem Daemon — hat gehalten.

Was der Rumpf nicht vorhersah

Der Weg dorthin lag nicht nur im Workflow. Zwei Ursachen lagen auf der Knotenebene und wurden in cluster-config behoben: eine MTU-Regression (rootful DinD legt Bruecken mit 1500 an, das Pod-Netz traegt 1450 — Downloads ueber ~1,4 kB starben stumm) und erschoepfte fs.inotify-Grenzen (128 Instanzen, Kernel-Standard; containerd kam nie hoch).

Dazu drei Annahmen im Workflow selbst: ein fest verdrahteter k3d-Clustername, der bei nebenlaeufigen Matrix-Zweigen kollidiert — der war auch auf GitHub falsch und fiel dort nur nie auf, weil jeder Zweig eine eigene VM bekommt —, sowie fehlendes kubectl und envsubst im Runner-Abbild.

Weiterhin ungemessen: /dev/fuse und User-Namespaces. Sie standen nie im Weg, geprueft sind sie damit nicht.

Die dauerhafte Fassung steht in der README unter „Where this repo lives"; der Verlauf in #14.

## Erledigt — der Install-Test laeuft hier Dieser Vorgang ist die migrierte Fassung des GitHub-Issues #227, das dort bereits mit Begruendung geschlossen wurde. Der Zwilling hier war versehentlich offen geblieben; das wird hiermit nachgezogen. Zu den drei Abschlusskriterien aus dem Rumpf: **1. `install-test` legt auf `ubuntu-docker` einen k3d-Cluster an und `kubectl` erreicht ihn.** Erfuellt. Von den vier im Rumpf skizzierten Ansaetzen trugen zwei: k3d haengt am Netz des Auftragscontainers (ueber die Selbstbefragung per Socket), und die kubeconfig wird von `0.0.0.0` auf `https://k3d-<cluster>-serverlb:6443` umgeschrieben. Die dort offen gelassene Folgefrage ist beantwortet und **gemessen**: das Zertifikat fuehrt den serverlb-Namen bereits, ein `--tls-san` war **nicht** noetig. `K3D_FIX_DNS`/`K3D_FIX_MOUNTS` wurden nicht gebraucht, und an `container.privileged` wurde nichts geaendert — die Warnung im Rumpf hat sich als berechtigt erwiesen. **2. Ein Chart-Install laeuft innerhalb der 30 Minuten durch.** Erfuellt, mit grossem Abstand. `Create k3d cluster` 19–25 s, der gesamte Install-Test 210–225 s. Im ersten echten Chart-PR (!25) erreichte `freesailarr` **10/10 Container Ready** hinter echtem Wireguard-Tunnel in 225 s — rund ein Achtel der Grenze, ohne dass `INSTALL_TIMEOUT` angefasst wurde. **3. Stabil ueber mehrere Laeufe.** Erfuellt. Drei vollstaendige Laeufe mit beiden Charts nebenlaeufig: 216 / 212 / 210 s, **6 s Streuung**. Gerade die Konstellation, die Sporadik erwarten liess — zwei k3s-Cluster gleichzeitig auf einem Daemon — hat gehalten. ## Was der Rumpf nicht vorhersah Der Weg dorthin lag nicht nur im Workflow. Zwei Ursachen lagen auf der Knotenebene und wurden in `cluster-config` behoben: eine **MTU**-Regression (rootful DinD legt Bruecken mit 1500 an, das Pod-Netz traegt 1450 — Downloads ueber ~1,4 kB starben stumm) und erschoepfte **`fs.inotify`**-Grenzen (128 Instanzen, Kernel-Standard; containerd kam nie hoch). Dazu drei Annahmen im Workflow selbst: ein fest verdrahteter k3d-Clustername, der bei nebenlaeufigen Matrix-Zweigen kollidiert — **der war auch auf GitHub falsch und fiel dort nur nie auf, weil jeder Zweig eine eigene VM bekommt** —, sowie fehlendes `kubectl` und `envsubst` im Runner-Abbild. **Weiterhin ungemessen:** `/dev/fuse` und User-Namespaces. Sie standen nie im Weg, geprueft sind sie damit nicht. Die dauerhafte Fassung steht in der README unter „Where this repo lives"; der Verlauf in #14.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: jlab-cluster/helm-charts#15