first commit
Docker-Images bauen und veroeffentlichen / build-and-push (push) Failing after 1m32s

This commit is contained in:
Nick Szaunig committed 2026-09-27 06:06:56 +02:00
commit 59bb5a38a3
132 files changed
+19435

No files matched your search

+117
View File
@@ -0,0 +1,117 @@
# Betrieb auf RKE2
## 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:
- `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)
Ein Release entsteht dann so: `git tag v0.1.0 && git push origin v0.1.0`, anschließend den Tag im Overlay setzen.
**Manuell:**
```bash
docker build --target runner -t git.encry.de/nick.rosenloecher/artista-filmcamp:0.1.0 .
docker build --target tools -t git.encry.de/nick.rosenloecher/artista-filmcamp-tools:0.1.0 .
docker push … # beide Images, immer mit identischem Tag
```
Der Build braucht Internet für `next/font` (Schriften werden eingebettet), aber weder Datenbank noch Secrets.
## 2. Einmalig einrichten
1. **Overlay anpassen** (`k8s/overlays/production/`): Image-Namen und Tag, Domain in `kustomization.yaml` (APP_URL) und `ingress.yaml`, `ingressClassName` (`kubectl get ingressclass`), ClusterIssuer (Beispiel `letsencrypt-prod`) und TLS-Secret-Name.
2. **StorageClasses:**
- PostgreSQL bekommt Blockspeicher mit ReadWriteOnce, auf keinen Fall eine SMB-Freigabe (Begründung in `k8s/base/postgres.yaml`).
- Das Medienvolume braucht ReadWriteMany, sonst `replicas: 1`.
- Das Backup-Volume darf NFS oder SMB sein.
3. **Secret anlegen:** Befehl steht in `k8s/secret.example.yaml`, besser über Sealed Secrets oder den External Secrets Operator.
4. **Anwenden:** `kubectl apply -k k8s/overlays/production`
5. **Migration** (siehe Abschnitt 3), danach das erste 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-filmcamp-tools:0.1.0 \
--overrides='{"spec":{"containers":[{"name":"admin-create","image":"git.encry.de/nick.rosenloecher/artista-filmcamp-tools:0.1.0","command":["tsx","scripts/create-admin.ts","--email","[email protected]","--name","Dein Name"],"envFrom":[{"configMapRef":{"name":"artista-config"}},{"secretRef":{"name":"artista-secrets"}}]}]}}'
```
Das einmalige Passwort wird ausgegeben. Bei der ersten Anmeldung unter `/admin` wird die 2FA eingerichtet.
## 3. Update ausrollen (kontrollierter Migrationsschritt)
```bash
# 1. Neuen Tag im Overlay setzen, anwenden (App rollt noch NICHT aus, solange Tag gleich bleibt)
kubectl apply -k k8s/overlays/production
# 2. Migration bewusst starten und abwarten
JOB=migrate-$(date +%Y%m%d%H%M)
kubectl -n artista create job $JOB --from=cronjob/artista-migrate
kubectl -n artista wait --for=condition=complete job/$JOB --timeout=5m
# 3. Rollout beobachten
kubectl -n artista rollout status deploy/artista-app
```
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
- **Schlüssel:** Im Test `sk_test_…`, live `sk_live_…` (nur mit `APP_ENV=production`). Empfohlen ist ein eingeschränkter Schlüssel mit Schreibrechten auf Customers, Checkout Sessions, PaymentIntents, PaymentMethods und Refunds.
- **Webhook-Endpunkt:** `https://<domain>/api/stripe/webhook`. Diese Events abonnieren:
`checkout.session.completed`, `checkout.session.async_payment_succeeded`, `checkout.session.async_payment_failed`, `checkout.session.expired`, `payment_intent.succeeded`, `payment_intent.payment_failed`, `charge.refunded`, `refund.created`, `refund.updated`, `charge.dispute.created`.
Das Signing Secret gehört als `STRIPE_WEBHOOK_SECRET` ins Secret. Test- und Live-Endpunkt haben getrennte Secrets.
- **Zahlungsarten** (Dashboard → Zahlungsmethoden): Für Ratenzahlung nur wiederverwendbare Methoden aktivieren (Karten, gegebenenfalls SEPA-Lastschrift). Checkout blendet bei Ratenzahlung ohnehin nur Methoden ein, die `setup_future_usage` unterstützen.
- **Test lokal:** `stripe listen --forward-to localhost:3000/api/stripe/webhook`
- **Sicherheitsnetz:** Der Worker gleicht bei abgelaufenen Reservierungen und hängenden Einzügen immer mit Stripe ab. Ein verlorener Webhook führt so nicht zu verlorenen Zahlungen.
## 5. E-Mail
`MAIL_TRANSPORT=smtp` mit `SMTP_HOST`, `SMTP_PORT`, `SMTP_SECURE`, `SMTP_USER` und `SMTP_PASSWORD`. Den Absender (`MAIL_FROM`) per SPF, DKIM und DMARC für die Absenderdomain freischalten. `ADMIN_NOTIFY_EMAIL` erhält Hinweise zu „Prüfung nötig“. Nicht zustellbare Mails zeigt das Admin-Dashboard. Der Worker versucht jede Mail bis zu achtmal.
## 6. Worker
Der CronJob `artista-worker` läuft alle 10 Minuten und kümmert sich um:
- abgelaufene Reservierungen, vorher mit Rückfrage bei Stripe
- hängende Einzüge
- fällige Raten
- das Trennen der Zahlungsmethode nach der letzten Rate
- den E-Mail-Versand
- das Aufräumen der Rate Limits
Ein fehlgeschlagener Lauf erscheint als fehlgeschlagener Job (`kubectl -n artista get jobs`).
## 7. Sicherung und Wiederherstellung
- **Sicherung:** Der CronJob `artista-db-backup` erstellt täglich um 3:15 Uhr einen Dump im `pg_dump`-Custom-Format nach `artista-db-backup` und bewahrt ihn 30 Tage auf.
- **Aus dem Cluster sichern (regelmäßig):**
```bash
POD=$(kubectl -n artista get pod -l app.kubernetes.io/name=artista-postgres -o name | head -1)
kubectl -n artista exec $POD -- pg_dump -U artista -d artista --format=custom > artista-$(date +%F).dump
```
- **Wiederherstellen:**
1. App stoppen: `kubectl -n artista scale deploy/artista-app --replicas=0` und den Worker-CronJob pausieren: `kubectl -n artista patch cronjob artista-worker -p '{"spec":{"suspend":true}}'`
2. Einspielen: `kubectl -n artista exec -i $POD -- pg_restore -U artista -d artista --clean --if-exists < artista-YYYY-MM-DD.dump`
3. Migration laufen lassen (Abschnitt 3), App wieder hochskalieren, 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:** Das Volume `artista-media` separat sichern, z. B. mit Volume-Snapshots oder der Sicherung des Speichersystems.
- **Wiederherstellung üben:** mindestens einmal vor dem Live-Verkauf in einen Test-Namespace.
## 8. Stornierung und Erstattung
Die Software trifft keine Storno-Entscheidung, sie setzt sie nur nachvollziehbar um.
1. **Stornieren** (Admin → Buchung → „Stornieren“, Grund ist Pflicht): Offene Raten werden beendet, Platz und Gutschein freigegeben, die gespeicherte Zahlungsmethode getrennt und offene Checkout-Sessions geschlossen. Danach wird nichts mehr abgebucht.
2. **Erstatten** (je bezahlter Rate, Betrag frei wählbar, höchstens bis zum gezahlten Betrag): Welcher Betrag erstattet wird, bestimmen die freigegebenen Stornobedingungen. Die Erstattung wird bei Stripe ausgelöst, der Zahlungsstatus aktualisiert sich per Webhook (`charge.refunded`). Ein doppeltes Abschicken wird über einen Idempotenzschlüssel abgefangen.
3. **Prüfung nötig** entsteht bei verspäteter Zahlung ohne freien Platz, Doppelzahlung, Rückbuchung oder endgültig fehlgeschlagener Rate. Dann entweder „Buchung bestätigen“ (nur mit freiem Platz, sonst vorher Kapazität erhöhen) oder stornieren und erstatten.
4. **Dokumentation:** Alle Schritte stehen im Protokoll der Buchung (AuditLog) mit handelnder Person.
## 9. Medien
Dateien (jpg, png, webp, avif, mp4, webm) in das Volume `artista-media` kopieren:
```bash
kubectl -n artista cp villa-aussen.jpg <app-pod>:/app/media/villa-aussen.jpg # Volume ist im App-Pod read-only → einen Hilfs-Pod mit Schreibzugriff nutzen
```
Danach in `src/content/site.ts` eintragen. Hotlinking fremder Bilder, etwa von Booking.com, ist nicht vorgesehen: Nur Bilder mit geklärter Nutzungsberechtigung ablegen.