meldestelle/docs/07_Infrastructure/runbooks/zora-setup-runbook.md
StefanMoCoAt f022de7358
All checks were successful
Build and Publish Docker Images / build-and-push (., backend/infrastructure/gateway/Dockerfile, api-gateway, api-gateway) (push) Successful in 8m7s
Build and Publish Docker Images / build-and-push (., backend/services/ping/Dockerfile, ping-service, ping-service) (push) Successful in 7m16s
Build and Publish Docker Images / build-and-push (., config/docker/caddy/web-app/Dockerfile, web-app, web-app) (push) Successful in 1m48s
Build and Publish Docker Images / build-and-push (., config/docker/keycloak/Dockerfile, keycloak, keycloak) (push) Successful in 1m32s
Update Zora configuration: add detailed runbook, reassign ai-stack to CT 120, and improve infrastructure documentation
2026-03-08 00:37:34 +01:00

12 KiB

type status owner last_update
Runbook ACTIVE DevOps Engineer 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

# 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

ssh gitearun@10.0.0.23

2.2 Docker Daemon konfigurieren (bereits erledigt)

# Verifizieren — muss bereits korrekt sein:
docker info | grep -A5 "Insecure Registries"

Erwartetes Ergebnis:

Insecure Registries:
  10.0.0.22:3000

Falls nicht gesetzt:

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)

# 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

# Interaktive Registrierung
act_runner register \
  --no-interactive \
  --instance http://10.0.0.22:3000 \
  --token <REGISTRATION_TOKEN_AUS_SCHRITT_1> \
  --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:

mkdir -p ~/.gitea-runner
mv .runner ~/.gitea-runner/

Schritt 4: Systemd-Service anlegen

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:

# 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

ssh user@10.0.0.50

3.2 Voraussetzungen prüfen

# Docker installiert?
docker --version       # erwartet: Docker 27+
docker compose version # erwartet: v2.x

# Git installiert?
git --version          # erwartet: 2.x

Falls Docker nicht installiert:

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Neu einloggen!

3.3 Git-Deployment einrichten

# 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!

nano .env

Pflichtfelder für Production (Konfig-Matrix beachten!):

# --- 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=<SICHERES_PASSWORT_MIN_20_ZEICHEN>
KC_DB_PASSWORD=<SICHERES_PASSWORT_MIN_20_ZEICHEN>
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=<SICHERES_PASSWORT_MIN_20_ZEICHEN>
POSTGRES_DB=meldestelle
POSTGRES_SHARED_BUFFERS=16GB
POSTGRES_EFFECTIVE_CACHE_SIZE=48GB

# --- VALKEY ---
VALKEY_MAXMEMORY=4gb
VALKEY_PASSWORD=<SICHERES_PASSWORT>

# --- GRAFANA ---
GF_ADMIN_PASSWORD=<SICHERES_PASSWORT>

# --- 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:

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

cd ~/meldestelle

# Mit Gitea-Registry authentifizieren
docker login 10.0.0.22:3000 -u <REGISTRY_USER> -p <REGISTRY_TOKEN>

# 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

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

# 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

# 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

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)

# 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

# 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).