- Mehrstufige Dockerfiles je Dienst mit Stufen für Entwicklung und Betrieb, beide Images laufen als unprivilegierter Benutzer - Backend-Entrypoint wartet auf die Datenbank, migriert und seedt nur bei leerem Kategoriebaum (neues Flag --if-empty) - docker-compose.yml mit Netz-Trennung, Healthchecks und benannten Volumes; die Datenbank ist ausschließlich im internen Netz erreichbar - nginx liefert das Bundle aus und reicht /api weiter; index.html ungecacht, gehashte Assets ein Jahr - Gitea-Workflows: ci.yml für Lint, Tests und Bundle-Bau, build.yml für die Images nach linux/amd64 ohne Deploy-Schritt - docs/runner.md und docs/deployment.md, .dockerignore je Dienst Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014e7t8UpmoVNMtWivY5LiSH
8.0 KiB
Gitea-Actions-Runner einrichten
Die Workflows unter .gitea/workflows/ brauchen einen act_runner, der Docker
erreichen kann. Diese Anleitung richtet ihn als Container auf docker-srv001
ein. Sie ist vollständig – von der Tokenbeschaffung bis zur Fehlersuche.
1. Registrierungstoken holen
Es gibt zwei Möglichkeiten:
Nur für dieses Repository (empfohlen, wenn der Runner ausschließlich moneyfy bauen soll):
- In Gitea das Repository
menzeljonas/moneyfyöffnen. - Settings → Actions → Runners aufrufen.
- Create new Runner anklicken. Gitea zeigt das Registrierungstoken an – eine Zeichenkette aus etwa 40 Zeichen.
- Token kopieren. Es lässt sich nur einmal verwenden.
Instanzweit (der Runner steht dann allen Repositories zur Verfügung):
- Als Administrator anmelden.
- Site Administration → Actions → Runners aufrufen.
- Ebenfalls Create new Runner, Token kopieren.
Falls Actions in der Instanz noch abgeschaltet sind, in der app.ini des
Gitea-Servers ergänzen und Gitea neu starten:
[actions]
ENABLED = true
2. Runner als Container betreiben
Auf docker-srv001 ein eigenes Verzeichnis anlegen:
mkdir -p /opt/gitea-runner/data
cd /opt/gitea-runner
docker-compose.yml:
name: gitea-runner
services:
act-runner:
image: gitea/act_runner:0.2.11
container_name: gitea-act-runner
restart: unless-stopped
environment:
GITEA_INSTANCE_URL: https://git.menzel.center
GITEA_RUNNER_REGISTRATION_TOKEN: ${GITEA_RUNNER_REGISTRATION_TOKEN}
GITEA_RUNNER_NAME: docker-srv001
# Muss zu den Labels in config.yaml passen.
GITEA_RUNNER_LABELS: ubuntu-latest,ubuntu-22.04
CONFIG_FILE: /config.yaml
volumes:
# Der Runner startet die Job-Container über den Docker-Socket des Hosts.
- /var/run/docker.sock:/var/run/docker.sock
- ./config.yaml:/config.yaml:ro
- ./data:/data
.env daneben:
GITEA_RUNNER_REGISTRATION_TOKEN=<Token aus Schritt 1>
Der gemountete Docker-Socket gibt dem Runner faktisch Root-Rechte auf dem Host. Das ist für einen Runner, der eigene Images baut, üblich – betreibe ihn deshalb nur mit vertrauenswürdigen Repositories.
3. config.yaml
Im selben Verzeichnis anlegen:
log:
level: info
runner:
file: /data/.runner
capacity: 2
timeout: 3h
# Die linke Seite ist das Label aus `runs-on`, rechts das Image, in dem der Job läuft.
labels:
- "ubuntu-latest:docker://node:20-bookworm"
- "ubuntu-22.04:docker://node:20-bookworm"
cache:
enabled: true
dir: /data/cache
# Adresse, unter der die Job-Container den Cache-Server des Runners erreichen.
# Leer lassen heißt: automatisch ermitteln. Bei mehreren Netzen hier die IP
# des Hosts im Runner-Netz eintragen.
host: ""
port: 0
container:
# Job-Container in dasselbe Netz hängen wie der Runner.
network: bridge
privileged: false
# Nötig, damit `actions/cache` und `docker/build-push-action` Pfade einblenden dürfen.
valid_volumes:
- /data/cache
docker_host: "-"
# Bei selbstsigniertem Zertifikat auf true setzen, siehe Abschnitt 6.
force_pull: false
docker_host: "-" bedeutet: den Socket des Runners an die Job-Container
weiterreichen. Genau das braucht build.yml, um Images zu bauen.
Starten:
docker compose up -d
docker compose logs -f
Beim ersten Start registriert sich der Runner und legt /data/.runner an. Ab
dann wird das Registrierungstoken nicht mehr gebraucht.
4. Repo-Secret REGISTRY_TOKEN anlegen
Der Build-Workflow lädt Images in die Gitea-Registry und braucht dafür ein Token mit Schreibrecht auf Pakete:
- In Gitea auf Benutzereinstellungen → Applications → Generate New Token.
- Namen vergeben, etwa
moneyfy-registry. - Unter Select permissions bei package auf
Read and Writestellen. - Generate Token, Wert kopieren – er wird nur einmal angezeigt.
- Im Repository Settings → Actions → Secrets → Add Secret:
- Name:
REGISTRY_TOKEN - Wert: das kopierte Token
- Name:
Der Workflow meldet sich mit ${{ github.actor }} an, also mit dem Benutzer,
der den Lauf ausgelöst hat. Dieser Benutzer muss Schreibrecht auf die Pakete des
Namensraums menzeljonas haben.
5. Verifikation
-
Runner erscheint als „idle“: in Gitea unter Settings → Actions → Runners. Dort stehen Name (
docker-srv001), Labels und Status. -
Testworkflow läuft: einen Commit auf einen beliebigen Branch schieben. Unter Actions muss
CIstarten und beide Jobs grün abschließen. -
Image landet in der Registry: auf
mainpushen und danachhttps://git.menzel.center/menzeljonas/-/packagesöffnen. Dort müssenmoneyfy-backendundmoneyfy-frontendmit den Tagsmainundsha-<kurz>auftauchen. -
Versions-Tag prüfen:
git tag -a v0.1.0 -m "Erste Version" git push origin v0.1.0Danach müssen zusätzlich
0.1.0,0.1undlatestvorhanden sein. -
Image von Hand ziehen (prüft nebenbei die Registry-Anmeldung):
docker login git.menzel.center docker pull git.menzel.center/menzeljonas/moneyfy-backend:latest
6. Fehlersuche
docker: command not found im Job
Das Job-Image bringt keinen Docker-Client mit. Zwei Wege:
- In
config.yamlsicherstellen, dassdocker_host: "-"gesetzt ist – der Runner reicht dann seinen Socket weiter. - Für Jobs, die Docker brauchen, ein Image mit Client verwenden, etwa das Label
ubuntu-latest:docker://catthehacker/ubuntu:act-22.04. Die Actionsdocker/setup-buildx-actionunddocker/build-push-actionbringen den Rest mit.
Prüfen lässt sich das mit einem Wegwerf-Schritt:
- run: docker version
Registry-Anmeldung schlägt mit selbstsigniertem Zertifikat fehl
Meldung: x509: certificate signed by unknown authority.
Auf dem Host das CA-Zertifikat hinterlegen und den Docker-Daemon neu starten:
mkdir -p /etc/docker/certs.d/git.menzel.center
cp menzel-center-ca.crt /etc/docker/certs.d/git.menzel.center/ca.crt
systemctl restart docker
Zusätzlich muss der Runner-Container die CA kennen, weil buildx im Container läuft. Im Compose des Runners einblenden:
volumes:
- /usr/local/share/ca-certificates/menzel-center-ca.crt:/usr/local/share/ca-certificates/menzel-center-ca.crt:ro
und im Runner einmalig update-ca-certificates ausführen. Ein gültiges
Let's-Encrypt-Zertifikat auf dem Gitea-Host erspart diesen ganzen Abschnitt.
DOCKER_HOST im DinD-Betrieb
Wer statt des Host-Sockets einen Docker-in-Docker-Dienst betreibt, ergänzt diesen als zweiten Service und setzt im Runner:
environment:
DOCKER_HOST: tcp://docker-in-docker:2376
DOCKER_CERT_PATH: /certs/client
DOCKER_TLS_VERIFY: "1"
volumes:
- ./certs:/certs
In config.yaml dann docker_host: tcp://docker-in-docker:2376. DinD ist
langsamer und der Schichten-Cache geht bei jedem Neustart verloren – der
gemountete Host-Socket ist für ein Homelab die pragmatischere Wahl.
Der Cache des Runners frisst Speicherplatz
/opt/gitea-runner/data/cache und die Build-Schichten wachsen mit jedem Lauf.
Belegung ansehen:
du -sh /opt/gitea-runner/data/cache
docker system df
Aufräumen:
# Nicht mehr referenzierte Schichten und Build-Cache älter als eine Woche
docker buildx prune --filter "until=168h" -f
docker image prune -f
# Cache-Verzeichnis des Runners leeren
docker compose -f /opt/gitea-runner/docker-compose.yml down
rm -rf /opt/gitea-runner/data/cache/*
docker compose -f /opt/gitea-runner/docker-compose.yml up -d
Regelmäßig per Cron:
0 4 * * 0 docker buildx prune --filter "until=168h" -f >/dev/null 2>&1
Job bleibt in „waiting“ hängen
Die Labels stimmen nicht überein. runs-on: ubuntu-latest im Workflow braucht
ein Label ubuntu-latest am Runner. Nach einer Änderung in config.yaml muss
der Runner neu registriert werden:
docker compose down
rm data/.runner
# neues Registrierungstoken in .env eintragen
docker compose up -d