docs: Doku-Abgleich mit origin/main (Stand 2026-10-16)
Docker-Images bauen und veroeffentlichen / lint (push) Successful in 1m30s
Docker-Images bauen und veroeffentlichen / typecheck (push) Successful in 1m48s
Docker-Images bauen und veroeffentlichen / test (push) Successful in 1m33s
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 7m20s
Docker-Images bauen und veroeffentlichen / lint (push) Successful in 1m30s
Docker-Images bauen und veroeffentlichen / typecheck (push) Successful in 1m48s
Docker-Images bauen und veroeffentlichen / test (push) Successful in 1m33s
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 7m20s
README mit Projektstand (Links auf Issues #33, #32), fehlenden Befehlen und CI-Gate. BETRIEB: sha-gepinnte Manifeste, DB-Sicherung, Rollout per set image, Label-Selector im pg_dump-Befehl, Storage Box. k8s/README: Schritt 09, Warnung vor delete namespace. TESTS, OFFENE-PUNKTE, ARCHITEKTUR (ADR-8, ADR-9) aktualisiert; Kommentare in .env.example und Worker-CronJob korrigiert. Refs #31 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
9785eb221f
commit
8a85bfaed4
8 files changed
+85
-33
No files matched your search
+22
-12
@@ -2,16 +2,18 @@
|
||||
|
||||
## 1. Images bauen
|
||||
|
||||
**Automatisch (Gitea Actions):** `.gitea/workflows/docker-build.yml` baut bei jedem Push auf `main`, bei Tags `v*` oder manuell per „Run workflow“ beide Images und pusht sie in die Gitea-Registry:
|
||||
**Automatisch (Gitea Actions):** `.gitea/workflows/docker-build.yml` läuft bei jedem Push auf `main`, bei Tags `v*` oder manuell per „Run workflow“. Zuerst prüfen drei Jobs auf dem isolierten Runner (`ci-isolated`, ohne Docker-Socket) den Code: `lint` (inklusive `pnpm audit --prod --audit-level high`), `typecheck` und `test` (mit PostgreSQL 18.6 als Service). Nur wenn alle drei grün sind, baut `build-and-push` auf dem Runner `docker-builder` beide Images und pusht sie in die Gitea-Registry:
|
||||
|
||||
- `main` ergibt die Tags `latest` und `sha-<kurz>`
|
||||
- `v0.1.0` ergibt zusätzlich den Tag `0.1.0`
|
||||
|
||||
Einmalig einrichten:
|
||||
- ein Personal Access Token mit Scope `package` als Repo-Secret `REGISTRY_TOKEN` hinterlegen (Repo-Einstellungen → Actions → Secrets)
|
||||
- einen Runner mit dem Label `docker-builder` bereitstellen (wie bei Wabenhain)
|
||||
- Runner mit den Labels `ci-isolated` (Prüfungen) und `docker-builder` (Image-Build) bereitstellen; im EnCry-Cluster sind beide vorhanden
|
||||
|
||||
Die Manifeste in `k8s/` nutzen den Tag `latest`. Für feste Versionen dort `:latest` durch z. B. `:0.1.0` ersetzen (`git tag v0.1.0 && git push origin v0.1.0`).
|
||||
Die Manifeste in `k8s/` sind auf einen CI-Build gepinnt (`sha-<commit>`, Platzhalter `sha-REPLACE_ME` im Repo), nicht auf `latest`. Ausgerollt wird mit `kubectl set image`, siehe Abschnitt 3 und [k8s/README.md](../k8s/README.md).
|
||||
|
||||
Scheitert `build-and-push` beim Push mit `500 … cannot allocate memory`, lag das bisher an der Gitea-Registry, wenn gleichzeitig ein anderes Repo pushte. Dann den Lauf neu starten, sobald der andere Build fertig ist.
|
||||
|
||||
**Manuell:**
|
||||
|
||||
@@ -29,24 +31,32 @@ Der Build braucht Internet für `next/font` (Schriften werden eingebettet), aber
|
||||
- der Worker-CronJob, nötig für Reservierungsfreigabe, Abgleich der Folgeraten und Mail-Wiederholung
|
||||
- ConfigMap
|
||||
- zwei NetworkPolicies (Schutz der Rate Limits und der Datenbank)
|
||||
- der Sicherungs-CronJob `artista-db-backup` (täglicher `pg_dump`, Abschnitt 7)
|
||||
|
||||
Eine eigene Sicherung im Cluster gibt es nicht, die SMB-Freigabe wird auf dem NAS gesichert.
|
||||
**Schritt für Schritt zum Kopieren: [k8s/README.md](../k8s/README.md).** Die Manifeste liegen flach in `k8s/` (`00-namespace` bis `09-db-backup-cronjob`, Namensschema wie bei Wabenhain) und werden in dieser Reihenfolge einzeln angewendet. Die Secrets kommen aus `k8s/02-secret.yaml.example` und werden nicht eingecheckt. Pod Security: Erzwungen wird `baseline`, damit der Zertifikats-Solver von cert-manager starten kann. `restricted` wird nur gemeldet, die eigenen Pods erfüllen es.
|
||||
|
||||
**Schritt für Schritt zum Kopieren: [k8s/README.md](../k8s/README.md).** Die Manifeste liegen flach in `k8s/` (`00-namespace` bis `08-networkpolicy`, Namensschema wie bei Wabenhain) und werden in dieser Reihenfolge einzeln angewendet. Die Secrets kommen aus `k8s/02-secret.yaml.example` und werden nicht eingecheckt. Pod Security: Erzwungen wird `baseline`, damit der Zertifikats-Solver von cert-manager starten kann. `restricted` wird nur gemeldet, die eigenen Pods erfüllen es.
|
||||
Im EnCry-Cluster liegen die SMB-Freigaben auf der Hetzner Storage Box, nicht auf einem lokalen NAS; „NAS“ meint im Folgenden diese Freigabe. Im Namespace `artista` läuft außerdem Artista-Telefonie (eigenes Repo `Artista-TeleTracking`). Sie nutzt eine eigene Datenbank `telefonie` in derselben PostgreSQL-Instanz `artista-modelcamp-postgres`. Wer die Instanz oder den Namespace anfasst, trifft beide Anwendungen.
|
||||
|
||||
Zu PostgreSQL:
|
||||
- **PostgreSQL auf SMB ist eine bewusste Ausnahme.** SMB garantiert Sperren und fsync nicht in jedem Fall wie ein lokales Dateisystem. Bei einem NAS- oder Netzwerkausfall während Schreibvorgängen kann die Datenbank beschädigt werden.
|
||||
- **Gemildert** wird das durch `hard`, `cache=strict`, SMB-Verschlüsselung (`seal`) und ein 120 Sekunden langes, sauberes Herunterfahren. Die Rechte setzen die `mountOptions`, weil `fsGroup` auf SMB nicht wirkt: Postgres `uid=70`, `0700`; Medien `uid=1001`, nur lesbar.
|
||||
- **Sicherung:** Die NAS-Sicherung ist für die laufende Datenbank nur mit **atomaren Snapshots** der Freigabe (Btrfs/ZFS) konsistent. Zusätzlicher Schutz bei Bedarf: gelegentlich einen `pg_dump` ziehen (Abschnitt 7).
|
||||
- **Sicherung:** Eine Kopie der Freigabe ist für die laufende Datenbank nur mit **atomaren Snapshots** (Btrfs/ZFS) konsistent. Deshalb zieht der CronJob `artista-db-backup` jede Nacht einen `pg_dump` (Abschnitt 7).
|
||||
- **Nach SMB-Unterbrechungen** melden lang laufende Postgres-Prozesse gelegentlich `could not seek to end of file … Bad file descriptor`. Abhilfe: `kubectl -n artista rollout restart sts/artista-modelcamp-postgres` (fährt sauber herunter).
|
||||
|
||||
## 3. Update ausrollen
|
||||
|
||||
App, Migration und Worker immer mit demselben Tag `sha-<commit>` aus der CI ausrollen (genauer Befehl in [k8s/README.md](../k8s/README.md), Abschnitt „Bei einem neuen Image“):
|
||||
|
||||
```bash
|
||||
kubectl -n artista rollout restart deploy/artista-modelcamp-app
|
||||
TAG=sha-<commit>
|
||||
kubectl -n artista set image deployment/artista-modelcamp-app migrate=git.encry.de/nick.rosenloecher/artista-modelcamp-tools:$TAG app=git.encry.de/nick.rosenloecher/artista-modelcamp:$TAG
|
||||
kubectl -n artista set image cronjob/artista-modelcamp-worker worker=git.encry.de/nick.rosenloecher/artista-modelcamp-tools:$TAG
|
||||
kubectl -n artista rollout status deploy/artista-modelcamp-app
|
||||
kubectl -n artista logs deploy/artista-modelcamp-app -c migrate # Migrationsausgabe
|
||||
```
|
||||
|
||||
Ein `rollout restart` allein holt kein neues Image, weil die Manifeste auf einen festen Tag zeigen.
|
||||
|
||||
Jeder neue App-Pod migriert per initContainer zuerst die Datenbank. Scheitert das, startet der Pod nicht, und die alten Pods laufen weiter.
|
||||
|
||||
Migrationen sind vorwärtskompatibel zu schreiben (erst Spalten ergänzen, später entfernen), damit alte und neue Pods während des Rollouts parallel laufen können.
|
||||
@@ -85,10 +95,10 @@ Ein fehlgeschlagener Lauf erscheint als fehlgeschlagener Job (`kubectl -n artist
|
||||
|
||||
## 7. Sicherung und Wiederherstellung
|
||||
|
||||
- **Sicherung:** Die NAS-Sicherung der SMB-Freigabe übernimmt das Speichersystem. Für PostgreSQL gilt das nur mit atomaren Snapshots, siehe Abschnitt 2. Im Cluster läuft kein Backup-Job.
|
||||
- **Logischer Dump** (empfohlen vor Updates und vor dem Live-Verkauf):
|
||||
- **Täglicher Dump im Cluster:** Der CronJob `artista-db-backup` (`k8s/09-db-backup-cronjob.yaml`) läuft um 02:25 Uhr (Zeitzone des Clusters) und schreibt `pg_dump -Fc` der Datenbank `artista` in die PVC `artista-db-backup` (StorageClass `smb-backup`, Ordner `Backups/artista/` auf der Storage Box). Aufbewahrung: 14 Tage täglich (`daily/`), 8 Wochen sonntags (`weekly/`). Die Datenbank `telefonie` derselben Instanz ist darin nicht enthalten. Stand prüfen: `kubectl -n artista get cronjob artista-db-backup` und `kubectl -n artista get jobs | grep artista-db-backup`.
|
||||
- **Logischer Dump von Hand** (zusätzlich vor Updates und vor dem Live-Verkauf):
|
||||
```bash
|
||||
POD=$(kubectl -n artista get pod -l app.kubernetes.io/name=artista-modelcamp-postgres -o name | head -1)
|
||||
POD=$(kubectl -n artista get pod -l app=artista-modelcamp-postgres -o name | head -1)
|
||||
kubectl -n artista exec $POD -- pg_dump -U artista -d artista --format=custom > artista-$(date +%F).dump
|
||||
```
|
||||
- **Wiederherstellen:**
|
||||
@@ -96,8 +106,8 @@ Ein fehlgeschlagener Lauf erscheint als fehlgeschlagener Job (`kubectl -n artist
|
||||
2. Einspielen: `kubectl -n artista exec -i $POD -- pg_restore -U artista -d artista --clean --if-exists < artista-YYYY-MM-DD.dump`
|
||||
3. App wieder hochskalieren (der initContainer migriert dabei), Worker fortsetzen.
|
||||
4. Abgleich: Zahlungen, die nach dem Sicherungszeitpunkt eingingen, im Stripe-Dashboard prüfen. Stripe stellt Webhooks bis zu drei Tage erneut zu (Dashboard → Webhooks → „Erneut senden“).
|
||||
- **Medien:** liegen im SMB-Ordner `modelcamp-media` und sind in der NAS-Sicherung enthalten.
|
||||
- **Wiederherstellung üben:** mindestens einmal vor dem Live-Verkauf in einen Test-Namespace.
|
||||
- **Medien:** liegen im SMB-Ordner `modelcamp-media`; gesichert sind sie nur über die Sicherung der Freigabe selbst.
|
||||
- **Wiederherstellung üben:** mindestens einmal vor dem Live-Verkauf in einen Test-Namespace. Das ist noch nicht geschehen.
|
||||
|
||||
## 8. Stornierung und Erstattung
|
||||
|
||||
|
||||
Reference in new issue
Block a user