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_PASSWORDINITIAL_ADMIN_USERNAME,INITIAL_ADMIN_PASSWORD,INITIAL_ADMIN_EMAILGITEA_URL,GITEA_TOKEN,GITEA_OWNER,GITEA_REPOPUSH_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.
-
Nach Bereitstellung der Änderung den Checkout aktualisieren. Bestehende Konfiguration geschützt aufbewahren und auf Vollständigkeit prüfen.
-
Geschützte Verzeichnisse anlegen:
install -d -m 700 /home/kai/.config/metalcircle /home/kai/.secrets/metalcircle -
Eine bestehende Zieldatei nicht überschreiben. Die bisherige
.envkann als Ausgangspunkt dienen:test ! -e /home/kai/.config/metalcircle/preprod.env && \ install -m 600 /opt/pingu-concerts/.env /home/kai/.config/metalcircle/preprod.envAlternativ 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.envDie 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. -
Den eigenen Pre-Production-Key am oben angegebenen Host-Pfad bereitstellen, Eigentümer kai und Modus 600 prüfen.
FIREBASE_SERVICE_ACCOUNT_FILEmuss diesen absoluten Pfad enthalten. Local-Key nicht wiederverwenden. Firebase-Einrichtung und IAM. -
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 -
Bei PASS normal deployen, danach Login, einen Bugreport mit optionalem Screenshot und die drei Push-Typen prüfen:
./scripts/deploy-preprod.sh -
Erst nach erfolgreichem Anwendungstest die alte
/opt/pingu-concerts/.envmanuell 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:
- Verlangt Repository-Kontext, Branch
mainund sauberen Working Tree; führtgit pull --ff-only origin mainaus. - Prüft Env-Datei, Eigentümer, Rechte und Pflichtwerte sowie den externen Firebase-Key. Ausgabe enthält nur Variablennamen und
setbzw. den fehlenden Namen. Compose-Parsing erfolgt intern, ohne Werte oder rohe Fehlermeldungen auszugeben. - Führt
config --quietaus und baut ausschließlichweb. - Startet zwei kurzlebige Container mit
run --rm --no-deps -T web: zuerstpush_preflight.py, danngitea_preflight.py. Beide müssen Exit-Code 0 undPASSliefern. Diese Programme starten weder FastAPI noch den Push-Worker und führen keine Migration aus. - 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.
- Sichert die geprüfte Env-Datei. Erst dann folgt
up -d --no-deps web. - Wiederholt beide Preflights per
exec -T webim 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: verlangtlogin == "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-Scopesread:userundwrite:issue. Kein zusätzlichesread:repositorynö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.