From 126d556bd7142abfffefb89e41b4edba44417c4d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 21:44:54 +0000 Subject: [PATCH] docs(runner): Livegang-Anleitung (Netz, Compose, Firewall, deploy.sh-Vorschlag) Co-Authored-By: Claude Sonnet 5.5 --- runner/DEPLOY.md | 147 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 runner/DEPLOY.md diff --git a/runner/DEPLOY.md b/runner/DEPLOY.md new file mode 100644 index 0000000..a5708cd --- /dev/null +++ b/runner/DEPLOY.md @@ -0,0 +1,147 @@ +# Preview-Runner: Livegang-Anleitung (R18) + +Stand: Vorschlag aus der Abschluss-Prüfung. Nichts davon ist im Repo umgesetzt, `deploy.sh` +(`/home/webklar/apps/kundenbereich.webklar.com/deploy.sh`) liegt außerhalb des Repos und wird hier +nur als Vorschlag beschrieben. Reihenfolge laut Spezifikation: L1 (Netz, Geheimnisse, Compose, +Schema-Skript) vor L2 (Portal mit `PREVIEW_RUNNER=off`), dann L3 (`PREVIEW_RUNNER=on`). + +## 1. Netzwerk `preview-sandbox` (VOR dem ersten Start anlegen) + +```sh +docker network create --subnet 172.30.0.0/24 preview-sandbox +``` + +Der Runner verweigert den Start, wenn er keine IPv4-Adresse außerhalb dieses Subnetzes hat. +`PREVIEW_SANDBOX_SUBNET` muss exakt dem Subnetz entsprechen. Das Subnetz nicht in Compose anders +definieren (Netz als `external: true` einbinden). + +Verzeichnisse auf dem Host anlegen (root-eigen genügt, der Runner setzt Besitzer 1000 selbst): + +```sh +mkdir -p /srv/webklar-preview-runner/work /srv/webklar-preview-runner/cache /var/www/previews +``` + +## 2. Compose-Dienst (in `/home/webklar/apps/docker-compose.yml`) + +Der Image-Build-Kontext ist `kundenbereich.webklar.com/build/runner` (Dockerfile im Repo unter +`runner/`). Hostpfade stehen im Container unter dem IDENTISCHEN Pfad (R6). + +```yaml + preview-runner: + build: + context: ./kundenbereich.webklar.com/build/runner + image: webklar-preview-runner:latest + container_name: webklar-preview-runner + restart: unless-stopped + init: true + mem_limit: 256m + # keine ports: - erreichbar nur im Docker-Netz webklar-network (Traefik) und vom Portal + environment: + - PREVIEW_RUNNER_TOKEN=${PREVIEW_RUNNER_TOKEN} + - GITEA_URL=https://git.webklar.com + - GITEA_TOKEN=${GITEA_SYSTEM_TOKEN} # lesender Token, der die WEBklar-Repos sehen darf + - PREVIEW_BASE_HOST=project.webklar.com + - PREVIEWS_ROOT=/var/www/previews + - RUNNER_WORK_ROOT=/srv/webklar-preview-runner/work + - RUNNER_CACHE_ROOT=/srv/webklar-preview-runner/cache + - PREVIEW_SANDBOX_SUBNET=172.30.0.0/24 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /var/www/previews:/var/www/previews + - /srv/webklar-preview-runner:/srv/webklar-preview-runner + networks: + - webklar-network + - preview-sandbox + labels: + - traefik.enable=true + - traefik.docker.network=webklar-network + - traefik.http.services.preview-runner.loadbalancer.server.port=8081 + # nur Dienst, kein Router: die Router erzeugt previewDeploy.syncPreviewTls (preview-runner@docker) + +networks: + preview-sandbox: + external: true +``` + +(Den `networks:`-Block in die bestehende Datei einfügen, nicht doppelt anlegen.) Keine Appwrite-, +SMTP- oder Stripe-Variablen und kein `PREVIEW_ENV_KEY` im Runner. + +Erster Start: `docker compose build preview-runner && docker compose up -d preview-runner`. + +## 3. Portal-Umgebung (Dienst `kundenbereich-server`, Variablen einzeln auflisten) + +```yaml + - PREVIEW_RUNNER=${PREVIEW_RUNNER:-off} # L2: off, L3: on + - PREVIEW_RUNNER_URL=http://webklar-preview-runner:8080 + - PREVIEW_RUNNER_TOKEN=${PREVIEW_RUNNER_TOKEN} + - PREVIEW_ENV_KEY=${PREVIEW_ENV_KEY} # 32 Byte, Base64 (openssl rand -base64 32) +``` + +Das Verzeichnis `upload-data/preview-logs` muss persistent sein (liegt in `./build/`). + +## 4. Firewall für die Sandbox-Bridge (Livegang-Schritt, kein Code) + +Zuerst den Bridge-Namen ermitteln: `BR=br-$(docker network inspect preview-sandbox -f '{{.Id}}' | cut -c1-12)`. + +```sh +# Sandbox -> Host (INPUT): alles verwerfen, bereits bestehende Antworten erlauben +iptables -I INPUT -i $BR -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT +iptables -A INPUT -i $BR -j DROP + +# Sandbox -> andere Ziele (DOCKER-USER, wird vor Docker-Regeln ausgewertet) +iptables -I DOCKER-USER -s 172.30.0.0/24 -d 169.254.169.254 -j DROP # Hetzner-Metadaten +iptables -I DOCKER-USER -s 172.30.0.0/24 -d 10.0.0.0/8 -j DROP +iptables -I DOCKER-USER -s 172.30.0.0/24 -d 192.168.0.0/16 -j DROP +# 172.16.0.0/12 (andere Docker-Netze) sperren, ausser dem eigenen /24; Runner-Zugriff wird nicht gebraucht +iptables -I DOCKER-USER -s 172.30.0.0/24 -d 172.30.0.0/24 -j ACCEPT # Dev-zu-Dev im eigenen Netz (R13, bekannt) +iptables -I DOCKER-USER -s 172.30.0.0/24 ! -d 172.30.0.0/24 -d 172.16.0.0/12 -j DROP +``` + +Reihenfolge beachten: `-I` setzt vorne ein, die ACCEPT-Regel für das eigene /24 muss vor dem +172.16/12-DROP stehen (also zuletzt mit `-I` eingefügt, wie oben die Zeilen in umgekehrter +Wirkungsreihenfolge; nach dem Einrichten mit `iptables -S DOCKER-USER` prüfen). DNS läuft über den +Docker-Resolver (127.0.0.11) und bleibt erlaubt, Ausgang ins Internet (npm, Registries) ebenfalls. +Regeln persistent machen (z. B. `iptables-persistent`), sonst gehen sie beim Neustart verloren. + +## 5. deploy.sh-Erweiterung (Vorschlag, bewusst NICHT eingespielt) + +Voraussetzung: der Build-Container (`kundenbereich-build`) bekommt zusätzlich +`/home/webklar/apps/docker-compose.yml:/compose/docker-compose.yml:ro` eingehängt und hat +`docker-cli` samt Compose-Plugin. Der Runner wird nur neu gebaut und neu erstellt, wenn sich +`runner/` geändert hat UND `GET /status` eine leere Warteschlange meldet (sonst würden laufende +Builds/Dev-Server beendet); andernfalls überspringen und protokollieren, der nächste Deploy versucht +es erneut. Der Runner wird VOR dem Portal neu gestartet, damit dessen Dev-Registrierung gleich +klappt (der Abgleich im Portal holt Verpasstes ohnehin alle 2 Minuten nach). + +Einfügen direkt vor dem Neustart des Portals; vor dem `git reset --hard` den alten Stand merken +(`OLD_HEAD=$(git rev-parse HEAD 2>/dev/null || true)`): + +```sh +# --- Preview-Runner (nur wenn runner/ geaendert) --- +if [ -S /var/run/docker.sock ] && command -v docker >/dev/null 2>&1 && [ -f /compose/docker-compose.yml ]; then + NEW_HEAD=$(git rev-parse HEAD) + if [ -z "${OLD_HEAD}" ] || ! git diff --quiet "${OLD_HEAD}" "${NEW_HEAD}" -- runner 2>/dev/null; then + QUEUE=$(docker exec webklar-preview-runner sh -c \ + 'wget -qO- --header "Authorization: Bearer $PREVIEW_RUNNER_TOKEN" http://127.0.0.1:8080/status' 2>/dev/null \ + | sed -n 's/.*"queue":\([0-9]*\).*/\1/p') + if [ -z "${QUEUE}" ] || [ "${QUEUE}" = "0" ]; then + echo "Preview-Runner: Image neu bauen und Container neu erstellen..." + docker build -t webklar-preview-runner:latest /app/runner \ + && docker compose -f /compose/docker-compose.yml --project-directory /home/webklar/apps \ + up -d --no-build --no-deps preview-runner \ + || echo "Preview-Runner: Aktualisierung fehlgeschlagen (Portal-Deploy laeuft weiter)" + else + echo "Preview-Runner: Warteschlange nicht leer (${QUEUE}) - Aktualisierung uebersprungen, naechster Deploy versucht es erneut" + fi + fi +fi +``` + +Hinweise: Ist der Runner noch nicht angelegt (`docker exec` schlägt fehl, `QUEUE` leer), wird gebaut +und erstellt, das ist beim ersten Mal gewollt. `wget` ist im Alpine-Image enthalten. Die +Status-Abfrage bitte vor dem Einspielen einmal von Hand gegenprüfen. + +## 6. Rückfall + +`PREVIEW_RUNNER=off` und Portal neu starten: Der bisherige Weg läuft unverändert. Dokumente behalten +`previewMode`/`previewState`, die Dev-Router verschwinden beim nächsten TLS-Sync.