136 lines
4.9 KiB
Markdown
136 lines
4.9 KiB
Markdown
|
|
# 0004 — Docker Compose Deploy mit DB-/Reports-Volume und SN-XML-Sonderpfad
|
||
|
|
|
||
|
|
| | |
|
||
|
|
|---|---|
|
||
|
|
| **Status** | accepted |
|
||
|
|
| **Datum** | 2026-04-10 |
|
||
|
|
| **Refs** | CLAUDE.md "Deployment", `docker-compose.yml`, Issue #5, project_sn_xml_export |
|
||
|
|
|
||
|
|
## Kontext
|
||
|
|
|
||
|
|
Der GWÖ-Antragsprüfer läuft als Docker-Container `gwoe-antragspruefer` auf
|
||
|
|
einem VServer hinter Traefik (Let's Encrypt SSL, Domain
|
||
|
|
`gwoe.toppyr.de`). Code wird via Git aus `repo.toppyr.de` gezogen.
|
||
|
|
|
||
|
|
Drei Subsysteme haben unterschiedliche Lebenszyklen:
|
||
|
|
|
||
|
|
1. **Code** (`app/`) — wird bei jedem Deploy neu kopiert.
|
||
|
|
2. **SQLite-Daten** (`data/gwoe-antraege.db`, `data/embeddings.db`) —
|
||
|
|
Source of Truth, MUSS persistent über Deploys hinweg.
|
||
|
|
3. **PDF-Reports** (`reports/*.pdf`) — sind generierte Artefakte, könnten
|
||
|
|
theoretisch regeneriert werden, sind aber teuer (LLM-Calls + WeasyPrint),
|
||
|
|
also auch persistent.
|
||
|
|
|
||
|
|
Issue #5 hatte einen frühen Schmerzpunkt: der Container-Build hat die
|
||
|
|
DB überschrieben, weil `data/` nicht aus dem Build-Context exkludiert war.
|
||
|
|
|
||
|
|
## Optionen
|
||
|
|
|
||
|
|
### Option A — Alles im Image, manuelle Backups
|
||
|
|
|
||
|
|
Code, DB, Reports zusammen im Image. Backups via `docker cp` vor jedem
|
||
|
|
Deploy.
|
||
|
|
|
||
|
|
**Nachteile:** Image wird gigantisch, jeder Deploy ist ein Risk-Event,
|
||
|
|
kein automatischer Persistenz-Mechanismus.
|
||
|
|
|
||
|
|
### Option B — Docker-Volumes für DB und Reports
|
||
|
|
|
||
|
|
`docker-compose.yml` mountet `./data` und `./reports` vom Host als Volumes.
|
||
|
|
Build kopiert nur `app/` und `requirements.txt`.
|
||
|
|
|
||
|
|
**Vorteile:** Host-Volumes überleben Container-Restart und Image-Rebuild.
|
||
|
|
Backups via Standard-Linux-Tools auf dem Host.
|
||
|
|
|
||
|
|
**Nachteile:** Build-Context muss `data/`, `reports/`, `.env` exkludieren
|
||
|
|
(siehe `.dockerignore`), sonst werden sie versehentlich ins Image kopiert
|
||
|
|
und überschreiben das gemountete Volume bei Container-Start.
|
||
|
|
|
||
|
|
### Option C — Externe DB (Postgres)
|
||
|
|
|
||
|
|
Postgres als separater Container, gwoe-antragspruefer verbindet per asyncpg.
|
||
|
|
|
||
|
|
**Vorteile:** standard, robust, Backups via pg_dump.
|
||
|
|
|
||
|
|
**Nachteile:** Migration aller existierenden Queries, neue Abhängigkeit,
|
||
|
|
mehr Operational Surface. SQLite reicht für die aktuelle Last (~30 Anträge,
|
||
|
|
selten parallele Writes).
|
||
|
|
|
||
|
|
## Entscheidung
|
||
|
|
|
||
|
|
**Option B**. Docker-Compose mit Host-Volumes für `data/` und `reports/`.
|
||
|
|
`.dockerignore` exkludiert beide aus dem Build-Context.
|
||
|
|
|
||
|
|
### Standard-Deploy
|
||
|
|
|
||
|
|
```bash
|
||
|
|
ssh vserver 'cd /opt/gwoe-antragspruefer && git pull && docker compose up -d --build'
|
||
|
|
```
|
||
|
|
|
||
|
|
### Manueller Tar-Upload (falls Git-Workflow blockiert ist)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd webapp
|
||
|
|
tar czf /tmp/gwoe-webapp.tar.gz \
|
||
|
|
--exclude='venv' --exclude='__pycache__' \
|
||
|
|
--exclude='data' --exclude='reports' --exclude='.env' .
|
||
|
|
scp /tmp/gwoe-webapp.tar.gz vserver:/tmp/
|
||
|
|
ssh vserver 'cd /opt/gwoe-antragspruefer && tar xzf /tmp/gwoe-webapp.tar.gz && docker compose up -d --build'
|
||
|
|
```
|
||
|
|
|
||
|
|
Beachte: `--exclude='data'` ist **zwingend**, sonst überschreibt der Tar
|
||
|
|
die Live-DB.
|
||
|
|
|
||
|
|
### SN-XML-Sonderpfad
|
||
|
|
|
||
|
|
Sachsen hat keinen scrape-baren Endpoint und liest stattdessen wöchentlich
|
||
|
|
manuell exportierte XML-Dumps aus EDAS. Workflow:
|
||
|
|
|
||
|
|
1. User exportiert XML aus EDAS (manuell, im Browser).
|
||
|
|
2. `cp dokumente_export.xml gwoe-antragspruefer:/app/data/sn-edas/`
|
||
|
|
3. `scp` aus dem Container ins Host-Volume — kein Container-Restart nötig.
|
||
|
|
4. SNEdasXmlAdapter liest die XML beim nächsten Search-Call.
|
||
|
|
|
||
|
|
Details in `~/.claude/projects/<projekt>/memory/project_sn_xml_export.md`.
|
||
|
|
|
||
|
|
### Container-Zeitzone
|
||
|
|
|
||
|
|
Der Container läuft **UTC**, nicht CEST. DB-Timestamps in `assessments.created_at`
|
||
|
|
sind UTC (kein TZ-Suffix, aber UTC). Beim Korrelieren mit Commit-Zeiten
|
||
|
|
(lokal CEST = UTC+2) muss konvertiert werden, sonst fließen falsche
|
||
|
|
Schlussfolgerungen ein. Detailliert in
|
||
|
|
`~/.claude/projects/<projekt>/memory/reference_container_utc.md`.
|
||
|
|
|
||
|
|
## Konsequenzen
|
||
|
|
|
||
|
|
### Positiv
|
||
|
|
|
||
|
|
- **DB überlebt jeden Deploy** — verifiziert seit Issue #5 (Fix in
|
||
|
|
`.dockerignore`-Update).
|
||
|
|
- **Backups sind trivial**: `tar czf gwoe-data-$(date +%F).tar.gz data/`
|
||
|
|
auf dem Host.
|
||
|
|
- **Build ist schnell** (~30s für nicht-cached Layers), weil das Image
|
||
|
|
nur Code + Dependencies enthält.
|
||
|
|
|
||
|
|
### Negativ
|
||
|
|
|
||
|
|
- **`.dockerignore` ist ein Foot-Gun** — wenn jemand vergisst, `data/` neu
|
||
|
|
hinzuzufügen nach einem Refactor, kann es passieren dass der Build die
|
||
|
|
Live-DB überschreibt. Mitigation: ein dedicated Sub-D-style Test, der
|
||
|
|
nach dem Build prüft, dass die DB die erwarteten Tabellen hat — steht
|
||
|
|
noch aus.
|
||
|
|
- **SN-XML braucht manuelle Pflege** wöchentlich. Akzeptiert weil es kein
|
||
|
|
scrape-baren Endpoint gibt.
|
||
|
|
- **SQLite skaliert nicht über parallele Writes** — bei mehr als ~5
|
||
|
|
gleichzeitigen Analyses würde es Lock-Contention geben. Aktuell läuft
|
||
|
|
alles seriell durch den Background-Task-Mechanismus, also kein Problem.
|
||
|
|
Bei Wachstum auf >50 Analyses/Tag wäre ein Postgres-Migration ein
|
||
|
|
separater ADR.
|
||
|
|
|
||
|
|
### Folgen für andere ADRs
|
||
|
|
|
||
|
|
- ADR 0002 (Adapter-Architektur) ist davon unabhängig — Adapter sind reine
|
||
|
|
Code-Klassen ohne State.
|
||
|
|
- Ein zukünftiger Postgres-Migration-ADR würde diesen ADR partial superseden
|
||
|
|
(DB-Persistenz, nicht Reports).
|