refactor(k8s): minimale, flache Manifeste zum Kopieren

Kustomize-Struktur (base/overlays) entfernt. Stattdessen nummerierte
Einzeldateien, ausrollbar mit "kubectl apply -f k8s/":
00-namespace, 01-config, 02-storage (SMB-PVs/PVCs Postgres + Medien),
03-postgres (StatefulSet + Service), 04-app (Deployment mit
Migrations-initContainer + Service), 05-ingress, 06-worker,
07-networkpolicy. Secrets als secrets.yaml.example (echte secrets.yaml
ist gitignored). Images mit Tag latest aus der Gitea-Pipeline.
Kurzanleitung in k8s/README.md, Betriebsdoku angepasst.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-09-28 01:12:26 +02:00
1 parent c84eb1e740
commit 91791b791f
20 files changed
+332 -494

No files matched your search

+12 -28
View File
@@ -11,13 +11,13 @@ 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)
Ein Release entsteht dann so: `git tag v0.1.0 && git push origin v0.1.0`, anschließend den Tag im Overlay setzen.
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`).
**Manuell:**
```bash
docker build --target runner -t git.encry.de/nick.rosenloecher/artista-modelcamp:0.1.0 .
docker build --target tools -t git.encry.de/nick.rosenloecher/artista-modelcamp-tools:0.1.0 .
docker build --target runner -t git.encry.de/nick.rosenloecher/artista-modelcamp:latest .
docker build --target tools -t git.encry.de/nick.rosenloecher/artista-modelcamp-tools:latest .
docker push … # beide Images, immer mit identischem Tag
```
@@ -32,39 +32,23 @@ Der Build braucht Internet für `next/font` (Schriften werden eingebettet), aber
Eine eigene Sicherung im Cluster gibt es nicht, die SMB-Freigabe wird auf dem NAS gesichert.
1. **Namespace:** `kubectl create namespace artista` und `kubectl label namespace artista pod-security.kubernetes.io/enforce=restricted`
2. **Overlay anpassen** (`k8s/overlays/production/`): Image-Tag, Domain in `kustomization.yaml` (APP_URL) und `ingress.yaml`, `ingressClassName` (`kubectl get ingressclass`), ClusterIssuer (Beispiel `letsencrypt-prod`) und TLS-Secret-Name. Die HTTP→HTTPS-Umleitung erwartet das Manifest in Traefik selbst.
3. **SMB-Speicher** (`k8s/overlays/production/smb-volumes.yaml`):
1. csi-driver-smb installieren, Befehl steht im Kopf der Datei.
2. Auf dem NAS die Ordner `modelcamp-postgres` (leer) und `modelcamp-media` anlegen und `source` in der Datei anpassen.
3. SMB-Secret anlegen: `kubectl -n artista create secret generic artista-modelcamp-smb --from-literal=username='…' --from-literal=password='…'`. Empfohlen ist ein eigener NAS-Benutzer, der nur diese Ordner sieht.
4. Das NAS muss SMB 3.1.1 mit Verschlüsselung erlauben (`seal`), z. B. Synology unter „SMB-Verschlüsselung für Übertragung“, Windows mit `Set-SmbShare -EncryptData $true`.
**Schritt für Schritt zum Kopieren: [k8s/README.md](../k8s/README.md).** Die Manifeste liegen flach in `k8s/` (`00-…` bis `07-…`) und werden mit `kubectl apply -f k8s/` ausgerollt. Die Secrets kommen aus `k8s/secrets.yaml.example` und werden nicht eingecheckt.
Die Rechte setzen die `mountOptions`, auf SMB wirkt `fsGroup` nicht: Postgres `uid=70`, `0700`/`0600`; Medien `uid=1001`, für die App nur lesbar (`0550`/`0440`).
**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`, Verschlüsselung und ein 120 Sekunden langes, sauberes Herunterfahren. Die NAS-Sicherung ist nur mit **atomaren Snapshots** der Freigabe (Btrfs/ZFS) konsistent. Ein Dateikopie-Backup eines laufenden Datenverzeichnisses lässt sich nicht zuverlässig wiederherstellen. Zusätzlicher Schutz bei Bedarf: gelegentlich einen `pg_dump` ziehen (Abschnitt 7).
4. **Secret anlegen:** Befehl steht in `k8s/secret.example.yaml`, besser über Sealed Secrets oder den External Secrets Operator.
5. **Anwenden:** `kubectl apply -k k8s/overlays/production`. Der initContainer migriert die Datenbank vor dem App-Start.
6. **Erstes Admin-Konto:**
```bash
kubectl -n artista run admin-create --rm -it --restart=Never --labels=artista/db-client=true \
--image=git.encry.de/nick.rosenloecher/artista-modelcamp-tools:0.1.0 \
--overrides='{"spec":{"containers":[{"name":"admin-create","image":"git.encry.de/nick.rosenloecher/artista-modelcamp-tools:0.1.0","command":["tsx","scripts/create-admin.ts","--email","[email protected]","--name","Dein Name"],"envFrom":[{"configMapRef":{"name":"artista-modelcamp-config"}},{"secretRef":{"name":"artista-modelcamp-secrets"}}]}]}}'
```
Das einmalige Passwort wird ausgegeben. Bei der ersten Anmeldung unter `/admin` wird die 2FA eingerichtet.
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).
## 3. Update ausrollen
```bash
# Neuen Tag im Overlay setzen und anwenden. Jeder neue App-Pod migriert per
# initContainer "migrate" zuerst die Datenbank; scheitert das, startet er nicht
# und die alten Pods bleiben aktiv (maxUnavailable: 0).
kubectl apply -k k8s/overlays/production
kubectl -n artista rollout restart deploy/artista-modelcamp-app
kubectl -n artista rollout status deploy/artista-modelcamp-app
# Migrationsausgabe bei Bedarf:
kubectl -n artista logs deploy/artista-modelcamp-app -c migrate
kubectl -n artista logs deploy/artista-modelcamp-app -c migrate # Migrationsausgabe
```
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.
## 4. Stripe