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

13 KiB
Raw Blame History

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
  2. Voraussetzungen
  3. Erstinstallation auf TrueNAS Goldeye
  4. App starten / stoppen / aktualisieren
  5. Backup & Wiederherstellung
  6. ZFS-Snapshot-Schichtung
  7. CI/CD via Gitea Actions
  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):

# 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

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

git clone https://git.rismer.de/gulum/GerbilManager.git /opt/gerbilmanager
cd /opt/gerbilmanager

3.4 Konfiguration anlegen

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

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

# 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

cd /opt/gerbilmanager
docker compose -f deploy/truenas/compose.yaml up -d

Stoppen

docker compose -f deploy/truenas/compose.yaml down

Aktualisieren (nach CI-Push auf main)

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

docker compose -f deploy/truenas/compose.yaml exec backup /bin/sh /scripts/backup.sh

Backup-Log prüfen

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)

docker compose -f deploy/truenas/compose.yaml stop api frontend

Schritt 2: Restore ausführen

# 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

docker compose -f deploy/truenas/compose.yaml start api frontend

Schritt 4 — Verifikation (Pflicht nach erstem Restore-Drill):

# 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 → DatasetsJailStorage/DockerVolumes/gerbilmanagerSnapshotsRegelmäß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)

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

# 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

# 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

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:

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

# 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

# Neu einloggen:
docker login git.rismer.de
# Dann pull wiederholen:
docker compose -f deploy/truenas/compose.yaml pull

Datenbank nicht erreichbar

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

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

docker compose -f deploy/truenas/compose.yaml exec api ls /data/photos

Kompletter Neustart (Daten bleiben erhalten)

docker compose -f deploy/truenas/compose.yaml down
docker compose -f deploy/truenas/compose.yaml up -d