Aim the deploy at the runner that exists
Some checks failed
CI / Lint, Typen, Tests, Build (push) Failing after 5m28s
CI / Integrationstests (echtes Postgres) (push) Failing after 5m11s
Deploy / Migrationen und Container (push) Failing after 4s

The workflow asked for a `self-hosted` label. No runner on the instance
offers one, so the run would have sat in "Waiting" forever — no error, no
message, nothing to notice. The existing global runner elycon-runner-01
offers `docker` and `ubuntu-latest`, so both workflows now ask for
`ubuntu-latest`, the same label ci.yml already used.

That correction exposed a second thing the first version glossed over.
act_runner starts a container per job; mounting the Docker socket into
the *runner* does not put it in the *job*. Whether this job can reach the
host's daemon depends on the runner's config.yaml, which is not visible
from here — and the runner is global, so changing it affects every
repository on the instance, not just this one.

Rather than guess, the workflow now measures it in its first step and
fails with the fix if it cannot: which config lines to add for the
socket, or that SSH is the other way. Without that, the run would have
died three steps later on a message nobody could act on.

Both branches of the check were exercised: docker absent prints the
first message, docker present with no reachable daemon the second.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 16:15:49 +02:00
parent fc0989debb
commit d574d3c9d6
2 changed files with 72 additions and 29 deletions

View File

@@ -24,8 +24,45 @@ concurrency:
jobs: jobs:
deploy: deploy:
name: Migrationen und Container name: Migrationen und Container
runs-on: self-hosted # `ubuntu-latest`, weil der vorhandene Runner (elycon-runner-01) genau
# diese Bezeichnung anbietet — neben `docker`. Ein Label, das kein Runner
# führt, lässt den Lauf wortlos in der Warteschlange stehen: kein Fehler,
# keine Meldung, nur „Waiting".
runs-on: ubuntu-latest
steps: steps:
# ── Vorprüfung ──────────────────────────────────────────────────
#
# Dieser Job läuft in einem Container, den der Runner startet — nicht
# auf dem Server selbst. Ob er den Docker-Dienst des **Hosts** erreicht,
# hängt davon ab, wie der Runner konfiguriert ist; von aussen ist das
# nicht zu sehen. Statt es zu raten, wird es hier gemessen und im
# Fehlerfall gesagt, was fehlt — sonst scheitert der Lauf erst drei
# Schritte später an einer Meldung, die niemandem weiterhilft.
- name: Erreicht dieser Job den Docker-Dienst des Servers?
run: |
set -u
if ! command -v docker >/dev/null 2>&1; then
echo 'Im Job-Container gibt es kein "docker".'
echo
echo "Zwei Wege:"
echo " a) Im Runner (config.yaml) ein Abbild verwenden, das die Docker-CLI"
echo " mitbringt, und den Socket durchreichen — siehe DEPLOYMENT.md §5."
echo " b) Statt lokal per SSH auf den Server ausrollen."
exit 1
fi
if ! docker info >/dev/null 2>&1; then
echo "Docker-CLI vorhanden, aber kein Dienst erreichbar."
echo
echo "Dem Job-Container fehlt der Socket des Hosts. In der config.yaml des"
echo "Runners:"
echo " container:"
echo " options: -v /var/run/docker.sock:/var/run/docker.sock"
echo " valid_volumes: [/var/run/docker.sock]"
echo "Danach den Runner neu starten. Alternative: Deploy per SSH."
exit 1
fi
docker compose version
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4

View File

@@ -230,37 +230,44 @@ Zuständig ist [`.github/workflows/deploy.yml`](.github/workflows/deploy.yml).
> `ci.yml` stillschweigend abschalten. Solange beide Dateien unter `.github/` > `ci.yml` stillschweigend abschalten. Solange beide Dateien unter `.github/`
> liegen, sieht Gitea beide. > liegen, sieht Gitea beide.
### a) Runner registrieren ### a) Der Runner
Der Runner läuft als Container **auf demselben Server** wie die Anwendung und Verwendet wird der bestehende, globale Runner **`elycon-runner-01`** (act_runner
bekommt den Docker-Socket des Hosts eingehängt — damit baut und startet er die v0.2.11, Labels `docker` und `ubuntu-latest`). Ein eigener ist nicht nötig.
Container des Hosts, statt welche in sich selbst zu erzeugen.
Registrierungs-Token holen: Gitea → Repository → *Settings* → *Actions* → Beide Workflows laufen deshalb auf `ubuntu-latest`. **Ein Label, das kein
*Runners* → *Create new runner*. Runner führt, lässt den Lauf wortlos in der Warteschlange stehen** — kein
Fehler, keine Meldung, nur „Waiting". Das ist der häufigste Grund, warum
scheinbar nichts passiert.
```bash #### Der Job läuft im Container, nicht auf dem Server
mkdir -p /opt/gitea-runner && cd /opt/gitea-runner
docker run -d --name gitea-runner --restart unless-stopped \ Das ist die Stelle, an der es klemmen kann. act_runner startet je Job einen
-e GITEA_INSTANCE_URL=https://git.elycon.solutions \ eigenen Container; der Docker-Socket des **Hosts** ist darin nur, wenn der
-e GITEA_RUNNER_REGISTRATION_TOKEN=<token> \ Runner so konfiguriert ist:
-e GITEA_RUNNER_NAME=alpenwerk-deploy \
-e GITEA_RUNNER_LABELS=self-hosted,ubuntu-latest \ ```yaml
-v /var/run/docker.sock:/var/run/docker.sock \ # config.yaml des Runners
-v /opt/gitea-runner:/data \ container:
gitea/act_runner:latest options: -v /var/run/docker.sock:/var/run/docker.sock
valid_volumes:
- /var/run/docker.sock
``` ```
Zu den Labels: `self-hosted` verlangt der Deploy-Workflow, `ubuntu-latest` die Zusätzlich muss das Job-Abbild die Docker-CLI mitbringen (die üblichen
bestehende `ci.yml`. Fehlt eines, bleibt der jeweilige Lauf ohne Fehlermeldung `runner-images`/`catthehacker`-Abbilder tun das).
in der Warteschlange — **das ist der häufigste Grund, warum „nichts passiert"**.
> Der eingehängte Docker-Socket gibt dem Runner faktisch Wurzelrechte auf dem Der erste Schritt des Deploy-Workflows prüft genau das und bricht mit einer
> Host. Das ist bei einem Deploy-Runner der Zweck der Übung, aber es heisst Anleitung ab, wenn etwas fehlt — statt drei Schritte später an einer Meldung zu
> auch: wer in dieses Repository schreiben darf, darf auf diesem Server alles. scheitern, mit der niemand etwas anfangen kann.
> Bei zwei Personen vertretbar; bei einem grösseren Kreis gehört der Runner auf
> eine eigene Maschine, die sich per SSH verbindet. > Der durchgereichte Socket gibt dem Job faktisch Wurzelrechte auf dem Host.
> Bei einem Deploy-Runner ist das der Zweck der Übung, aber es heisst auch: wer
> in dieses Repository schreiben darf, darf auf diesem Server alles. Bei einem
> **globalen** Runner gilt das für jedes Repository der Instanz, das denselben
> Socket benutzt. Ist das zu weit gefasst, ist der Weg über SSH der richtige:
> ein Schlüssel als Repository-Secret, der nur auf den einen Server und nur auf
> das Deploy-Verzeichnis zeigt.
### b) Secrets hinterlegen ### b) Secrets hinterlegen
@@ -370,10 +377,9 @@ Single-Instance-Compose-Konfiguration ist das nicht nötig.
- **Push gemacht, aber kein Lauf startet:** meist die Labels. Der Lauf steht - **Push gemacht, aber kein Lauf startet:** meist die Labels. Der Lauf steht
dann in Gitea unter *Actions* als „Waiting" — ohne Fehlermeldung, weil kein dann in Gitea unter *Actions* als „Waiting" — ohne Fehlermeldung, weil kein
Runner das verlangte Label anbietet. `deploy.yml` verlangt `self-hosted`, Runner das verlangte Label anbietet. Beide Workflows verlangen
`ci.yml` verlangt `ubuntu-latest`. Welche Labels registriert sind, zeigt `ubuntu-latest`; `elycon-runner-01` führt es. Welche Labels registriert sind,
Gitea → *Settings* → *Actions* → *Runners*; ändern lassen sie sich durch zeigt Gitea → *Site Administration* → *Actions* → *Runners*.
erneutes Registrieren.
- **Deploy läuft, aber es entsteht ein zweiter Stapel:** `COMPOSE_PROJECT_NAME` - **Deploy läuft, aber es entsteht ein zweiter Stapel:** `COMPOSE_PROJECT_NAME`
passt nicht zum bestehenden. Auf dem Server `docker compose ls` — der dort passt nicht zum bestehenden. Auf dem Server `docker compose ls` — der dort
gelistete Name gehört als Variable ins Repository. Bis dahin läuft die alte gelistete Name gehört als Variable ins Repository. Bis dahin läuft die alte