--- type: Runbook status: ACTIVE owner: DevOps Engineer last_update: 2026-03-06 --- # Runbook: Zora — Vollständige Systemkonfiguration Dieses Runbook beschreibt die vollständige Konfiguration aller drei Zora-Systeme: **Gitea (CT 101)**, **Gitea-Runner (VM 102)** und **Meldestelle-Host (VM 110)**. > **Referenz:** `docs/01_Architecture/Minisforum-MS-R1/SSoT_Konfigurations-Masterplan_Zora.md` --- ## Übersicht & IP-Adressen | System | Typ | IP | Port | Zweck | |:------------------|:-----|:-------------|:------|:-----------------------------| | Gitea | LXC | `10.0.0.22` | `3000`| Git-Server + Registry | | Gitea-Runner | VM | `10.0.0.23` | — | CI/CD ARM64-Builder | | Meldestelle-Host | VM | `10.0.0.50` | — | Docker App-Stack | | Proxmox Node | — | `10.0.0.20` | `8006`| Hypervisor Web-UI | --- ## 1. Gitea (CT 101 — `10.0.0.22`) ### 1.1 Erreichbarkeit prüfen ```bash # Intern (im Heimnetz) curl -s http://10.0.0.22:3000 | grep -i gitea # Extern (via Pangolin) curl -s https://git.mo-code.at | grep -i gitea ``` **Erwartetes Ergebnis:** HTTP 200, Gitea-Login-Seite. ### 1.2 Container-Registry verifizieren Im Gitea Web-UI (`http://10.0.0.22:3000`): 1. **Einstellungen → Packages** → sicherstellen, dass Packages aktiviert sind. 2. **Organisation `mocode-software` → Packages** → folgende Images müssen vorhanden sein: - `mocode-software/meldestelle/api-gateway:latest` - `mocode-software/meldestelle/ping-service:latest` - `mocode-software/meldestelle/web-app:latest` - `mocode-software/meldestelle/keycloak:latest` Falls Images fehlen: Pipeline manuell triggern (Push auf `main` oder Re-Run im Gitea Actions UI). ### 1.3 Org-Secrets prüfen Im Gitea Web-UI: **Organisation `mocode-software` → Einstellungen → Secrets & Variables → Actions** Folgende Secrets müssen gesetzt sein: | Secret-Name | Wert | |:-----------------|:----------------------------| | `REGISTRY_USER` | Gitea-Username des CI-Users | | `REGISTRY_TOKEN` | Gitea Access-Token (Scope: `package:write`) | Falls nicht gesetzt: 1. Gitea → Profil → **Settings → Applications → Generate Token** 2. Scope: `package:write` aktivieren 3. Token als `REGISTRY_TOKEN` in Org-Secrets speichern --- ## 2. Gitea-Runner (VM 102 — `10.0.0.23`) — Vollständige Installation > **Status (06.03.2026):** > - ✅ `daemon.json` korrekt gesetzt (`insecure-registries: 10.0.0.22:3000`) > - ❌ `gitea-runner.service` **existiert nicht** — Binary und Service müssen installiert werden! ### 2.1 SSH-Verbindung ```bash ssh gitearun@10.0.0.23 ``` ### 2.2 Docker Daemon konfigurieren ✅ (bereits erledigt) ```bash # Verifizieren — muss bereits korrekt sein: docker info | grep -A5 "Insecure Registries" ``` **Erwartetes Ergebnis:** ``` Insecure Registries: 10.0.0.22:3000 ``` Falls nicht gesetzt: ```bash sudo tee /etc/docker/daemon.json > /dev/null <<'EOF' { "insecure-registries": ["10.0.0.22:3000"] } EOF sudo systemctl restart docker ``` ### 2.3 Gitea Actions Runner installieren #### Schritt 1: Registrierungs-Token holen Im Gitea Web-UI (`http://10.0.0.22:3000`): **Site Administration → Actions → Runners → Create new Runner** > Alternativ auf Organisations-Ebene: > **Organisation `mocode-software` → Einstellungen → Actions → Runners → Create Runner** Den angezeigten **Registration Token** kopieren (einmalig gültig). #### Schritt 2: Binary herunterladen (ARM64) ```bash # Aktuelle Version prüfen: https://gitea.com/gitea/act_runner/releases ACT_RUNNER_VERSION="0.2.11" # ARM64-Binary laden (passend zu Zora/MS-R1) wget -O act_runner "https://gitea.com/gitea/act_runner/releases/download/v${ACT_RUNNER_VERSION}/act_runner-${ACT_RUNNER_VERSION}-linux-arm64" # Ausführbar machen und in PATH legen chmod +x act_runner sudo mv act_runner /usr/local/bin/act_runner # Verifizieren act_runner --version ``` #### Schritt 3: Runner registrieren ```bash # Interaktive Registrierung act_runner register \ --no-interactive \ --instance http://10.0.0.22:3000 \ --token \ --name "zora-arm64-runner" \ --labels "ubuntu-latest:docker://node:20-bullseye" # Ergebnis: .runner Datei im aktuellen Verzeichnis ls -la .runner ``` > **Wichtig:** Den Runner-Config-Ordner festlegen: ```bash mkdir -p ~/.gitea-runner mv .runner ~/.gitea-runner/ ``` #### Schritt 4: Systemd-Service anlegen ```bash sudo tee /etc/systemd/system/gitea-runner.service > /dev/null <<'EOF' [Unit] Description=Gitea Actions Runner After=docker.service Requires=docker.service [Service] Type=simple User=gitearun WorkingDirectory=/home/gitearun/.gitea-runner ExecStart=/usr/local/bin/act_runner daemon Restart=always RestartSec=5 Environment="HOME=/home/gitearun" [Install] WantedBy=multi-user.target EOF # Service aktivieren und starten sudo systemctl daemon-reload sudo systemctl enable --now gitea-runner # Status prüfen sudo systemctl status gitea-runner ``` **Erwartetes Ergebnis:** ``` ● gitea-runner.service - Gitea Actions Runner Active: active (running) ``` #### Schritt 5: Runner in Gitea bestätigen Gitea Web-UI → **Site Administration → Actions → Runners** Der Runner `zora-arm64-runner` muss als **Online** angezeigt werden. ### 2.4 Workflow-Workaround ablösen (optional, nach Verifikation) Nach erfolgreichem Permanent-Fix kann der manuelle `config.json`-Schritt im Workflow durch den Standard-`docker/login-action` ersetzt werden. > **Hinweis:** Erst nach einem erfolgreichen Test-Run mit der neuen `daemon.json` durchführen. Im Workflow (`.gitea/workflows/docker-publish.yaml`), den Schritt ersetzen: ```yaml # ALT (Workaround): - 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' "${{ env.REGISTRY_INTERNAL }}" "${AUTH}" > ~/.docker/config.json # NEU (sauber, nach daemon.json-Fix): - name: Login to Gitea Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY_INTERNAL }} username: ${{ secrets.REGISTRY_USER }} password: ${{ secrets.REGISTRY_TOKEN }} ``` --- ## 3. Meldestelle-Host (VM 110 — `10.0.0.50`) ### 3.1 SSH-Verbindung ```bash ssh user@10.0.0.50 ``` ### 3.2 Voraussetzungen prüfen ```bash # Docker installiert? docker --version # erwartet: Docker 27+ docker compose version # erwartet: v2.x # Git installiert? git --version # erwartet: 2.x ``` Falls Docker nicht installiert: ```bash curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER # Neu einloggen! ``` ### 3.3 Git-Deployment einrichten ```bash # Projektverzeichnis anlegen mkdir -p ~/meldestelle && cd ~/meldestelle # Git initialisieren und mit Gitea verknüpfen git init git remote add origin http://10.0.0.22:3000/mocode-software/meldestelle.git # Neueste Konfiguration holen git fetch origin git reset --hard origin/main # Deployment-Skript ausführbar machen chmod +x config/scripts/deploy.sh chmod +x config/scripts/backup.sh ``` ### 3.4 Environment-Variablen konfigurieren > **Status (06.03.2026):** `.env` ist befüllt, aber enthält noch **Placeholder-Passwörter** > (`pg-password`, `kc-password`, `gf-password`). Diese **müssen** vor dem ersten Start > durch starke Passwörter (min. 20 Zeichen) ersetzt werden! ```bash nano .env ``` **Pflichtfelder für Production** (Konfig-Matrix beachten!): ```bash # --- KEYCLOAK --- KC_COMMAND=start --optimized --import-realm KC_HOSTNAME=10.0.0.50 KC_HOSTNAME_STRICT=false KC_HOSTNAME_STRICT_HTTPS=false KC_ADMIN_PASSWORD= KC_DB_PASSWORD= KC_HEAP_MAX=4096m # --- JWT --- SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=http://10.0.0.50:8180/realms/meldestelle SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=http://keycloak:8080/realms/meldestelle/protocol/openid-connect/certs # --- POSTGRES --- POSTGRES_USER=meldestelle POSTGRES_PASSWORD= POSTGRES_DB=meldestelle POSTGRES_SHARED_BUFFERS=16GB POSTGRES_EFFECTIVE_CACHE_SIZE=48GB # --- VALKEY --- VALKEY_MAXMEMORY=4gb VALKEY_PASSWORD= # --- GRAFANA --- GF_ADMIN_PASSWORD= # --- BACKUP --- BACKUP_DIR=/home/user/backups/meldestelle BACKUP_RETENTION_DAYS=7 ``` > **Wichtig:** `KC_HOSTNAME` darf auf dem Server **niemals** `localhost` sein! > Ist Pangolin konfiguriert, kann `KC_HOSTNAME=auth.mo-code.at` gesetzt werden. ### 3.5 Interne Registry konfigurieren Docker muss die Gitea-Registry intern (HTTP) kennen: ```bash sudo tee /etc/docker/daemon.json > /dev/null <<'EOF' { "insecure-registries": ["10.0.0.22:3000"] } EOF sudo systemctl restart docker ``` ### 3.6 Images aus Registry pullen ```bash cd ~/meldestelle # Mit Gitea-Registry authentifizieren docker login 10.0.0.22:3000 -u -p # Images pullen docker pull 10.0.0.22:3000/mocode-software/meldestelle/keycloak:latest docker pull 10.0.0.22:3000/mocode-software/meldestelle/api-gateway:latest docker pull 10.0.0.22:3000/mocode-software/meldestelle/ping-service:latest docker pull 10.0.0.22:3000/mocode-software/meldestelle/web-app:latest ``` ### 3.7 Stack starten ```bash cd ~/meldestelle # Schritt 1: Infrastruktur (DB, Keycloak, Valkey, Consul) docker compose -f dc-infra.yaml up -d echo "Warte 30s auf Keycloak-Start..." sleep 30 # Schritt 2: Backend (API-Gateway, Ping-Service) docker compose -f dc-backend.yaml up -d # Schritt 3: Ops (Prometheus, Grafana, Zipkin) docker compose -f dc-ops.yaml up -d ``` ### 3.8 Smoke-Tests ```bash # 1. Keycloak Health curl -s http://10.0.0.50:9000/health/ready | python3 -m json.tool # Erwartet: {"status": "UP"} # 2. Keycloak Admin-Dashboard erreichbar curl -s -o /dev/null -w "%{http_code}" http://10.0.0.50:8180/admin/ # Erwartet: 200 # 3. API-Gateway Ping curl -s http://10.0.0.50:8081/ping # Erwartet: {"status":"UP"} o.ä. # 4. Consul Service Discovery curl -s http://10.0.0.50:8500/v1/agent/services | python3 -m json.tool # Erwartet: api-gateway + ping-service registriert # 5. Grafana erreichbar curl -s -o /dev/null -w "%{http_code}" http://10.0.0.50:3000 # Erwartet: 200 ``` ### 3.9 Backup-Cron einrichten ```bash # Backup-Verzeichnis anlegen mkdir -p ~/backups/meldestelle # Cron-Job: täglich um 02:00 Uhr (crontab -l 2>/dev/null; echo "0 2 * * * /home/user/meldestelle/config/scripts/backup.sh >> /home/user/backups/meldestelle/backup.log 2>&1") | crontab - # Verifizieren crontab -l ``` --- ## 4. Abschluss-Checkliste > Stand 06.03.2026 — ✅ = erledigt, ❌ = offen ``` [✅] Gitea erreichbar (intern + extern via Pangolin) [✅] Registry-Images (4x) vorhanden und aktuell [✅] Org-Secrets REGISTRY_USER + REGISTRY_TOKEN gesetzt [✅] VM 102: /etc/docker/daemon.json mit insecure-registries gesetzt [❌] VM 102: act_runner Binary installiert (/usr/local/bin/act_runner) [❌] VM 102: gitea-runner.service angelegt, enabled und active [❌] VM 102: Runner in Gitea als "Online" sichtbar [⚠️] VM 110: .env befüllt — Placeholder-Passwörter ersetzen! [✅] VM 110: /etc/docker/daemon.json mit insecure-registries gesetzt (prüfen!) [❌] VM 110: Stack gestartet (infra + backend + ops) [❌] Keycloak Health: UP [❌] Keycloak Admin-Dashboard: erreichbar [❌] API-Gateway Ping: antwortet [❌] Grafana: erreichbar [❌] Backup-Cron: eingerichtet ``` --- ## 5. Troubleshooting ### Keycloak startet nicht ```bash docker logs meldestelle-keycloak --tail 50 ``` - `start-dev` statt `start --optimized`? → `.env`: `KC_COMMAND=start --optimized --import-realm` - `KC_HOSTNAME=localhost` auf Server? → `KC_HOSTNAME=10.0.0.50` setzen ### Docker-Pull schlägt fehl (x509/TLS) ```bash # daemon.json prüfen cat /etc/docker/daemon.json docker info | grep -A5 "Insecure" # Falls leer: daemon.json nochmals setzen + docker restart ``` ### API-Gateway findet Keycloak nicht ```bash # JWT_ISSUER_URI prüfen docker exec meldestelle-api-gateway env | grep ISSUER # Muss sein: http://10.0.0.50:8180/realms/meldestelle (Public) # JWK_SET_URI muss sein: http://keycloak:8080/... (intern) ``` ### Pipeline schlägt fehl (RAM-OOM) - `max-parallel: 1` im Workflow prüfen — muss immer gesetzt sein auf VM 102 (16 GB RAM).