docs: massive restructuring of documentation, development guides and agent playbooks
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# 🚀 Deployment Guide - Online-Nennung (Neumarkt 2026)
|
||||
|
||||
Dieser Guide beschreibt den Prozess zum Deployment des "Biest" Online-Nennung Stacks auf den Produktions-Server.
|
||||
|
||||
## 1. Voraussetzungen
|
||||
|
||||
- Docker & Docker Compose installiert.
|
||||
- Zugriff auf den OEPS SMTP Server oder eine Alternative.
|
||||
- Domain (z.B. `nennung.mo-code.at`) zeigt auf den Server.
|
||||
|
||||
## 2. Infrastruktur (Backend)
|
||||
|
||||
Der Stack wird über `dc-backend.yaml` gestartet.
|
||||
|
||||
### Umgebungsvariablen (`.env` Datei am Server)
|
||||
|
||||
Folgende Variablen müssen gesetzt sein:
|
||||
|
||||
```env
|
||||
# Datenbank
|
||||
POSTGRES_USER=pg-user
|
||||
POSTGRES_PASSWORD=dein-geheimes-passwort
|
||||
|
||||
# SMTP (für Bestätigungs-Mails)
|
||||
MAIL_SMTP_HOST=smtp.mo-code.at
|
||||
MAIL_SMTP_PORT=587
|
||||
MAIL_SMTP_USER=online-nennen@mo-code.at
|
||||
MAIL_SMTP_PASSWORD=dein-smtp-passwort
|
||||
```
|
||||
|
||||
### Starten
|
||||
|
||||
```bash
|
||||
docker-compose -f dc-backend.yaml up -d mail-service postgres consul
|
||||
```
|
||||
|
||||
## 3. Frontend (Wasm Web App)
|
||||
|
||||
Die Web-App kommuniziert direkt mit dem `mail-service`.
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
./gradlew :frontend:shells:meldestelle-web:wasmJsBrowserDistribution
|
||||
```
|
||||
|
||||
Die Artefakte liegen in `frontend/shells/meldestelle-web/build/dist/wasmJs/productionExecutable/`.
|
||||
|
||||
### Konfiguration (Laufzeit)
|
||||
|
||||
In der `index.html` oder über ein vorgeschaltetes Nginx können globale Variablen gesetzt werden, um die Backend-URLs zu
|
||||
steuern:
|
||||
|
||||
```html
|
||||
|
||||
<script>
|
||||
window.MAIL_SERVICE_URL = "https://nennung.mo-code.at/api/mail";
|
||||
window.API_BASE_URL = "https://nennung.mo-code.at/api";
|
||||
</script>
|
||||
```
|
||||
|
||||
## 4. Sicherheit & Härtung
|
||||
|
||||
- **CORS:** Der `MailController` ist aktuell für `localhost:8080` und `nennung.mo-code.at` freigeschaltet.
|
||||
- **Reverse Proxy:** Es wird empfohlen, einen Nginx oder Traefik mit SSL (Let's Encrypt) vor den Stack zu schalten.
|
||||
- **Mail-Absender:** Die Absender-Adresse ist im `MailController` hartcodiert auf `online-nennen@mo-code.at`. Dies
|
||||
sollte bei Bedarf angepasst werden.
|
||||
|
||||
---
|
||||
*Dokumentiert durch den Lead Architect am 15. April 2026.*
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
type: How-to
|
||||
status: ACTIVE
|
||||
owner: DevOps Engineer
|
||||
---
|
||||
|
||||
# Runbook: Caddy & Pangolin Deployment (Plan-B Setup)
|
||||
|
||||
Dieses Dokument sichert das Wissen über die Konfiguration von Caddy als Webserver/Reverse-Proxy in Kombination mit Pangolin-Tunneln, welches während der "Plan-B" Online-Nennung erarbeitet wurde.
|
||||
|
||||
## 1. Architektur-Übersicht
|
||||
|
||||
* **Pangolin:** Stellt den sicheren Tunnel vom lokalen Netzwerk (Zora) ins Internet her (ersetzt Cloudflare). Leitet Traffic auf spezifische lokale Ports weiter.
|
||||
* **Caddy:** Agiert als Reverse-Proxy und TLS-Terminierungspunkt. Nimmt Traffic von Pangolin (und lokalem Netz) an und routet ihn zu den internen Docker-Services (z.B. Frontend-Web, API-Gateway).
|
||||
|
||||
## 2. Caddy Konfiguration (`Caddyfile`)
|
||||
|
||||
Die Konfiguration befindet sich in `config/docker/caddy/web-app/Caddyfile`.
|
||||
|
||||
### Wichtige Erkenntnisse / Fallstricke:
|
||||
* **TLS/SSL:** Caddy wurde mit `auto_https off` konfiguriert, da die SSL-Terminierung extern (Pangolin/Edge) erfolgt. Caddy läuft intern auf Port 80.
|
||||
* **Same-Origin Strategy:** Um CORS-Probleme im Browser zu vermeiden, werden alle API-Anfragen (`/api/*`) über Caddy an den `mail-service:8085` geproxt. Dies macht die App robuster gegen Browser-Security-Policies.
|
||||
* **MIME-Types:** Explizite Setzung von `application/wasm` für `.wasm` Dateien ist für KMP-Web-Apps kritisch (siehe Snippet).
|
||||
* **COOP/COEP Header:** Für WASM/KMP-Web-Apps sind `Cross-Origin-Embedder-Policy "require-corp"` und `Cross-Origin-Opener-Policy "same-origin"` essentiell, damit SharedArrayBuffer etc. funktionieren.
|
||||
* **Caching:**
|
||||
* Assets mit Hashes im Namen sind `immutable` (max-age 1 Jahr).
|
||||
* `.wasm` und `.js` Dateien wurden während Plan-B auf `no-store, no-cache, must-revalidate` gesetzt, um sicherzustellen, dass Teilnehmer immer die aktuellste Logik erhalten.
|
||||
* **Header-Weiterleitung:** Wichtige Header für das Backend: `X-Real-IP`, `X-Forwarded-For`, `X-Forwarded-Proto`.
|
||||
|
||||
### Aktuelles Plan-B Snippet:
|
||||
```caddyfile
|
||||
{
|
||||
auto_https off
|
||||
}
|
||||
|
||||
:80 {
|
||||
root * /usr/share/caddy
|
||||
|
||||
header {
|
||||
Cross-Origin-Embedder-Policy "require-corp"
|
||||
Cross-Origin-Opener-Policy "same-origin"
|
||||
}
|
||||
|
||||
# API Proxy (Same-Origin Strategy)
|
||||
handle /api/* {
|
||||
reverse_proxy mail-service:8085 {
|
||||
header_up Host {upstream_hostport}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
|
||||
# Wasm MIME & Caching
|
||||
@wasm path *.wasm
|
||||
header @wasm Content-Type "application/wasm"
|
||||
|
||||
@wasm_js path *.wasm *.js
|
||||
header @wasm_js Cache-Control "no-store, no-cache, must-revalidate"
|
||||
|
||||
# SPA Fallback
|
||||
handle {
|
||||
try_files {path} /index.html
|
||||
file_server
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Pangolin Konfiguration
|
||||
|
||||
### Erkenntnisse aus Plan-B:
|
||||
* **Tunnel-Endpunkt:** Pangolin leitet den Traffic von der öffentlichen Domain (z.B. `meldestelle.mo-code.at`) auf den lokalen Port der Zora-Instanz weiter (standardmäßig Port 80, gemappt auf Host-Port via Docker).
|
||||
* **Stabilität:** Der Pangolin-Client läuft als persistenter Dienst auf der Zora-Node. Er ist extrem stabil gegenüber IP-Wechseln des ISP (DSL-Reconnect).
|
||||
* **Konfiguration:** Erfolgt primär über das Pangolin-Dashboard (Web-UI). Wichtig ist das Mapping der Resource auf die interne IP von Zora.
|
||||
|
||||
## 4. Deployment-Workflow (Erkenntnisse)
|
||||
|
||||
### SMTP-Härtung (Plan-B Mail-Service)
|
||||
In `dc-planb.yaml` wurden folgende Einstellungen für World4You (SMTP) als stabil verifiziert:
|
||||
```yaml
|
||||
SPRING_MAIL_HOST: "smtp.world4you.com"
|
||||
SPRING_MAIL_PORT: "587"
|
||||
SPRING_MAIL_PROPERTIES_MAIL_SMTP_STARTTLS_ENABLE: "true"
|
||||
SPRING_MAIL_PROPERTIES_MAIL_SMTP_STARTTLS_REQUIRED: "true"
|
||||
```
|
||||
Wichtig: `STARTTLS_REQUIRED` verhindert den Versand, falls keine verschlüsselte Verbindung aufgebaut werden kann.
|
||||
|
||||
### Infrastruktur-Optimierung
|
||||
* **Zero-Downtime:** `docker compose exec web-app caddy reload --config /etc/caddy/Caddyfile` ermöglicht Konfigurationsänderungen ohne Container-Neustart.
|
||||
* **Health-Check:** Der `/health` Endpunkt in Caddy wurde genutzt, um die Erreichbarkeit des Containers zu prüfen.
|
||||
* **In-Memory DB:** Für Plan-B wurde die H2-Datenbank (In-Memory) genutzt, da keine Persistenz über den Turnier-Zeitraum hinaus (außer E-Mail-Kopien) nötig war. Dies vereinfachte das Deployment massiv.
|
||||
|
||||
---
|
||||
*Hinweis: Dieses Dokument basiert auf den erfolgreichen Feld-Tests vom April 2026.*
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
type: Guide
|
||||
status: ACTIVE
|
||||
owner: DevOps Engineer
|
||||
last_update: 2026-03-05
|
||||
tags: [backup, postgres, zora, cron]
|
||||
---
|
||||
|
||||
# 🛡️ Guide: Automatisierte Backups auf Zora
|
||||
|
||||
Dieser Guide beschreibt, wie du regelmäßige Backups der Meldestelle-Datenbank und Konfigurationen auf Zora einrichtest.
|
||||
|
||||
## 📋 Voraussetzungen
|
||||
|
||||
* SSH-Zugriff auf Zora (`ssh user@10.0.0.xx`).
|
||||
* Docker-Container `meldestelle-postgres` läuft.
|
||||
* Das Skript `config/scripts/backup.sh` liegt im Repo.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Schritt-für-Schritt Anleitung
|
||||
|
||||
### 1. Backup-Skript vorbereiten
|
||||
|
||||
Das Skript liegt bereits im Repo unter `config/scripts/backup.sh`.
|
||||
Du musst es auf Zora ausführbar machen und ggf. anpassen.
|
||||
|
||||
1. Verbinde dich mit Zora:
|
||||
```bash
|
||||
ssh user@zora-ip
|
||||
cd ~/meldestelle
|
||||
```
|
||||
|
||||
2. Mache das Skript ausführbar:
|
||||
```bash
|
||||
chmod +x config/scripts/backup.sh
|
||||
```
|
||||
|
||||
3. **WICHTIG:** Prüfe die Variablen im Skript (`nano config/scripts/backup.sh`):
|
||||
* `BACKUP_DIR`: Wohin sollen die Backups? (Standard: `/home/grandmo/backups/meldestelle`)
|
||||
* `DB_CONTAINER`: Name des Postgres-Containers (Standard: `meldestelle-postgres`).
|
||||
* `DB_USER`: Datenbank-User (Standard: `meldestelle`).
|
||||
|
||||
### 2. Cronjob einrichten (Automatische Ausführung)
|
||||
|
||||
Wir wollen das Backup jede Nacht um 03:00 Uhr laufen lassen.
|
||||
|
||||
1. Öffne die Crontab des Users:
|
||||
```bash
|
||||
crontab -e
|
||||
```
|
||||
|
||||
2. Füge folgende Zeile am Ende hinzu:
|
||||
```cron
|
||||
# Meldestelle Backup: Täglich um 03:00 Uhr
|
||||
0 3 * * * /home/grandmo/meldestelle/config/scripts/backup.sh >> /home/grandmo/meldestelle/logs/backup.log 2>&1
|
||||
```
|
||||
|
||||
3. Speichern und schließen (`Ctrl+O`, `Enter`, `Ctrl+X`).
|
||||
|
||||
### 3. Testen
|
||||
|
||||
Führe das Skript einmal manuell aus, um sicherzustellen, dass alles klappt:
|
||||
|
||||
```bash
|
||||
./config/scripts/backup.sh
|
||||
```
|
||||
|
||||
* Prüfe den Output: "✅ Backup abgeschlossen".
|
||||
* Prüfe das Backup-Verzeichnis: `ls -lh /home/grandmo/backups/meldestelle`.
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Wiederherstellung (Restore)
|
||||
|
||||
Falls der Ernstfall eintritt (Datenbank korrupt oder gelöscht):
|
||||
|
||||
1. Stoppe die App-Container (aber lasse die DB laufen):
|
||||
```bash
|
||||
docker compose stop api-gateway ping-service keycloak
|
||||
```
|
||||
|
||||
2. Entpacke das Backup:
|
||||
```bash
|
||||
gunzip /home/grandmo/backups/meldestelle/db_dump_YYYY-MM-DD.sql.gz
|
||||
```
|
||||
|
||||
3. Spiele den Dump ein:
|
||||
```bash
|
||||
cat db_dump_YYYY-MM-DD.sql | docker exec -i meldestelle-postgres psql -U meldestelle
|
||||
```
|
||||
|
||||
4. Starte die App wieder:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Tipps
|
||||
|
||||
* **Offsite-Backup:** Kopiere die Backups regelmäßig auf einen externen Speicher (z.B. NAS oder Cloud), falls die Festplatte von Zora ausfällt. `rsync` ist dein Freund!
|
||||
* **Monitoring:** Prüfe regelmäßig die Log-Datei `logs/backup.log`, ob Fehler auftreten.
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
type: Guide
|
||||
status: ACTIVE
|
||||
owner: DevOps Engineer
|
||||
last_update: 2026-03-05
|
||||
tags: [deployment, git, zora, docker]
|
||||
---
|
||||
|
||||
# 🚀 Guide: Git-basiertes Deployment auf Zora einrichten
|
||||
|
||||
Dieser Guide beschreibt, wie du den Produktions-Server "Zora" so konfigurierst, dass er Updates direkt aus dem Gitea-Repository zieht. Dies stellt sicher, dass nicht nur die Docker-Images, sondern auch die `docker-compose.yaml` Konfigurationen immer synchron sind.
|
||||
|
||||
## 📋 Voraussetzungen
|
||||
|
||||
* SSH-Zugriff auf Zora (`ssh user@10.0.0.xx`).
|
||||
* `git` und `docker` sind auf Zora installiert.
|
||||
* Ein Gitea-Account mit Zugriff auf das `meldestelle` Repository.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Schritt-für-Schritt Anleitung
|
||||
|
||||
### 1. Projektverzeichnis vorbereiten
|
||||
|
||||
Verbinde dich mit Zora und gehe in das Zielverzeichnis:
|
||||
|
||||
```bash
|
||||
ssh user@zora-ip
|
||||
cd ~/meldestelle
|
||||
```
|
||||
|
||||
**WICHTIG:** Falls dort bereits Konfigurationsdateien liegen, erstelle ein Backup:
|
||||
|
||||
```bash
|
||||
mkdir -p ../backup_meldestelle_$(date +%F)
|
||||
cp * ../backup_meldestelle_$(date +%F)/
|
||||
```
|
||||
|
||||
### 2. Git-Repository initialisieren
|
||||
|
||||
Wir verwandeln den Ordner in ein Git-Repo, das mit Gitea verknüpft ist.
|
||||
|
||||
```bash
|
||||
# Git initialisieren
|
||||
git init
|
||||
|
||||
# Remote Origin hinzufügen (HTTPS)
|
||||
git remote add origin https://git.mo-code.at/mocode-software/meldestelle.git
|
||||
|
||||
# Neueste Änderungen holen
|
||||
git fetch origin
|
||||
|
||||
# Hard Reset auf den main-Branch (ACHTUNG: Überschreibt lokale Änderungen!)
|
||||
git reset --hard origin/main
|
||||
```
|
||||
|
||||
*Jetzt sollten alle `dc-*.yaml` Dateien und Skripte im Ordner liegen.*
|
||||
|
||||
### 3. Environment-Variablen (.env) konfigurieren
|
||||
|
||||
Die `.env` Datei enthält Geheimnisse und wird **nicht** in Git gespeichert.
|
||||
|
||||
1. Kopiere die Vorlage:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. Bearbeite die Datei:
|
||||
```bash
|
||||
nano .env
|
||||
```
|
||||
|
||||
3. **Wichtige Anpassungen für Production:**
|
||||
* `KC_HOSTNAME=auth.mo-code.at`
|
||||
* `KC_ISSUER_URI=https://auth.mo-code.at/realms/meldestelle`
|
||||
* `KC_JWK_SET_URI=http://keycloak:8080/realms/meldestelle/protocol/openid-connect/certs`
|
||||
* Setze starke Passwörter für Datenbank, Keycloak und Grafana!
|
||||
|
||||
### 4. Deployment-Skript einrichten
|
||||
|
||||
Das Skript `config/scripts/deploy.sh` liegt nun im Repo bereit (sobald wir es gepusht haben).
|
||||
Du kannst es direkt nutzen oder manuell anlegen:
|
||||
|
||||
**Inhalt von `deploy.sh`:**
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# deploy.sh - Update & Restart Meldestelle Stack
|
||||
|
||||
echo "🚀 Starte Deployment..."
|
||||
|
||||
# 1. Neueste Konfiguration holen
|
||||
echo " -> Git Pull..."
|
||||
git pull origin main
|
||||
|
||||
# 2. Neue Images laden
|
||||
echo " -> Docker Pull..."
|
||||
docker compose pull
|
||||
|
||||
# 3. Container neu starten (nur geänderte)
|
||||
echo " -> Docker Up..."
|
||||
docker compose up -d --remove-orphans
|
||||
|
||||
# 4. Aufräumen
|
||||
echo " -> Pruning old images..."
|
||||
docker image prune -f
|
||||
|
||||
echo "✅ Deployment erfolgreich!"
|
||||
```
|
||||
|
||||
Mache das Skript ausführbar:
|
||||
|
||||
```bash
|
||||
chmod +x config/scripts/deploy.sh
|
||||
# Optional: Symlink ins Root
|
||||
ln -s config/scripts/deploy.sh deploy.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Workflow für Updates
|
||||
|
||||
Wenn du Code oder Konfiguration gepusht hast und die CI-Pipeline durchgelaufen ist:
|
||||
|
||||
1. SSH auf Zora.
|
||||
2. `cd ~/meldestelle`
|
||||
3. `./deploy.sh`
|
||||
|
||||
Das System ist nun auf dem neuesten Stand!
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: DevOps Engineer
|
||||
last_update: 2026-03-15
|
||||
---
|
||||
# Heimnetzwerk
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
%% Internet & Modem
|
||||
WAN((Internet A1)) --- ONT[A1 Glasfaser ONT]
|
||||
ONT ---|RJ45 - VLAN 2 / PPPoE| Router[VLAN-Router / Firewall]
|
||||
|
||||
subgraph Keller [Keller: Technik & Server]
|
||||
Router ---|Trunk: Alle VLANs| Switch[Managed 10G Switch]
|
||||
|
||||
subgraph ServerZone [VLAN 20: Apps & Server]
|
||||
Switch ===|10GbE Port 1| MiniPC[Mini-PC Server]
|
||||
Switch ---|10GbE Port 2| MiniPC
|
||||
note1[Dual 10G für Redundanz oder Bonding]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph Etagen [Wohnbereich: EG & DG]
|
||||
Switch ---|Cat-7| AP_EG[Access Point EG]
|
||||
Switch ---|Cat-7| AP_DG[Access Point DG]
|
||||
|
||||
AP_EG -.-> SSID_P((WLAN Privat - VLAN 10))
|
||||
AP_EG -.-> SSID_A((WLAN Apps - VLAN 20))
|
||||
end
|
||||
|
||||
%% Definition der VLANs
|
||||
classDef vlan10 fill:#e1f5fe,stroke:#01579b
|
||||
classDef vlan20 fill:#fff3e0,stroke:#e65100
|
||||
class MiniPC,SSID_A vlan20
|
||||
class SSID_P vlan10
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: DevOps Engineer
|
||||
last_update: 2026-03-15
|
||||
---
|
||||
# Konfigurations-Matrix
|
||||
|
||||
### Dev-Umgebungen vs. Zora (Production)
|
||||
|
||||
|
||||
| Variable | .env / .env.example (Dev Default) | Gitea-Secrets (Zora Production) | Zweck / Beschreibung |
|
||||
|:----------------------------------|:----------------------------------------------------|:----------------------------------------------|:----------------------------------------------------------------------------------|
|
||||
| **POSTGRES_SHARED_BUFFERS** | `256MB` (Default via Compose) | `16GB` | Hauptspeicher für DB-Caching (ca. 25% vom RAM). |
|
||||
| **POSTGRES_EFFECTIVE_CACHE_SIZE** | `768MB` (Default via Compose) | `48GB` | Schätzwert für den OS-Cache (ca. 75% vom RAM). |
|
||||
| **POSTGRES_USER** | `meldestelle` | `meldestelle` (oder eigener Secret-User) | Administrator-Nutzer der Datenbank. |
|
||||
| **POSTGRES_PASSWORD** | `meldestelle` | `[STARKES_PASSWORT]` | Passwort für den DB-Zugriff (SSoT-Geheimnis). |
|
||||
| **POSTGRES_DB** | `meldestelle` | `meldestelle` | Name der primären Datenbank-Instanz. |
|
||||
| **POSTGRES_PORT** | `5432:5432` | `5432:5432` | Mapping vom Host zum Container. |
|
||||
| **PROJECT_NAME** | `meldestelle` | `meldestelle` | Präfix für Container-Namen auf dem Host. |
|
||||
| **KC_HOSTNAME** | `localhost` | `<SERVER_IP_ODER_DOMAIN>` | Erreichbarkeit von Keycloak (wichtig für Tokens). Auf dem Server nie `localhost`! |
|
||||
| **KC_DB_URL** | `jdbc:postgresql://postgres:5432/pg-meldestelle-db` | `jdbc:postgresql://postgres:5432/meldestelle` | JDBC-String (muss zur POSTGRES_DB passen). |
|
||||
| **VALKEY_MAXMEMORY** | `256mb` | `4gb` bis `8gb` | Zora hat 64 GB RAM; hier können wir großzügig cachen. |
|
||||
| **VALKEY_POLICY** | `allkeys-lru` | `allkeys-lru` | Wirft die am längsten nicht genutzten Schlüssel raus, wenn der Speicher voll ist. |
|
||||
| **VALKEY_PASSWORD** | `leer` oder `dev` | `[STARKES_SECRET]` | SSoT-Geheimnis aus Gitea-Secrets. |
|
||||
| **VALKEY_PORT** | `6379:6379` | `6379:6379` | Standard-Port-Mapping. |
|
||||
| **KC_HEAP_MAX** | `1024m` | `4096m` | Mehr Power für Zoras 64 GB RAM. |
|
||||
| **KC_COMMAND** | `start-dev --import-realm` | `start --optimized --import-realm` | `start-dev` + pre-built Image = Konflikt! Server immer mit `--optimized`. |
|
||||
| **KC_HOSTNAME_STRICT** | `false` | `false` | `false` = beliebige Hostnamen erlaubt (Pflicht für HTTP-only Betrieb). |
|
||||
| **KC_HOSTNAME_STRICT_HTTPS** | `false` | `false` | `false` = kein HTTPS-Zwang. Bei TLS-Einrichtung auf `true` setzen. |
|
||||
| **KC_MANAGEMENT_PORT** | `9000:9000` | `9000:9000` | Health/Metrics-Port (immer auf 0.0.0.0 gebunden, unabhängig von KC_HOSTNAME). |
|
||||
| **KC_DB_PASSWORD** | `meldestelle` | `[GEHEIM]` | SSoT-Passwort aus den Gitea-Secrets. |
|
||||
| **KEYCLOAK_IMAGE_TAG** | `26.4` | `26.4` | Versionierung. |
|
||||
| **ZIPKIN_HEAP** | `256m` | `1024m` | Mehr Puffer für Tracing-Daten auf Zora. |
|
||||
| **CONSUL_IMAGE** | `hashicorp/consul:1.22.1` | `hashicorp/consul:1.22.1` | Versionierung. |
|
||||
| **MP_MAX_MESSAGES** | `500` | `5000` | Mailpit Speicherlimit. |
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: DevOps Engineer
|
||||
last_update: 2026-03-15
|
||||
---
|
||||
|
||||
## 🛡️ Pangolin vs. Cloudflare Tunnel
|
||||
|
||||
| Merkmal | Cloudflare Tunnel (`cloudflared`) | Pangolin (Self-Hosted) |
|
||||
|------------------|---------------------------------------------|-------------------------------------------------|
|
||||
| **Kontrolle** | Zentralisiert (Cloudflare sieht Traffic). | **Dezentral** (Du besitzt den VPS & Schlüssel). |
|
||||
| **Datenschutz** | SSL terminiert bei Cloudflare. | **End-to-End** (SSL terminiert auf DEINEM VPS). |
|
||||
| **AGB / Limits** | Verbot von Video-Streaming (Plex/Jellyfin). | **Keine Limits** (Du bestimmst den Traffic). |
|
||||
| **Protokolle** | Primär TCP/HTTP. | **TCP & UDP** (Dank WireGuard-Basis). |
|
||||
| **Kosten** | Kostenlos (Free Tier). | VPS-Miete (ca. 4€ bei Hetzner). |
|
||||
| **Features** | WAF, DDoS-Schutz (proprietär). | SSO, CrowdSec, Geoblocking (Open Source). |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Deployment-Bauplan (Hetzner + MS-R1)
|
||||
|
||||
### Teil 1: VPS Setup (Hetzner Cloud)
|
||||
|
||||
1. Erstelle einen **CX21** VPS (Location: Frankfurt für geringste Latenz).
|
||||
2. Installiere Docker und erstelle das Pangolin-Verzeichnis.
|
||||
|
||||
**Docker Compose für den VPS (`compose.yaml`):**
|
||||
|
||||
```yaml
|
||||
services:
|
||||
pangolin:
|
||||
image: ghcr.io/m-pennat/pangolin:latest
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80" # HTTP (Let's Encrypt)
|
||||
- "443:443" # HTTPS
|
||||
- "51820:51820/udp" # WireGuard Tunnel
|
||||
volumes:
|
||||
- ./data:/var/lib/pangolin
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
environment:
|
||||
- PANGOLIN_DOMAIN=dashboard.deinedomain.de
|
||||
|
||||
```
|
||||
|
||||
*Nach dem Start (`docker compose up -d`) unter der Domain einloggen und einen neuen **Site-Client** anlegen, um den **Enrollment Token** zu erhalten.*
|
||||
|
||||
---
|
||||
|
||||
### Teil 2: Home-Server Setup (MS-R1 / Debian 12 arm64)
|
||||
|
||||
Auf deinem MS-R1 installierst du den Gegenpart **Newt**. Da du Debian 12 auf arm64 nutzt, ist das Setup extrem ressourcensparend.
|
||||
|
||||
**Docker Compose für den MS-R1 (`compose.yaml`):**
|
||||
|
||||
```yaml
|
||||
services:
|
||||
# Der Tunnel-Client
|
||||
newt:
|
||||
image: ghcr.io/m-pennat/newt:latest
|
||||
container_name: newt
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- NEWT_ENROLL_TOKEN=DEIN_TOKEN_VON_HETZNER_VPS
|
||||
depends_on:
|
||||
- gitea
|
||||
|
||||
# Dein Service (Beispiel: Gitea)
|
||||
gitea:
|
||||
image: gitea/gitea:latest
|
||||
container_name: gitea
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./gitea_data:/data
|
||||
# Hinweis: Kein "ports"-Mapping nötig! Zugriff erfolgt über den Tunnel.
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Warum das für dich ein "Erfolg" ist
|
||||
|
||||
* **Kein Port-Forwarding:** Dein Router zu Hause bleibt komplett dicht.
|
||||
* **Arm64-Optimiert:** Debian 12 und die Container nutzen kaum CPU-Zyklen deines MS-R1.
|
||||
* **Identity-Aware Proxy:** Du kannst im Pangolin-Dashboard (Hetzner) mit drei Klicks einstellen, dass man sich erst per **Google** oder **GitHub** anmelden muss, um dein Gitea überhaupt zu sehen.
|
||||
|
||||
---
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,48 @@
|
||||
---
|
||||
type: Reference
|
||||
owner: DevOps Engineer
|
||||
status: ACTIVE
|
||||
review_cycle: 180d
|
||||
last_reviewed: 2025-10-31
|
||||
summary: "Übersicht der wichtigsten lokalen URLs und Ports. Quelle: docker-compose.yaml + config/env"
|
||||
last_update: 2026-03-15
|
||||
---
|
||||
|
||||
# Referenz: Wichtige URLs und Ports (lokal)
|
||||
|
||||
Quelle der Wahrheit für Ports/URLs (Repo-aktuell):
|
||||
|
||||
* `docker-compose.yaml`
|
||||
* `config/env/.env` (und optional `config/env/.env.local`)
|
||||
|
||||
## Infrastruktur
|
||||
|
||||
- API Gateway: http://localhost:8081
|
||||
- Keycloak (Auth): http://localhost:8180
|
||||
- Consul (Service Discovery): http://localhost:8500
|
||||
- PostgreSQL: localhost:5432
|
||||
- Redis: localhost:6379
|
||||
|
||||
## Services
|
||||
|
||||
- Ping Service: http://localhost:8082
|
||||
- Members Service: http://localhost:8083
|
||||
- Horses Service: http://localhost:8084
|
||||
- Events Service: http://localhost:8085
|
||||
- Masterdata Service: http://localhost:8086
|
||||
|
||||
## Monitoring
|
||||
|
||||
- Prometheus: http://localhost:9090
|
||||
- Grafana: http://localhost:3000
|
||||
|
||||
## Clients
|
||||
|
||||
- Web App: http://localhost:4000
|
||||
- Desktop App (VNC): localhost:5901
|
||||
- Desktop App (noVNC): http://localhost:6080
|
||||
|
||||
## Hinweise
|
||||
|
||||
- Die oben genannten Ports sind aus dem aktuellen Compose-/Env-Setup abgeleitet.
|
||||
- Bei Port-Konflikten passe die Werte in `config/env/.env` an (oder nutze lokale Overrides in `config/env/.env.local`).
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: DevOps Engineer
|
||||
last_update: 2026-03-15
|
||||
---
|
||||
# Zipkin Tracing
|
||||
|
||||
## Übersicht
|
||||
Zipkin ist ein verteiltes Tracing-System, das hilft, Latenzprobleme in Microservice-Architekturen zu analysieren. Es sammelt Timing-Daten, die benötigt werden, um Latenzprobleme in Service-Architekturen zu beheben.
|
||||
|
||||
## Konfiguration in Docker Compose
|
||||
Der Zipkin-Service ist in der `docker-compose.yaml` definiert:
|
||||
|
||||
```yaml
|
||||
zipkin:
|
||||
image: "${ZIPKIN_IMAGE:-openzipkin/zipkin:3}"
|
||||
container_name: "${PROJECT_NAME:-meldestelle}-zipkin"
|
||||
restart: no
|
||||
ports:
|
||||
- "${ZIPKIN_PORT:-9411:9411}"
|
||||
profiles: [ "ops", "all" ]
|
||||
networks:
|
||||
meldestelle-network:
|
||||
aliases:
|
||||
- "zipkin"
|
||||
```
|
||||
|
||||
## Integration in Services
|
||||
Die Services (`api-gateway`, `ping-service`, etc.) sind so konfiguriert, dass sie Tracing-Daten an Zipkin senden. Dies geschieht über Umgebungsvariablen in der `docker-compose.yaml`:
|
||||
|
||||
```yaml
|
||||
MANAGEMENT_ZIPKIN_TRACING_ENDPOINT: "${ZIPKIN_ENDPOINT:-http://zipkin:9411/api/v2/spans}"
|
||||
MANAGEMENT_TRACING_SAMPLING_PROBABILITY: "${ZIPKIN_SAMPLING_PROBABILITY:-1.0}"
|
||||
```
|
||||
|
||||
## Zugriff
|
||||
Die Zipkin UI ist unter `http://localhost:9411` erreichbar.
|
||||
|
||||
## Troubleshooting
|
||||
- **Keine Traces sichtbar:** Stelle sicher, dass die Services korrekt gestartet sind und die Umgebungsvariablen für Zipkin gesetzt sind. Prüfe die Logs der Services auf Verbindungsfehler zu Zipkin.
|
||||
- **Zipkin nicht erreichbar:** Prüfe, ob der Container läuft (`docker ps`) und ob der Port 9411 nicht blockiert ist.
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: DevOps Engineer
|
||||
last_update: 2026-03-15
|
||||
---
|
||||
|
||||
# Roadmap: Zora Infrastructure & Deployment (Februar 2026)
|
||||
|
||||
## Phase 1: Identity & CI/CD "Green Light"
|
||||
|
||||
**Ziel:** Den automatisierten Workflow scharf schalten und Berechtigungskonflikte lösen.
|
||||
|
||||
* ✅ **Gitea Secrets Finalisierung:** Erstellen der Secrets
|
||||
* `REGISTRY_TOKEN` und
|
||||
* `REGISTRY_USER` im Repository „Meldestelle“ (Umgehung der `GITEA_`-Namenssperre).
|
||||
*[x] **Runner-Stabilisierung:** _(verifiziert 2026-03-09: Screenshot bestätigt)_
|
||||
* `act_runner` ist `active (running)` seit 06.03.2026, `enabled; preset: enabled`.
|
||||
*[x] Status im Gitea-Interface: Runner ist **Aktiv** (läuft Tasks seit 06.03.2026).
|
||||
*[ ] **Erster Test-Build:**
|
||||
* Erstellen eines Test-Projekts und
|
||||
* Push aus der IDEA auslösen und das Log-Streaming in Gitea Actions verfolgen.
|
||||
*[ ] Verifizieren, dass das Docker-Image in der Gitea-Registry (`git.mo-code.at`) landet.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Core-Infrastructure Deployment
|
||||
|
||||
**Ziel:** Die Basis-Dienste (Datenbanken, Auth) in der `prod-meldestelle` zum Laufen bringen.
|
||||
|
||||
*[ ] **Umgebungs-Konfiguration:** * Vervollständigen der `/home/grandmo/meldestelle/.env` mit echten Passwörtern und
|
||||
Keycloak-Secrets.
|
||||
*[ ] **Stack-Launch (dc-infra.yaml):** * Start von **Postgres**, **Keycloak** und **Valkey** (Redis-Alternative).
|
||||
*[ ] **Wichtig:** Kontrolle der Logs auf ARM64-Kompatibilität (`exec format error` vermeiden).
|
||||
*[ ] **Netzwerk-Check:** * Testen der Erreichbarkeit von Zoras Mail-Relay (`10.0.0.20:25`) aus dem neuen Stack heraus.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Application & Gateway Launch
|
||||
|
||||
**Ziel:** Den Ping-Service über das Spring Cloud Gateway erreichbar machen.
|
||||
|
||||
*[ ] **Backend-Start (dc-backend.yaml):** * Deployment des **api-gateways** und des **ping-services**.
|
||||
*[ ] **Pangolin-Tunnel Update:** * Hinzufügen der Route `api.mo-code.at`, die auf die IP der Meldestelle (`10.0.0.50`)
|
||||
und den Port des Gateways (`8081`) zeigt.
|
||||
*[ ] **Keycloak-Veredelung:** * Konfiguration des Realms und Erstellen des Clients für die Meldestelle im
|
||||
Keycloak-Admin-Panel (via `auth.mo-code.at`).
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Resilience & Hardening
|
||||
|
||||
**Ziel:** Das System gegen äußere Einflüsse und Datenverlust absichern.
|
||||
|
||||
*[ ] **USV-Anbindung (NUT):** * Installation und Konfiguration der Network UPS Tools für die **Eaton 3S850D**.
|
||||
*[ ] Test des Shutdown-Szenarios bei kritischem Akkustand.
|
||||
*[ ] **Monitoring-Integration (dc-ops.yaml):** * Start von **Prometheus** und **Grafana**.
|
||||
*[ ] Anbindung des Alertmanagers an das Postfix-Relay für E-Mail-Alarme.
|
||||
*[ ] **Backup-Vollendung:** * Erweiterung des nächtlichen 03:00-Uhr-Skripts um die Sicherung der neuen Docker-Volumes in
|
||||
`prod-meldestelle`.
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: DevOps Engineer
|
||||
last_update: 2026-03-15
|
||||
---
|
||||
## 🏗️ System-Architektur "Zora" (ARM64)
|
||||
|
||||
**Stand: 05. März 2026**
|
||||
|
||||
Die gesamte Umgebung läuft auf einer **ARM64-Architektur** (Cortex-A720). Dies bietet hohe Performance bei geringem Energieverbrauch, erfordert aber spezifische Build-Konfigurationen (SVE-Support, ARM64-Images).
|
||||
|
||||
### 1. Gitea & Central Registry (LXC)
|
||||
|
||||
* **Rolle:** Quellcode-Verwaltung, CI/CD-Steuerung und Container-Registry.
|
||||
* **IP-Adresse:** `10.0.0.22:3000` (Intern) / `git.mo-code.at` (Extern).
|
||||
* **Registry:** Integriert unter `git.mo-code.at`.
|
||||
* **Pfad-Struktur:** `git.mo-code.at/mocode-software/meldestelle/<service-name>`
|
||||
|
||||
|
||||
* **Wichtig für Entwickler:** Authentifizierung erfolgt via Gitea-Benutzer oder Access-Token.
|
||||
|
||||
### 2. Gitea-Power-Runner (VM 102)
|
||||
|
||||
Der Runner ist das "Arbeitstier" des Systems.
|
||||
|
||||
* **Software:** `act_runner` (v0.2.11).
|
||||
* **Ressourcen:** 16 GiB RAM (optimiert für schwere Kotlin/JS-Builds).
|
||||
* **Build-Stack:** Java 25 (Temurin), Gradle 9.5.0.
|
||||
* **Besonderheiten:**
|
||||
* **Sequenzieller Build:** Um GitHub Rate-Limits und RAM-Spitzen zu vermeiden, arbeitet der Runner die Matrix-Jobs kontrolliert ab.
|
||||
* **Docker Buildx:** Native ARM64-Builds mit Optimierungs-Flags (`-XX:+UseTransparentHugePages`, `-XX:+UseSVE=1`).
|
||||
* **Der "Terser-Fix":** Für die Web-App wurde die Minifizierung deaktiviert, da das Terser-Plugin mit der `sqlite-wasm` Bibliothek kollidiert. Dies wird über die Datei `z_disable-minification.js` im Webpack-Ordner sichergestellt (das Präfix `z_` erzwingt die Ausführung als letzten Schritt).
|
||||
|
||||
|
||||
|
||||
### 3. Meldestelle-Host (VM 110 / .50)
|
||||
|
||||
Die Zielumgebung für das Deployment.
|
||||
|
||||
* **Deployment-Methode:** Docker Compose mit Profilen (`infra`, `backend`, `gui`).
|
||||
* **Verzeichnis:** `~/meldestelle/`
|
||||
* **Service-Übersicht:**
|
||||
|
||||
| Dienst | Externer Port | Interner Port | Image-Name (Registry) |
|
||||
|------------------|---------------|---------------|-----------------------|
|
||||
| **Web-App** | 4000 | 4000 (Caddy) | `web-app` |
|
||||
| **API-Gateway** | 8081 | 8081 | `api-gateway` |
|
||||
| **Keycloak** | 8180 (Admin) | 8080 | `keycloak` |
|
||||
| **Ping-Service** | 8082 | 8082 | `ping-service` |
|
||||
| **Consul** | 8500 | 8500 | `hashicorp/consul` |
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Der CI/CD Workflow
|
||||
|
||||
Wenn ein Entwickler Code in den `main`-Branch pusht:
|
||||
|
||||
1. **Trigger:** Gitea Actions erkennt den Push.
|
||||
2. **Build:** Der Runner (VM 102) baut die Services.
|
||||
* Das Frontend nutzt Kotlin/JS.
|
||||
* Das Backend nutzt Kotlin/JVM (Java 25).
|
||||
|
||||
|
||||
3. **Tagging:** Jedes Image erhält zwei Tags:
|
||||
* `:latest` (für den schnellen Pull).
|
||||
* `:sha-<long>` (für eindeutige Versionierung und Rollbacks).
|
||||
|
||||
|
||||
4. **Registry:** Die Images werden nach `git.mo-code.at` gepusht.
|
||||
5. **Deployment (Manuell/Host):** Auf der VM 110 wird via `docker compose pull` die neueste Version geladen.
|
||||
|
||||
---
|
||||
|
||||
## 📝 Wichtige Hinweise für Entwickler
|
||||
|
||||
* **Frontend-Builds:** Lokal kann weiterhin mit `./gradlew jsBrowserDistribution` gearbeitet werden. Im Production-Build (`-Pproduction=true`) sorgt die Konfiguration im Ordner `webpack.config.d` dafür, dass der Runner nicht abstürzt.
|
||||
* **Docker-Registry:** Vor dem ersten Pull auf einem neuen System ist ein `docker login git.mo-code.at` zwingend erforderlich.
|
||||
* **Service-Discovery:** Die Dienste kommunizieren intern über das Docker-Netzwerk `meldestelle-network`. Namen wie `api-gateway`, `keycloak` oder `postgres` werden automatisch aufgelöst.
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: DevOps Engineer
|
||||
last_update: 2026-01-15
|
||||
---
|
||||
|
||||
# Infrastruktur: API-Gateway
|
||||
|
||||
Dieses Dokument beschreibt die Konfiguration und die Aufgaben des API-Gateways im "Meldestelle"-Projekt.
|
||||
|
||||
## Zweck
|
||||
|
||||
Das API-Gateway (implementiert mit Spring Cloud Gateway) ist der zentrale, nach außen exponierte Einstiegspunkt für alle HTTP-Anfragen an das System.
|
||||
|
||||
Seine Hauptaufgaben sind:
|
||||
* **Routing:** Leitet Anfragen an den korrekten Microservice weiter (z.B. `/api/ping/**` -> `ping-service`).
|
||||
* **Security:** Erzwingt die Authentifizierung und Autorisierung für alle eingehenden Anfragen. Es validiert die von Keycloak ausgestellten JWTs.
|
||||
* **Cross-Cutting Concerns:** Implementiert übergreifende Funktionalitäten wie Rate Limiting, Logging und Circuit Breaking (mit Resilience4j).
|
||||
|
||||
## Konfiguration
|
||||
|
||||
Die Routen werden in der `application.yml` des Gateways definiert. Die Konfiguration für die Service Discovery erfolgt über Consul.
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
cloud:
|
||||
gateway:
|
||||
discovery:
|
||||
locator:
|
||||
enabled: true
|
||||
lower-case-service-id: true
|
||||
routes:
|
||||
- id: ping-service
|
||||
uri: lb://ping-service
|
||||
predicates:
|
||||
- Path=/api/ping/**
|
||||
filters:
|
||||
- StripPrefix=2
|
||||
```
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
type: Guide
|
||||
status: ACTIVE
|
||||
owner: DevOps Engineer
|
||||
tags: [jwt, oidc, keycloak, docker, networking, security]
|
||||
last_update: 2026-03-15
|
||||
---
|
||||
|
||||
# Leitfaden: JWT-Validierung in der Docker-Umgebung
|
||||
|
||||
Dieser Leitfaden erklärt eine kritische Herausforderung und deren Lösung bei der Arbeit mit Keycloak (OIDC) und Microservices in einer Docker-Umgebung.
|
||||
|
||||
## Das Problem: Das "Split Horizon"-Dilemma
|
||||
|
||||
In unserer lokalen Docker-Umgebung existieren zwei "Sichtweisen" (Horizons) auf Keycloak:
|
||||
|
||||
1. **Die externe Sicht (Browser/Postman):** Ein Client außerhalb von Docker (z.B. Postman) greift auf Keycloak über `http://localhost:8180` zu. Wenn dieser Client ein Token anfordert, stellt Keycloak dieses Token mit dem Issuer (`iss`) Claim `http://localhost:8180/realms/meldestelle` aus.
|
||||
|
||||
2. **Die interne Sicht (Microservices):** Ein Service innerhalb des Docker-Netzwerks (z.B. `ping-service`) kann `localhost` nicht verwenden, um Keycloak zu erreichen. Er muss den Docker-internen Hostnamen `http://keycloak:8080` verwenden.
|
||||
|
||||
Wenn der Service nun das von außen kommende Token validieren will, passiert Folgendes:
|
||||
* Das Token sagt: "Ich wurde von `http://localhost:8180/realms/meldestelle` ausgestellt."
|
||||
* Die Standardkonfiguration des Services sagt: "Ich vertraue aber nur Token von `http://keycloak:8080/realms/meldestelle`."
|
||||
* **Ergebnis:** Die Validierung schlägt mit `iss claim is not valid` fehl.
|
||||
|
||||
## Die Lösung: Getrennte Konfiguration von Issuer und JWK-Pfad
|
||||
|
||||
Die Lösung besteht darin, Spring Security so zu konfigurieren, dass es zwischen der **Validierung des Issuers** und dem **technischen Abruf der Schlüssel** unterscheidet.
|
||||
|
||||
* `spring.security.oauth2.resourceserver.jwt.issuer-uri`: **Muss exakt mit dem `iss`-Claim im Token übereinstimmen.** Hier verwenden wir die öffentliche URL.
|
||||
* `spring.security.oauth2.resourceserver.jwt.jwk-set-uri`: Der technische Endpunkt, unter dem der Service die Public Keys (JWKs) zur Signaturprüfung abruft. Hier verwenden wir die interne Docker-URL.
|
||||
|
||||
### Implementierung
|
||||
|
||||
Wir steuern dies zentral über unsere `.env`-Datei, um eine "Single Source of Truth" zu haben:
|
||||
|
||||
**`.env`**
|
||||
```dotenv
|
||||
# Public Issuer URI (must match the token issuer from browser/postman)
|
||||
KC_ISSUER_URI=http://localhost:8180/realms/meldestelle
|
||||
|
||||
# Internal JWK Set URI (for service-to-service communication within Docker)
|
||||
KC_JWK_SET_URI=http://keycloak:8080/realms/meldestelle/protocol/openid-connect/certs
|
||||
```
|
||||
|
||||
Diese Variablen werden dann in der `dc-backend.yaml` an die Services durchgereicht:
|
||||
|
||||
**`dc-backend.yaml`**
|
||||
```yaml
|
||||
services:
|
||||
ping-service:
|
||||
environment:
|
||||
# ...
|
||||
# --- KEYCLOAK ---
|
||||
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: "${KC_ISSUER_URI}"
|
||||
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: "${KC_JWK_SET_URI}"
|
||||
```
|
||||
|
||||
Diese Konfiguration stellt sicher, dass die Validierung erfolgreich ist, während die Kommunikation innerhalb des Docker-Netzwerks korrekt funktioniert.
|
||||
|
||||
---
|
||||
|
||||
## Empfehlungen für die Entwickler
|
||||
|
||||
### Für den Senior Backend Developer
|
||||
|
||||
1. **Konfigurations-Muster übernehmen:** Wenn du neue Microservices erstellst, kopiere exakt den `environment`-Block für Keycloak aus dem `ping-service` in der `dc-backend.yaml`. Das stellt sicher, dass die JWT-Validierung von Anfang an korrekt funktioniert.
|
||||
2. **Rollen-Synchronisation:** Bevor du einen Endpunkt mit `@PreAuthorize("hasRole('DEINE_ROLLE')")` sicherst, stelle sicher, dass die Rolle `DEINE_ROLLE` auch in der `config/docker/keycloak/meldestelle-realm.json` definiert ist.
|
||||
3. **Debugging-Tipp:** Bei einem `401 Unauthorized` auf einem geschützten Endpunkt, prüfe immer zuerst die Logs des Services. Die Fehlermeldungen von Spring Security sind sehr aussagekräftig (z.B. `iss claim not valid`, `Connection refused`, `An error occurred while attempting to decode the Jwt`).
|
||||
|
||||
### Für den KMP Frontend Expert
|
||||
|
||||
1. **Stabiler Auth-Flow:** Die Authentifizierung ist jetzt stabil. Du kannst dich auf die Implementierung des Login-Prozesses konzentrieren.
|
||||
2. **Client-Konfiguration:** Der `web-app` Client in Keycloak ist für dich vorbereitet. Er ist ein **public client** und nutzt den sicheren **Authorization Code Flow mit PKCE**. Du musst eine OIDC-Client-Bibliothek verwenden, die diesen Flow unterstützt (z.B. `keycloak-js` oder eine moderne Alternative wie `oidc-client-ts`).
|
||||
3. **Benötigte Konfiguration:** Dein Frontend benötigt folgende Informationen, die du am besten in einer Environment-Datei ablegst:
|
||||
* Keycloak URL: `http://localhost:8180`
|
||||
* Realm: `meldestelle`
|
||||
* Client ID: `web-app`
|
||||
4. **Keine Secrets im Frontend:** Der `web-app` Client hat absichtlich **kein** Secret. Versuche niemals, ein Secret im Frontend-Code zu speichern oder zu verwenden. PKCE sorgt hier für die nötige Sicherheit.
|
||||
5. **Token-Handling:** Nach dem Login erhältst du ein **Access Token**. Dieses muss bei jeder API-Anfrage an das Backend (`http://localhost:8081/api/...`) im `Authorization: Bearer <token>` HTTP-Header mitgesendet werden.
|
||||
@@ -0,0 +1,210 @@
|
||||
---
|
||||
type: Runbook
|
||||
status: ACTIVE
|
||||
owner: QA Specialist
|
||||
tags: [postman, testing, api, backend]
|
||||
last_update: 2026-04-03
|
||||
---
|
||||
# Runbook: API-Tests mit Postman (für Nicht‑Programmierer)
|
||||
|
||||
> Ziel: Alle Kern-APIs der Meldestelle lokal prüfen – sicher, reproduzierbar und ohne Programmierkenntnisse.
|
||||
|
||||
Inhalt:
|
||||
1. Voraussetzungen (Docker-Profile starten)
|
||||
2. Postman einrichten (Collection + Environment)
|
||||
3. Login/Token beschaffen
|
||||
4. Smoke‑Tests (System & Health)
|
||||
5. Ping‑Service durchtesten (ohne/mit Login, Resilience)
|
||||
6. Master‑Data & Pferde (Beispiele)
|
||||
7. Empfohlene Test‑Reihenfolge
|
||||
8. Troubleshooting & FAQ
|
||||
|
||||
---
|
||||
|
||||
## 1. Voraussetzungen
|
||||
|
||||
Bitte sicherstellen, dass die lokale Umgebung läuft:
|
||||
|
||||
- Infrastruktur starten: `docker compose --profile infra up -d`
|
||||
- Backend starten: `docker compose --profile backend up -d`
|
||||
|
||||
Standard‑Ports (lokal):
|
||||
- Gateway: `http://localhost:8081` (Einstiegspunkt für alle APIs)
|
||||
- Keycloak: `http://localhost:8180`
|
||||
- Ping‑Service (direkt): `http://localhost:8082`
|
||||
- ZNS‑Import (direkt): `http://localhost:8095`
|
||||
- Consul: `http://localhost:8500`
|
||||
|
||||
Hinweis: Nutze für Postman immer die `gateway_url` (`:8081`), um die Security- und Routing-Logik des Systems zu testen.
|
||||
|
||||
---
|
||||
|
||||
## 2. Postman einrichten
|
||||
|
||||
### 2.1 Collection importieren
|
||||
1. Postman öffnen → „Import“ → Datei wählen:
|
||||
```
|
||||
backend/infrastructure/gateway/src/main/resources/static/docs/postman/Meldestelle_API_Collection.json
|
||||
```
|
||||
2. Die Collection „Meldestelle API Collection“ erscheint links.
|
||||
|
||||
### 2.2 Environment anlegen (z. B. „Meldestelle Local“)
|
||||
Trage folgende Variablen ein:
|
||||
|
||||
| Variable | Wert (Initial) | Beschreibung |
|
||||
|---|---|---|
|
||||
| `gateway_url` | `http://localhost:8081` | API‑Gateway Einstieg |
|
||||
| `auth_url` | `http://localhost:8180/realms/meldestelle/protocol/openid-connect/token` | Token‑Endpoint |
|
||||
| `client_id` | `postman-client` | Keycloak Client |
|
||||
| `client_secret` | `postman-secret-123` | Secret zum Client |
|
||||
| `username` | `admin` | Testuser |
|
||||
| `password` | `Admin#1234` | Passwort |
|
||||
| `ping_url` | `http://localhost:8082` | Direkter Ping‑Service |
|
||||
| `zns_url` | `http://localhost:8095` | Direkter ZNS‑Import |
|
||||
| `consul_url` | `http://localhost:8500` | Consul UI |
|
||||
| `auth_token` | (leer) | Wird nach Login automatisch befüllt |
|
||||
| `horse_id` | (leer) | Wird bei Pferde‑Tests befüllt |
|
||||
| `country_id` | (leer) | Wird bei Länder‑Tests befüllt |
|
||||
|
||||
---
|
||||
|
||||
## 3. Login/Token beschaffen
|
||||
|
||||
In Postman im Reiter „Authorization“ (Request „Login“ in der Collection nutzen oder manuell):
|
||||
- Type: „OAuth 2.0“
|
||||
- Grant Type: „Password Credentials“
|
||||
- Access Token URL: `{{auth_url}}`
|
||||
- Client ID: `{{client_id}}`
|
||||
- Client Secret: `{{client_secret}}`
|
||||
- Username: `{{username}}`
|
||||
- Password: `{{password}}`
|
||||
- Button „Get New Access Token“ → „Use Token“
|
||||
|
||||
Das Token wird im Environment als `auth_token` gespeichert (Pre‑Request Scripts der Collection setzen es automatisch, sofern vorhanden).
|
||||
|
||||
---
|
||||
|
||||
## 4. Smoke‑Tests (immer zuerst)
|
||||
|
||||
Zweck: Schnell prüfen, ob das System erreichbar und „UP“ ist.
|
||||
|
||||
1) API‑Gateway Info
|
||||
- Methode: `GET`
|
||||
- URL: `{{gateway_url}}/actuator/info`
|
||||
- Auth: Keine
|
||||
- Erwartet: `200 OK` (leerer JSON‑Body ist aktuell normal)
|
||||
|
||||
2) API‑Gateway Health
|
||||
- Methode: `GET`
|
||||
- URL: `{{gateway_url}}/actuator/health`
|
||||
- Auth: Keine
|
||||
- Erwartet: `200 OK` mit `{ "status": "UP" }`
|
||||
|
||||
3) OpenAPI (optional)
|
||||
- Methode: `GET`
|
||||
- URL: `{{gateway_url}}/v3/api-docs`
|
||||
- Erwartet: `200 OK` (OpenAPI JSON)
|
||||
|
||||
---
|
||||
|
||||
## 5. Ping‑Service Tests
|
||||
|
||||
### 5.1 Öffentlich (ohne Login)
|
||||
1) Simple Ping
|
||||
- `GET {{gateway_url}}/api/ping/simple`
|
||||
- Erwartet: `200 OK` mit `{"status":"pong", ...}`
|
||||
|
||||
2) Health
|
||||
- `GET {{gateway_url}}/api/ping/health`
|
||||
- Erwartet: `200 OK` mit `{"status":"up", ...}`
|
||||
|
||||
3) Public Ping
|
||||
- `GET {{gateway_url}}/api/ping/public`
|
||||
- Erwartet: `200 OK`
|
||||
|
||||
### 5.2 Sicherheit (Negativ‑Test, ohne Login)
|
||||
1) Secure Ping (unauthenticated)
|
||||
- `GET {{gateway_url}}/api/ping/secure`
|
||||
- Erwartet: `401 Unauthorized`
|
||||
|
||||
2) Sync Ping (unauthenticated)
|
||||
- `GET {{gateway_url}}/api/ping/sync?since=0`
|
||||
- Erwartet: `401 Unauthorized` (Sicherheit erhöht)
|
||||
|
||||
3) Enhanced Ping (unauthenticated)
|
||||
- `GET {{gateway_url}}/api/ping/enhanced`
|
||||
- Erwartet: `401 Unauthorized`
|
||||
|
||||
### 5.3 Authentifiziert (mit Token)
|
||||
1) Secure Ping (authenticated)
|
||||
- `GET {{gateway_url}}/api/ping/secure`
|
||||
- Auth: OAuth 2.0 (Token)
|
||||
- Erwartet: `200 OK` mit `{"status":"secure-pong", ...}`
|
||||
|
||||
2) Sync Ping (authenticated)
|
||||
- `GET {{gateway_url}}/api/ping/sync?lastSyncTimestamp=0`
|
||||
- Erwartet: `200 OK` (Liste von Events)
|
||||
|
||||
### 5.4 Resilience (Circuit Breaker Demo)
|
||||
1) Enhanced Ping (normal)
|
||||
- `GET {{gateway_url}}/api/ping/enhanced` → `200 OK`
|
||||
|
||||
2) Enhanced Ping (Fehler provozieren)
|
||||
- `GET {{gateway_url}}/api/ping/enhanced?simulate=true` mehrfach senden
|
||||
- Erwartet: teils `500`, danach Fallback‑Antwort mit `circuitBreakerState: "OPEN"`
|
||||
|
||||
---
|
||||
|
||||
## 6. Master‑Data & Pferde (Beispiele)
|
||||
|
||||
Hinweis: Endpunkte können sich ändern. Maßgeblich ist die OpenAPI unter `{{gateway_url}}/v3/api-docs` sowie die importierte Postman‑Collection.
|
||||
|
||||
### 6.1 Countries
|
||||
- Create: `POST {{gateway_url}}/api/masterdata/countries` (Body siehe Collection)
|
||||
- Read: `GET {{gateway_url}}/api/masterdata/countries/{{country_id}}`
|
||||
- Erwartet: `201 Created` bei Anlage, `200 OK` beim Lesen.
|
||||
|
||||
### 6.2 Horse Registry
|
||||
- Create: `POST {{gateway_url}}/api/horses` → speichert `{{horse_id}}`
|
||||
- Get: `GET {{gateway_url}}/api/horses/{{horse_id}}`
|
||||
- Delete (optional): `DELETE {{gateway_url}}/api/horses/{{horse_id}}`
|
||||
|
||||
---
|
||||
|
||||
## 7. Empfohlene Test‑Reihenfolge (Checkliste)
|
||||
|
||||
1) Infrastruktur & Backend starten (Docker Profiles)
|
||||
2) Collection importieren, Environment setzen
|
||||
3) Smoke‑Tests (Info, Health, OpenAPI)
|
||||
4) Ping öffentlich (simple, health, public)
|
||||
5) Negativ‑Tests (secure/sync ohne Token → 401)
|
||||
6) Login/Token holen
|
||||
7) Ping authentifiziert (secure/sync)
|
||||
8) Resilience testen (enhanced, simulate=true)
|
||||
9) Optionale Domänen‑Tests (Countries, Horses)
|
||||
|
||||
---
|
||||
|
||||
## 8. Troubleshooting & FAQ
|
||||
|
||||
- 401 Unauthorized trotz Login:
|
||||
- Token wirklich gesetzt? In Postman „Use Token“ klicken; im Request unter „Authorization“ prüfen.
|
||||
- Uhrzeit/Zeitzone korrekt? Abgelaufene Tokens werden abgewiesen.
|
||||
- 502/503 vom Gateway:
|
||||
- Service wirklich „UP“? `{{gateway_url}}/actuator/health` und Container‑Logs prüfen: `docker compose ps` / `docker compose logs -f <service>`
|
||||
- Keycloak nicht erreichbar:
|
||||
- Warten Sie nach Start 30–60 Sekunden. Prüfen: `http://localhost:8180`.
|
||||
- OpenAPI leer oder 404:
|
||||
- Backend neu gestartet? Warten, bis Gateway die Routen geladen hat.
|
||||
|
||||
Weitere Details und Hintergründe finden Sie in der ursprünglichen, technischen Langfassung der QA‑Dokumentation (siehe Archiv‑Hinweis unten).
|
||||
|
||||
---
|
||||
|
||||
Quellen & Verweise:
|
||||
- Postman‑Collection: `backend/infrastructure/gateway/src/main/resources/static/docs/postman/Meldestelle_API_Collection.json`
|
||||
- Backend‑Referenz: `docs/05_Backend/Services/PingService_Reference.md`
|
||||
- OpenAPI: `{{gateway_url}}/v3/api-docs`
|
||||
|
||||
Archiv‑Hinweis:
|
||||
- Die frühere, sehr detaillierte Arbeitsversion „Postman Tests — Vollständige Dokumentation“ aus dem Agents‑Besprechungsordner wurde in dieses Runbook konsolidiert und ins Archiv verschoben.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
type: Index
|
||||
status: ACTIVE
|
||||
owner: Curator
|
||||
last_update: 2026-04-03
|
||||
---
|
||||
# Runbooks & Betriebsanleitungen
|
||||
|
||||
Dieser Ordner ist der zentrale Ablageort für alle Betriebsanleitungen (Runbooks) und Schritt-für-Schritt‑Guides, die auch von Nicht‑Programmierern genutzt werden können.
|
||||
|
||||
- Zielgruppe: Operations, Tester, Fachexperten, Auditoren
|
||||
- Prinzip: Docs‑as‑Code, offline‑fähig, reproduzierbar
|
||||
|
||||
Struktur:
|
||||
- Postman & API‑Tests: `POSTMAN_API_Tests_Runbook.md`
|
||||
- Zora/Infra Setup: `zora-setup-runbook.md`
|
||||
- Weitere Runbooks: jeweils als eigenständige Markdown‑Datei in diesem Verzeichnis
|
||||
|
||||
Archivierung:
|
||||
- Veraltete oder ersetzte Dokumente werden nach `docs/99_Journal/_archive/` oder in das jeweilige Themen‑`_archive/` verschoben und hier nicht mehr geführt.
|
||||
@@ -0,0 +1,104 @@
|
||||
# 🐎 ZNS-Importer Test-Anleitung (Runbook)
|
||||
|
||||
Diese Anleitung beschreibt den Prozess, um den ZNS-Importer in einer lokalen Entwicklungsumgebung zu starten und mit Postman vollständig zu testen.
|
||||
|
||||
## 1. Infrastruktur starten (Docker)
|
||||
|
||||
Bevor der Service gestartet werden kann, müssen die Basis-Dienste (Datenbank, Discovery, Auth) laufen.
|
||||
|
||||
### 1.1 Docker Container starten
|
||||
Öffne ein Terminal im Projekt-Root (`/mocode/Meldestelle`) und führe folgenden Befehl aus:
|
||||
|
||||
```bash
|
||||
docker compose up -d postgres consul keycloak valkey zipkin
|
||||
```
|
||||
|
||||
### 1.2 Status prüfen
|
||||
Stelle sicher, dass alle Container "healthy" sind:
|
||||
* **PostgreSQL:** `localhost:5432`
|
||||
* **Consul UI:** [http://localhost:8500](http://localhost:8500) (Hier muss der Status aller Dienste grün sein)
|
||||
|
||||
---
|
||||
|
||||
## 2. Backend Services starten
|
||||
|
||||
Der ZNS-Importer benötigt den `masterdata-service` (für die Datenbank-Tabellen) und den `zns-import-service`.
|
||||
|
||||
### 2.1 Masterdata Service (DB-Migrationen)
|
||||
Starte den Masterdata-Service, damit die Tabellen (Verein, Reiter, Pferd, Funktionär) angelegt werden:
|
||||
```bash
|
||||
./gradlew :backend:services:masterdata:masterdata-service:bootRun
|
||||
```
|
||||
*Warte bis im Log erscheint: `Started MasterdataServiceApplication`*
|
||||
|
||||
### 2.2 ZNS-Import Service
|
||||
Starte den Import-Service in einem neuen Terminal:
|
||||
```bash
|
||||
./gradlew :backend:services:zns-import:zns-import-service:bootRun
|
||||
```
|
||||
*Warte bis im Log erscheint: `Started ZnsImportServiceApplication`*
|
||||
|
||||
---
|
||||
|
||||
## 3. Postman Test-Ablauf
|
||||
|
||||
### 3.1 Health-Check (Optionaler Smoke-Test)
|
||||
Prüfe ob der Service erreichbar ist:
|
||||
* **Methode:** `GET`
|
||||
* **URL:** `http://localhost:8095/actuator/health`
|
||||
* **Erwartetes Ergebnis:** `{"status":"UP"}`
|
||||
|
||||
### 3.2 ZNS-ZIP Upload (Import starten)
|
||||
Dieser Schritt lädt die ZNS-Daten (ZIP-Datei mit .DAT Files) hoch und startet den asynchronen Prozess.
|
||||
|
||||
* **Methode:** `POST`
|
||||
* **URL:** `http://localhost:8095/api/v1/import/zns`
|
||||
* **Body:** `form-data`
|
||||
* Key: `file`
|
||||
* Type: `File`
|
||||
* Value: Wähle deine `ZNS_EXPORT.zip` aus.
|
||||
* **Erwartete Antwort (202 Accepted):**
|
||||
```json
|
||||
{
|
||||
"jobId": "7d3a...-..."
|
||||
}
|
||||
```
|
||||
*(Kopiere die `jobId` für den nächsten Schritt!)*
|
||||
|
||||
### 3.3 Status Polling
|
||||
Da der Import im Hintergrund läuft, musst du den Status abfragen:
|
||||
|
||||
* **Methode:** `GET`
|
||||
* **URL:** `http://localhost:8095/api/v1/import/zns/{jobId}/status`
|
||||
* **Erwartete Antwort (währenddessen):** `status: "VERARBEITUNG"`
|
||||
* **Erwartete Antwort (Erfolg):**
|
||||
```json
|
||||
{
|
||||
"jobId": "...",
|
||||
"status": "ABGESCHLOSSEN",
|
||||
"fortschritt": 100,
|
||||
"meldungen": ["Import erfolgreich: 4 Dateien verarbeitet (150 Reiter, 200 Pferde, ...)"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Erfolgskontrolle (Nach dem Import)
|
||||
|
||||
### 4.1 Datenbank prüfen (pgAdmin / SQL)
|
||||
Prüfe in der Tabelle `reiter` oder `horse`, ob Daten vorhanden sind:
|
||||
```sql
|
||||
SELECT count(*) FROM reiter;
|
||||
SELECT * FROM horse LIMIT 10;
|
||||
```
|
||||
|
||||
### 4.2 Archiv-Ordner
|
||||
Prüfe ob die Datei erfolgreich archiviert wurde:
|
||||
* Pfad (laut `application.yaml`): `/data/zns/archive` (oder der konfigurierte Pfad)
|
||||
* Die Datei sollte `zns_import_YYYYMMDD_HHMMSS.zip` heißen.
|
||||
|
||||
## 5. Troubleshooting
|
||||
|
||||
* **404 Not Found:** Prüfe ob der Service auf Port 8095 läuft.
|
||||
* **500 Internal Server Error:** Prüfe die Konsolenausgabe des `zns-import-service` auf Stacktraces.
|
||||
* **Import bleibt bei 0% hängen:** Prüfe ob die ZIP-Datei die richtigen Dateinamen enthält (z.B. `VEREIN01.DAT`, `LIZENZ01.DAT`).
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
type: Guide
|
||||
status: ACTIVE
|
||||
owner: DevOps Engineer
|
||||
last_update: 2026-03-15
|
||||
---
|
||||
# Runbook: Lokale Entwicklungsumgebung
|
||||
|
||||
Dieses Dokument beschreibt, wie die Docker-basierte lokale Entwicklungsumgebung für das Projekt "Meldestelle" verwendet wird.
|
||||
|
||||
## 1. Voraussetzungen
|
||||
|
||||
- Docker und Docker Compose müssen installiert sein.
|
||||
- Eine `.env`-Datei muss im Projekt-Root vorhanden sein. Eine Vorlage befindet sich in `.env.example`.
|
||||
|
||||
## 2. Starten der Umgebung
|
||||
|
||||
Die Docker-Compose-Konfiguration verwendet [Profile](https://docs.docker.com/compose/profiles/), um das Starten von verschiedenen Teilen des Systems zu ermöglichen.
|
||||
|
||||
### 2.1. Vollständiger Start (All-in-One)
|
||||
|
||||
Dieser Befehl startet alle definierten Services, einschließlich der Kerninfrastruktur, Backend-Services, Frontend und Monitoring-Tools.
|
||||
|
||||
```bash
|
||||
docker-compose --profile all up --build -d
|
||||
```
|
||||
|
||||
- `--build`: Baut die Images vor dem Start neu, falls Änderungen in den Dockerfiles oder im Quellcode vorliegen.
|
||||
- `-d`: Startet die Container im "detached" Modus (im Hintergrund).
|
||||
|
||||
### 2.2. Nur Backend-Entwicklung
|
||||
|
||||
Wenn Sie nur an den Backend-Services arbeiten, können Sie die GUI- und Monitoring-Komponenten weglassen, um Ressourcen zu sparen.
|
||||
|
||||
```bash
|
||||
docker-compose --profile backend --profile infra up --build -d
|
||||
```
|
||||
|
||||
Dieser Befehl startet:
|
||||
- Die Kerninfrastruktur (`infra`): `postgres`, `redis`, `keycloak`, `consul`.
|
||||
- Alle Backend-Services (`backend`): `api-gateway`, `ping-service`, etc.
|
||||
|
||||
### 2.3. Nur Frontend-Entwicklung
|
||||
|
||||
Wenn Sie primär am Frontend arbeiten, benötigen Sie nur das API-Gateway und die Kerninfrastruktur.
|
||||
|
||||
```bash
|
||||
docker-compose --profile gui --profile infra up --build -d
|
||||
```
|
||||
|
||||
Dieser Befehl startet:
|
||||
- Die Kerninfrastruktur (`infra`).
|
||||
- Das `api-gateway`.
|
||||
- Die `web-app` (Nginx-Server für das Frontend).
|
||||
|
||||
## 3. Stoppen der Umgebung
|
||||
|
||||
Um alle laufenden Container des Projekts zu stoppen:
|
||||
|
||||
```bash
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
Um die Container zu stoppen und auch die benannten Volumes (Datenbankinhalte, etc.) zu löschen:
|
||||
|
||||
```bash
|
||||
docker-compose down -v
|
||||
```
|
||||
|
||||
## 4. Debugging eines Services
|
||||
|
||||
Alle Backend-Services sind so konfiguriert, dass sie im Debug-Modus gestartet werden können, wenn die Umgebungsvariable `DEBUG=true` gesetzt ist.
|
||||
|
||||
**Beispiel: `ping-service` debuggen**
|
||||
|
||||
1. **Setzen der Umgebungsvariable:**
|
||||
Öffnen Sie die `docker-compose.yaml` und finden Sie den `ping-service`. Fügen Sie unter `environment` die folgende Zeile hinzu oder passen Sie sie an:
|
||||
```yaml
|
||||
environment:
|
||||
# ... andere Variablen
|
||||
DEBUG: "true"
|
||||
```
|
||||
*Hinweis: In der `docker-compose.yaml` ist dies bereits über `${PING_DEBUG:-true}` vorbereitet. Sie können also auch Ihre `.env`-Datei anpassen.*
|
||||
|
||||
2. **Port-Mapping:**
|
||||
Der `ping-service` mappt den Debug-Port `5006` auf den Host.
|
||||
|
||||
3. **Starten und Verbinden:**
|
||||
Starten Sie die Umgebung neu (`docker-compose up -d --build ping-service`). Sie können nun einen Remote-Debugger Ihrer IDE mit dem Host `localhost` und dem Port `5006` verbinden.
|
||||
|
||||
## 5. Nützliche Endpunkte und Tools
|
||||
|
||||
Wenn die Umgebung mit dem `all`-Profil läuft, sind die folgenden Tools verfügbar:
|
||||
|
||||
- **Keycloak Admin Console:** [http://localhost:8180](http://localhost:8180)
|
||||
- Login: `kc-admin` / `kc-password` (Standard, siehe `.env`)
|
||||
- **pgAdmin (Datenbank-Tool):** [http://localhost:8888](http://localhost:8888)
|
||||
- Login: `meldestelle@mo-code.at` / `pgadmin` (Standard, siehe `.env`)
|
||||
- **Grafana (Monitoring):** [http://localhost:3000](http://localhost:3000)
|
||||
- Login: `gf-admin` / `gf-password` (Standard, siehe `.env`)
|
||||
- **Consul UI (Service Discovery):** [http://localhost:8500](http://localhost:8500)
|
||||
- **API Gateway:** [http://localhost:8081](http://localhost:8081)
|
||||
- **Web App:** [http://localhost:4000](http://localhost:4000)
|
||||
- **Mailpit (E-Mail Testing):** [http://localhost:8025](http://localhost:8025)
|
||||
- Fängt alle E-Mails ab, die von Keycloak oder den Services gesendet werden.
|
||||
|
||||
## 6. Fortgeschrittene Anwendungsfälle: Override-Dateien
|
||||
|
||||
Im Projekt existieren leere Dateien wie `docker-compose.frontend.yaml` und `docker-compose.services.yaml`. Diese sind absichtlich leer und sollen **nicht** versioniert werden. Sie dienen als persönliche Override-Dateien für lokale Entwicklungsszenarien.
|
||||
|
||||
**Zweck:**
|
||||
Anstatt die zentrale `docker-compose.yaml` zu verändern, können Sie lokale Anpassungen in diesen Dateien vornehmen.
|
||||
|
||||
**Beispiel: Frontend-Entwicklung mit Hot-Reload**
|
||||
|
||||
Um Hot-Reload für die Nginx-Konfiguration zu aktivieren, können Sie die `docker-compose.frontend.yaml` mit folgendem Inhalt füllen:
|
||||
|
||||
```yaml
|
||||
# docker-compose.frontend.yaml
|
||||
services:
|
||||
web-app:
|
||||
volumes:
|
||||
- ./config/docker/nginx/web-app/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
```
|
||||
|
||||
Starten Sie die Umgebung dann mit beiden Dateien:
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.yaml -f docker-compose.frontend.yaml up
|
||||
```
|
||||
|
||||
Docker Compose wird die Konfigurationen zusammenführen, wobei die Definitionen in der `docker-compose.frontend.yaml` Vorrang haben.
|
||||
@@ -0,0 +1,454 @@
|
||||
---
|
||||
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 <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:
|
||||
```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=<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:
|
||||
|
||||
```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 <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
|
||||
|
||||
```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).
|
||||
Reference in New Issue
Block a user