Files
pingu-concerts/docs/wiki/Deployment.md
T

13 KiB

Deployment

MetalCircle hat getrennte Umgebungen: lokale Entwicklung auf PinguCore, Pre-Production unter https://konzerte.pinguholic.de/ und spätere Produktion. Codex bereitet Änderungen lokal vor; Serverzugriff und Deployment erfolgen durch den Betreiber. Keine lokalen Datenbanken oder Credentials auf Pre-Production übernehmen.

Pre-Production: verbindliche Pfade

Zweck Pfad
Git-Checkout /opt/pingu-concerts
Aktive Environment-Datei /home/kai/.config/metalcircle/preprod.env
Lokale Env-Sicherungen /home/kai/.config/metalcircle/backups/
Firebase-Key auf dem Host /home/kai/.secrets/metalcircle/firebase-push-preprod.json
Read-only Mount im Webcontainer /run/secrets/firebase-service-account.json

Der Checkout benötigt keine aktive .env mehr. scripts/deploy-preprod.sh definiert den Environment-Pfad zentral und führt über den stdlib-Helfer scripts/preprod_config.py diese Compose-Kombination aus:

sudo docker compose \
  --env-file /home/kai/.config/metalcircle/preprod.env \
  -f compose.yml -f compose.preprod.yml <Unterbefehl>

Voraussetzungen: Bash, Python 3.10+, Git, Docker Compose v2 mit config --environment und sudo-Berechtigung für lokales Docker. Als Deployment-Benutzer kai, nicht das gesamte Script mit sudo starten. Auf dem Host sind keine zusätzlichen Python-Pakete nötig. Compose-Projekt pingu-concerts und vorhandene Daten-/Upload-Volumes beibehalten.

Konfiguration und Rechte

config/preprod.env.example ist die Vorlage ohne Secrets. Pflichtvariablen:

  • POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD
  • INITIAL_ADMIN_USERNAME, INITIAL_ADMIN_PASSWORD, INITIAL_ADMIN_EMAIL
  • GITEA_URL, GITEA_TOKEN, GITEA_OWNER, GITEA_REPO
  • PUSH_ENABLED, FIREBASE_PROJECT_ID, FIREBASE_SERVICE_ACCOUNT_FILE, COOKIE_SECURE

Die Bootstrap-Adminwerte bleiben auch bei bestehender Datenbank Teil des vollständigen Deployment-Vertrags. DB-/Adminwerte aus der bestehenden Installation übernehmen: Ein geändertes POSTGRES_PASSWORD in der Datei ändert nicht das Passwort der bereits initialisierten PostgreSQL-Rolle.

Für normale Pre-Production-Deployments müssen PUSH_ENABLED=true, COOKIE_SECURE=true und FIREBASE_PROJECT_ID=metalcircle-30d9b gesetzt sein. GOOGLE_APPLICATION_CREDENTIALS setzt Compose intern. ALPHA_TESTER_UNTIL und BETA_TESTER_UNTIL bleiben optional.

Env-Datei und Firebase-Key müssen reguläre Dateien außerhalb des Checkouts sein, dem Deployment-Benutzer gehören und Modus 600 (alternativ 400) besitzen. Symlinks und Gruppen-/Weltzugriff werden abgelehnt. Verzeichnisse unter .config/metalcircle und .secrets/metalcircle mit 700 anlegen. Keine Keys in Buildkontext oder Image ablegen.

Compose interpretiert die dotenv-Syntax selbst. Einzeilige Passwörter/Tokens mit $ oder # in einfache Anführungszeichen setzen; die Datei niemals mit source ausführen. Der Helfer entfernt geerbte Shell-Variablen, die sonst laut Compose-Priorität die Env-Datei übersteuern könnten. Fehlende Variablen werden weder durch die alte Checkout-.env noch durch Shell-Exports ergänzt. Keine COMPOSE_*-/DOCKER_*-Overrides in preprod.env eintragen; ein abweichendes Compose-Projekt wird abgelehnt.

Migration der bisherigen Checkout-.env

Diese Schritte führt der Betreiber einmalig auf Pre-Production aus. Das Script löscht oder verändert /opt/pingu-concerts/.env nicht.

  1. Nach Bereitstellung der Änderung den Checkout aktualisieren. Bestehende Konfiguration geschützt aufbewahren und auf Vollständigkeit prüfen.

  2. Geschützte Verzeichnisse anlegen:

    install -d -m 700 /home/kai/.config/metalcircle /home/kai/.secrets/metalcircle
    
  3. Eine bestehende Zieldatei nicht überschreiben. Die bisherige .env kann als Ausgangspunkt dienen:

    test ! -e /home/kai/.config/metalcircle/preprod.env && \
      install -m 600 /opt/pingu-concerts/.env /home/kai/.config/metalcircle/preprod.env
    

    Alternativ bei fehlender/ungeeigneter Altdatei die Vorlage verwenden, ebenfalls nur bei noch nicht vorhandenem Ziel:

    test ! -e /home/kai/.config/metalcircle/preprod.env && \
      install -m 600 /opt/pingu-concerts/config/preprod.env.example /home/kai/.config/metalcircle/preprod.env
    

    Die aktiven DB-/Adminwerte und den dedizierten metalcircle-bot-Token im Editor ergänzen. Alle Variablennamen mit der Vorlage abgleichen. Keine Secret-Inhalte in Terminalausgabe, Tickets oder Chats kopieren.

  4. Den eigenen Pre-Production-Key am oben angegebenen Host-Pfad bereitstellen, Eigentümer kai und Modus 600 prüfen. FIREBASE_SERVICE_ACCOUNT_FILE muss diesen absoluten Pfad enthalten. Local-Key nicht wiederverwenden. Firebase-Einrichtung und IAM.

  5. Nur die Prüfungen ausführen; dies baut das Web-Image, ersetzt aber keinen laufenden Container:

    cd /opt/pingu-concerts
    ./scripts/deploy-preprod.sh --check
    
  6. Bei PASS normal deployen, danach Login, einen Bugreport mit optionalem Screenshot und die drei Push-Typen prüfen:

    ./scripts/deploy-preprod.sh
    
  7. Erst nach erfolgreichem Anwendungstest die alte /opt/pingu-concerts/.env manuell geschützt außerhalb des Checkouts archivieren oder entfernen. Editor-Swap-/Backupdateien können ebenfalls Secrets enthalten. Der neue Ablauf ist bereits vor diesem Aufräumen unabhängig von der Altdatei.

Normaler Ablauf und Abbruchbedingungen

./scripts/deploy-preprod.sh:

  1. Verlangt Repository-Kontext, Branch main und sauberen Working Tree; führt git pull --ff-only origin main aus.
  2. Prüft Env-Datei, Eigentümer, Rechte und Pflichtwerte sowie den externen Firebase-Key. Ausgabe enthält nur Variablennamen und set bzw. den fehlenden Namen. Compose-Parsing erfolgt intern, ohne Werte oder rohe Fehlermeldungen auszugeben.
  3. Führt config --quiet aus und baut ausschließlich web.
  4. Startet zwei kurzlebige Container mit run --rm --no-deps -T web: zuerst push_preflight.py, dann gitea_preflight.py. Beide müssen Exit-Code 0 und PASS liefern. Diese Programme starten weder FastAPI noch den Push-Worker und führen keine Migration aus.
  5. Prüft, ob Env-Datei oder Key seit Beginn der Prüfung verändert wurden, und bricht gegebenenfalls ab. Während eines Deployments diese Dateien nicht bearbeiten.
  6. Sichert die geprüfte Env-Datei. Erst dann folgt up -d --no-deps web.
  7. Wiederholt beide Preflights per exec -T web im laufenden Container und zeigt Containerstatus, gefilterte Web-Logs (letzte zwei Minuten, höchstens 100 Eingabezeilen) und den deployten Commit.

Beispiel vor dem Containerwechsel:

ERROR: required variable GITEA_TOKEN is missing
Deployment aborted. Running container unchanged.

Fehler bei Konfiguration, Build, Vorprüfungen oder Backup ersetzen keinen laufenden Container. Scheitert eine Prüfung nach up, meldet das Script ausdrücklich den bereits erfolgten Update-Versuch. Kein automatischer Rollback, kein down, kein Volume-Löschen, kein DB-Recreate und kein --force-recreate. Beim regulären FastAPI-Start läuft weiterhin die bestehende Schema-Initialisierung; diese Änderung führt keine neue Migration ein.

--check überspringt Pull, Env-Backup und Containerwechsel. Der vorhandene Checkout wird geprüft und das Image gebaut. Die lokale Vorbereitung ersetzt keine Pre-Production-Smoke-Tests.

Was die Container-Preflights prüfen

Firebase: Der bestehende Offline-Preflight prüft Aktivierung, exakten Containerpfad, reguläre Datei, restriktive Rechte, read-only Mount, lesbares Credential, Projekt und exakt metalcircle-push-preprod@metalcircle-30d9b.iam.gserviceaccount.com. Falsche Local-Credentials werden abgelehnt. IAM, OAuth und Zustellung werden dabei nicht über das Netzwerk getestet. Die erforderliche Rolle bleibt roles/firebasecloudmessaging.admin.

Gitea: gitea_preflight.py validiert die vier vorhandenen Variablen, URL und Repository-Namen. Danach ausschließlich:

  • GET /api/v1/user: verlangt login == "metalcircle-bot".
  • GET /api/v1/repos/{owner}/{repo}/issues?limit=1&page=1: prüft Erreichbarkeit des Repository-Issue-Bereichs mit den bereits benötigten Token-Scopes read:user und write:issue. Kein zusätzliches read:repository nötig. Siehe Gitea-API-Zugang.

Es werden weder Issues noch Anhänge angelegt. Response-Inhalte und Tokens werden nicht geloggt. Ein read-only PASS bestätigt keinen Schreib-/Attachment-Vorgang; ein vom Betreiber ausgelöster Bugreport gehört deshalb zum Smoke-Test. Timeouts, Authentifizierungsfehler, fehlende Rechte und ungültige Antworten stoppen das Deployment mit festen Fehlerkategorien.

Env-Backups

Nach bestandenen Vorprüfungen entsteht unter /home/kai/.config/metalcircle/backups/ eine Kopie preprod.env.YYYYMMDD-HHMMSS-microseconds (UTC). Verzeichnis 700, Dateien 600, nur die letzten 10 Sicherungen dieses Namensschemas werden behalten. Fremde Dateien werden nicht gelöscht. Gesichert wird ausschließlich die Env-Datei, nicht der Firebase-Key oder die Datenbank.

Eine Sicherung bleibt auch bei einem später fehlgeschlagenen Container-Update erhalten. Fehler beim Sichern/Aufräumen brechen vor dem Update ab. Diese Dateien enthalten Secrets und gehören niemals ins Git. Bei Wiederherstellung im Editor kontrollieren, geschützt nach preprod.env kopieren, --check ausführen und normal deployen. Die Sicherungen sind keine unabhängige Offsite-Backup-Strategie.

Manueller Fallback / Troubleshooting

Die einzelnen Compose-Schritte bleiben verfügbar. Diese Hilfsfunktion verwendet dieselbe explizite Env-Datei, bereinigte Shell-Umgebung und sichere Ausgabe wie das Script. Intern wird jeweils der oben gezeigte sudo docker compose --env-file ... -f compose.yml -f compose.preprod.yml-Befehl ausgeführt:

cd /opt/pingu-concerts
git status --short
# Nur main und einen sauberen Checkout verwenden.
git pull --ff-only origin main
compose_preprod() {
  python3 scripts/preprod_config.py \
    --env-file /home/kai/.config/metalcircle/preprod.env compose "$@"
}
python3 scripts/preprod_config.py --env-file /home/kai/.config/metalcircle/preprod.env check
compose_preprod config --quiet
compose_preprod build web
compose_preprod run --rm --no-deps -T web python push_preflight.py \
  --expected-service-account metalcircle-push-preprod@metalcircle-30d9b.iam.gserviceaccount.com
compose_preprod run --rm --no-deps -T web python gitea_preflight.py
# Nur nach ALLEN erfolgreichen Prüfungen weiter; bei jedem Fehler abbrechen.
python3 scripts/preprod_config.py --env-file /home/kai/.config/metalcircle/preprod.env backup
compose_preprod up -d --no-deps web
compose_preprod exec -T web python push_preflight.py \
  --expected-service-account metalcircle-push-preprod@metalcircle-30d9b.iam.gserviceaccount.com
compose_preprod exec -T web python gitea_preflight.py
compose_preprod ps --format json
compose_preprod logs --since=2m --tail=100 --no-color web
git log -1 --format='%h %s'

Keine vollständigen compose config, config --environment, docker inspect oder .env-Inhalte ausgeben/teilen. Der Helfer erfasst die Compose-Interpolation intern im Speicher; stdout/stderr mit potentiellen Secrets werden nicht durchgereicht. Web-Logs zeigen nur ausgewählte Lebenszyklusmeldungen, HTTP-Methode/Status ohne URL und feste Gitea-/Push-Fehlerkategorien. Andere Zeilen werden gezählt und ausgelassen.

Meldung Prüfung durch Betreiber
required variable ... is missing Benannte Variable in der externen Env-Datei ergänzen; alte .env hilft nicht
requires permissions 600 or 400 Dateirechte und Eigentümer prüfen
gitea_configuration Basis-URL ohne Login/Query/Fragment, Owner und Repository prüfen
gitea_wrong_account Dedizierten metalcircle-bot-Token verwenden
gitea_http_401 Token ungültig/widerrufen
gitea_http_403 Bot-Zugriff und read:user/write:issue prüfen
gitea_http_404 Repository/URL oder Sichtbarkeit für Bot prüfen
gitea_http_5xx / gitea_network_or_timeout Gitea-Verfügbarkeit, WireGuard, DNS/Netz prüfen
gitea_invalid_response Falsches Ziel/Proxy oder unerwartete API-Antwort prüfen
Firebase credential_service_account_mismatch Pre-Production-Key statt Local-Key verwenden
Compose build failed Build/Registry-Erreichbarkeit prüfen; laufender Container wurde nicht ersetzt

Lokale Verifikation

bash -n scripts/deploy-preprod.sh
python3 -m unittest discover -s scripts/tests -v

Shell-Ablauftests benutzen temporäre Verzeichnisse und simulierte Git-/Docker-Kommandos, keine echten Deployments. Ein Test verwendet nur den lokalen Compose-Parser mit synthetischer Env-Datei, ohne Docker-Engine/Netz. Die Backend-Suite unter app/tests deckt Gitea- und Firebase-Preflights ab; Testaufruf.