Files
GerbilManager/docs/ops.md
Gulum 2198c33898 OPS-2: TrueNAS Goldeye 25.10.2.1 Deploy finalisiert
Registry truenas:13000 -> git.rismer.de/gulum (alle 6 Stellen).
Dataset-Pfade auf /mnt/JailStorage/DockerVolumes/gerbilmanager/ gesetzt.
docs/ops.md: vollstaendiges Goldeye-Runbook (Ordner + UID-999-Perms,
Registry-Login, Port-80-Fallback, Restore-Drill-Pflichtschritt, ZFS-Pfade).
docker compose config: OK.
2026-06-07 01:38:25 +02:00

417 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GerbilManager — Betriebsanleitung (TrueNAS SCALE Goldeye)
> Zielgruppe: Julian (Systemadministration) und Ehefrau (tägliche Nutzung).
> Bookmark für die Ehefrau: **http://\<NAS-IP\>/** (z. B. http://truenas/)
---
## Inhaltsverzeichnis
1. [Übersicht & Architektur](#1-übersicht--architektur)
2. [Voraussetzungen](#2-voraussetzungen)
3. [Erstinstallation auf TrueNAS Goldeye](#3-erstinstallation-auf-truenas-goldeye)
4. [App starten / stoppen / aktualisieren](#4-app-starten--stoppen--aktualisieren)
5. [Backup & Wiederherstellung](#5-backup--wiederherstellung)
6. [ZFS-Snapshot-Schichtung](#6-zfs-snapshot-schichtung)
7. [CI/CD via Gitea Actions](#7-cicd-via-gitea-actions)
8. [Fehlerbehebung](#8-fehlerbehebung)
---
## 1. Übersicht & Architektur
```
Browser / Handy
| HTTP :80 (oder PORT aus .env, z.B. 8080)
v
┌──────────────────┐
│ frontend (nginx) │ statisches React-SPA + Reverse-Proxy
└──────┬───────────┘
│ /api/* → http://api:8080/*
│ /scalar → http://api:8080/scalar
v
┌──────────────────┐
│ api (.NET 10) │ GerbilManagerWebAPI, Minimal API
└──────┬───────────┘
│ ConnectionStrings__gerbilmanager
v
┌──────────────────┐ ┌──────────────────────┐
│ db (Postgres 17)│ │ backup (Sidecar) │
└──────────────────┘ │ pg_dump + tar + cron │
│ └──────────────────────┘
└─ pgdata-Volume → /mnt/JailStorage/DockerVolumes/gerbilmanager/pgdata
photos-Volume → /mnt/JailStorage/DockerVolumes/gerbilmanager/photos
backups-Volume → /mnt/JailStorage/DockerVolumes/gerbilmanager/backups
keys-Volume → /mnt/JailStorage/DockerVolumes/gerbilmanager/keys
```
**Einziger veröffentlichter Port:** `80` (konfigurierbar via `PORT` in `.env`).
Alles andere läuft intern im Docker-Netz.
---
## 2. Voraussetzungen
| Was | Details |
|-----|---------|
| TrueNAS SCALE | **25.10.2.1 „Goldeye"** (native Docker Custom Apps) |
| Container Registry | `git.rismer.de/gulum` (externes HTTPS) |
| Docker | bereits auf TrueNAS Goldeye vorhanden |
| Verzeichnisse | 4 Ordner unter `/mnt/JailStorage/DockerVolumes/gerbilmanager/` anlegen (Schritt 3.1) |
---
## 3. Erstinstallation auf TrueNAS Goldeye
### 3.1 Verzeichnisse anlegen und Berechtigungen setzen
Öffne eine Shell auf der NAS (TrueNAS → System → Shell oder SSH):
```bash
# Vier Ordner anlegen
mkdir -p /mnt/JailStorage/DockerVolumes/gerbilmanager/pgdata
mkdir -p /mnt/JailStorage/DockerVolumes/gerbilmanager/photos
mkdir -p /mnt/JailStorage/DockerVolumes/gerbilmanager/backups
mkdir -p /mnt/JailStorage/DockerVolumes/gerbilmanager/keys
# Postgres-Container läuft als UID 999 (postgres) / GID 999 intern.
# pgdata muss von UID 999 beschreibbar sein; postgres erzwingt chmod 0700.
chown -R 999:999 /mnt/JailStorage/DockerVolumes/gerbilmanager/pgdata
chmod 700 /mnt/JailStorage/DockerVolumes/gerbilmanager/pgdata
# photos, backups und keys werden von der API bzw. dem Sidecar beschrieben
# (laufen als root im Container) — keine weiteren ACL-Anpassungen nötig.
```
> **TrueNAS Dataset-ACL-Hinweis:** Falls `JailStorage` ein ZFS-Dataset mit NFSv4-ACLs ist,
> und `chown` meldet „Operation not permitted": setze in TrueNAS → Datasets →
> `JailStorage` → Berechtigungen → **ACL-Typ: POSIX** (oder nutze das UI-Formular
> „Eigentümer: 999, Gruppe: 999" für das `pgdata`-Unterverzeichnis).
### 3.2 Registry-Login auf der NAS
```bash
docker login git.rismer.de
# Benutzername und Token/Passwort eingeben (Gitea-Account oder Access Token mit read:packages)
```
Der Login wird unter `/root/.docker/config.json` gespeichert und bleibt nach Reboots erhalten.
### 3.3 Repository klonen
```bash
git clone https://git.rismer.de/gulum/GerbilManager.git /opt/gerbilmanager
cd /opt/gerbilmanager
```
### 3.4 Konfiguration anlegen
```bash
cp deploy/truenas/.env.example deploy/truenas/.env
nano deploy/truenas/.env
```
Mindestens setzen:
| Variable | Wert |
|----------|------|
| `POSTGRES_PASSWORD` | Sicheres Passwort (mind. 20 Zeichen, keine `"`) |
| `AI__BaseUrl` | Gemini: `https://generativelanguage.googleapis.com/v1beta/openai` |
| `AI__ApiKey` | Dein Gemini API-Key |
| `AI__Model` | `gemini-2.0-flash` (oder `gemini-flash-latest`) |
| `PORT` | `80` — falls Port 80 auf der NAS bereits belegt ist: **auf `8080` ändern** |
Die Pfad-Variablen (`PGDATA_PATH`, `PHOTOS_PATH`, etc.) sind bereits auf die Goldeye-Standardpfade
vorbelegt und müssen nur geändert werden, wenn du einen anderen Pool nutzt.
### 3.5 Images ziehen und App starten
```bash
cd /opt/gerbilmanager
docker compose -f deploy/truenas/compose.yaml pull
docker compose -f deploy/truenas/compose.yaml up -d
```
Erster Start dauert ca. 23 Minuten (Postgres-Init + EF-Migrationen).
> **TrueNAS Goldeye Custom App (Alternative):**
> Statt der Shell kann die App auch über TrueNAS → Apps → „Custom App installieren" →
> „Install via YAML" deployt werden: compose-Inhalt einfügen, Volumes als Host-Pfade
> konfigurieren. Die Shell-Methode ist einfacher und gibt mehr Kontrolle.
### 3.6 Verifikation
```bash
# Alle 4 Container laufen?
docker compose -f deploy/truenas/compose.yaml ps
# API-Healthcheck (erwartet: {"status":"Healthy"})
curl -s http://localhost/api/health
# Tier-Gesamtanzahl prüfen (erwartet > 0 nach Import)
curl -s "http://localhost/api/gerbils?pageSize=1" | grep -o '"totalCount":[0-9]*'
# API-Doku (Scalar) im Browser
http://<NAS-IP>/scalar
# Foto-Upload: in der Webapp ein Tier öffnen → Foto hochladen → Foto erscheint
```
---
## 4. App starten / stoppen / aktualisieren
### Starten
```bash
cd /opt/gerbilmanager
docker compose -f deploy/truenas/compose.yaml up -d
```
### Stoppen
```bash
docker compose -f deploy/truenas/compose.yaml down
```
### Aktualisieren (nach CI-Push auf main)
```bash
cd /opt/gerbilmanager
git pull
docker compose -f deploy/truenas/compose.yaml pull
docker compose -f deploy/truenas/compose.yaml up -d
```
> EF-Migrationen laufen automatisch beim API-Start — kein manueller Schritt nötig.
---
## 5. Backup & Wiederherstellung
### Automatisches Backup
Der `backup`-Sidecar-Container läuft dauerhaft und sichert täglich um **03:00 Uhr**:
- Kompletter `pg_dump` der Datenbank als `.sql`
- Komprimiertes Foto-Archiv als `.tar.gz`
- Rotation: Backups älter als `BACKUP_KEEP_DAYS` (Standard: 7) werden gelöscht
Backups liegen unter: `/mnt/JailStorage/DockerVolumes/gerbilmanager/backups/YYYY-MM-DD_HH-MM/`
```
/mnt/JailStorage/DockerVolumes/gerbilmanager/backups/
2026-06-06_03-00/
gerbilmanager_2026-06-06_03-00.sql (Datenbank-Dump, Klartext SQL)
photos_2026-06-06_03-00.tar.gz (Fotos)
backup.log (Protokoll)
```
### Manuelles Backup auslösen
```bash
docker compose -f deploy/truenas/compose.yaml exec backup /bin/sh /scripts/backup.sh
```
### Backup-Log prüfen
```bash
tail -50 /mnt/JailStorage/DockerVolumes/gerbilmanager/backups/backup.log
```
Backup-Validierung: Das Skript prüft ob der Dump `CREATE TABLE` enthält — fehlt dieser
Marker, erscheint eine WARNUNG im Log. Größe 0 KB bedeutet Fehlschlag.
### Wiederherstellung — Runbook
> **WARNUNG:** Alle aktuellen Datenbankdaten und Fotos werden überschrieben!
**Schritt 1:** API und Frontend stoppen (DB und backup-Sidecar laufen weiter)
```bash
docker compose -f deploy/truenas/compose.yaml stop api frontend
```
**Schritt 2:** Restore ausführen
```bash
# Neuestes Backup automatisch wählen und bestätigen:
docker compose -f deploy/truenas/compose.yaml exec -T backup \
/bin/sh /scripts/restore.sh latest -f
# Bestimmtes Backup (Datum aus Verzeichnisname):
docker compose -f deploy/truenas/compose.yaml exec -T backup \
/bin/sh /scripts/restore.sh 2026-06-06_03-00 -f
```
Das Skript:
1. Trennt alle offenen DB-Verbindungen
2. Spielt den SQL-Dump mit `psql -h db -U postgres -d gerbilmanager < dump.sql` ein
3. Entpackt das Foto-Archiv nach `/data/photos`
**Schritt 3:** API neu starten
```bash
docker compose -f deploy/truenas/compose.yaml start api frontend
```
**Schritt 4 — Verifikation (Pflicht nach erstem Restore-Drill):**
```bash
# Tier-Anzahl prüfen
curl -s "http://localhost/api/gerbils?pageSize=1" | grep -o '"totalCount":[0-9]*'
# ColorVariety-Anzahl (Stammdaten, erwartet: >= 60)
curl -s http://localhost/api/color-varieties | python3 -c "import sys,json; print(len(json.load(sys.stdin)))"
# Foto stichprobenartig prüfen
ls /mnt/JailStorage/DockerVolumes/gerbilmanager/photos/ | head -5
```
### Restore-Nachweis (Round-Trip-Test, lokal 2026-06-06)
Protokoll vom getesteten Restore auf lokalem Aspire-Postgres:
```
73 ColorVarieties vorhanden
→ DELETE 12 Zeilen → 61 verbleibend
→ psql < dump.sql eingespielt
→ 73 ColorVarieties bestätigt
Exit-Code: 0
```
**Erster TrueNAS-Restore-Drill:** nach Erstinstallation bitte ausführen und Tier-Anzahl
notieren — beweist dass Backup + Restore auf dem NAS korrekt funktionieren.
---
## 6. ZFS-Snapshot-Schichtung
ZFS-Snapshots ergänzen die pg_dump-Backups als zweite Sicherungsebene.
Sie schützen vor versehentlichem Datenverlust auf Dataset-Ebene.
### Empfohlene Snapshot-Konfiguration
In TrueNAS → **Datasets**`JailStorage/DockerVolumes/gerbilmanager`**Snapshots****Regelmäßige Snapshots**:
| Unterordner | Häufigkeit | Aufbewahrung |
|-------------|-----------|--------------|
| `.../photos` | Stündlich | 24 Stunden |
| `.../photos` | Täglich | 30 Tage |
| `.../pgdata` | Stündlich | 24 Stunden |
| `.../pgdata` | Täglich | 30 Tage |
| `.../backups` | Täglich | 90 Tage |
> **Hinweis:** `pgdata` enthält Live-Postgres-Dateien. ZFS-Snapshots davon sind crash-konsistent,
> aber **nicht** application-konsistent — für einen sauberen DB-Restore immer den `pg_dump` verwenden,
> nicht den ZFS-Snapshot von `pgdata`.
### Snapshot manuell erstellen (z. B. vor Update)
```bash
# Pool-/Dataset-Name anpassen falls nötig
zfs snapshot JailStorage/DockerVolumes/gerbilmanager/photos@vor-update-$(date +%Y%m%d)
zfs snapshot JailStorage/DockerVolumes/gerbilmanager/backups@vor-update-$(date +%Y%m%d)
```
### Aus ZFS-Snapshot wiederherstellen (Fotos)
```bash
# Snapshots auflisten
zfs list -t snapshot JailStorage/DockerVolumes/gerbilmanager/photos
# Einzelne Datei aus Snapshot kopieren
cp /mnt/JailStorage/DockerVolumes/gerbilmanager/photos/.zfs/snapshot/<NAME>/datei.jpg \
/mnt/JailStorage/DockerVolumes/gerbilmanager/photos/
```
---
## 7. CI/CD via Gitea Actions
CI pusht Images nach Erfolg zu `git.rismer.de/gulum/gerbilmanager-api` und
`git.rismer.de/gulum/gerbilmanager-frontend`.
### Registry-Secrets in Gitea
Gitea → Repository → Einstellungen → Secrets:
| Secret | Wert |
|--------|------|
| `REGISTRY_USER` | Gitea-Benutzername |
| `REGISTRY_TOKEN` | Gitea Access Token mit `package:write` |
### Update nach CI-Push
```bash
# Auf der NAS nach erfolgreichem CI-Lauf:
cd /opt/gerbilmanager
git pull
docker compose -f deploy/truenas/compose.yaml pull
docker compose -f deploy/truenas/compose.yaml up -d
```
---
## 8. Fehlerbehebung
### App startet nicht
```bash
docker compose -f deploy/truenas/compose.yaml logs
docker compose -f deploy/truenas/compose.yaml logs api
docker compose -f deploy/truenas/compose.yaml logs db
```
### Port 80 belegt
Falls Port 80 vom TrueNAS-System selbst genutzt wird:
```bash
# In deploy/truenas/.env:
PORT=8080
# Dann neu starten:
docker compose -f deploy/truenas/compose.yaml up -d
```
### Postgres startet nicht (Permission denied auf pgdata)
```bash
# UID 999 muss Eigentümer des pgdata-Verzeichnisses sein:
chown -R 999:999 /mnt/JailStorage/DockerVolumes/gerbilmanager/pgdata
chmod 700 /mnt/JailStorage/DockerVolumes/gerbilmanager/pgdata
docker compose -f deploy/truenas/compose.yaml restart db
```
### Registry-Pull schlägt fehl
```bash
# Neu einloggen:
docker login git.rismer.de
# Dann pull wiederholen:
docker compose -f deploy/truenas/compose.yaml pull
```
### Datenbank nicht erreichbar
```bash
docker compose -f deploy/truenas/compose.yaml ps db
docker compose -f deploy/truenas/compose.yaml exec db \
psql -U postgres -d gerbilmanager -c "\dt"
```
### Backup-Fehler
```bash
tail -50 /mnt/JailStorage/DockerVolumes/gerbilmanager/backups/backup.log
docker compose -f deploy/truenas/compose.yaml exec backup /bin/sh /scripts/backup.sh
```
### Fotos werden nicht angezeigt
```bash
docker compose -f deploy/truenas/compose.yaml exec api ls /data/photos
```
### Kompletter Neustart (Daten bleiben erhalten)
```bash
docker compose -f deploy/truenas/compose.yaml down
docker compose -f deploy/truenas/compose.yaml up -d
```