Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
8.9 KiB
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:
mainergibt die Tagslatestundsha-<kurz>v0.1.0ergibt zusätzlich den Tag0.1.0
Einmalig einrichten:
- ein Personal Access Token mit Scope
packageals Repo-SecretREGISTRY_TOKENhinterlegen (Repo-Einstellungen → Actions → Secrets) - einen Runner mit dem Label
docker-builderbereitstellen (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:
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 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
- Overlay anpassen (
k8s/overlays/production/): Image-Namen und Tag, Domain inkustomization.yaml(APP_URL) undingress.yaml,ingressClassName(kubectl get ingressclass), ClusterIssuer (Beispielletsencrypt-prod) und TLS-Secret-Name. - Speicher:
- PostgreSQL bekommt Blockspeicher mit ReadWriteOnce (StorageClass im StatefulSet), auf keinen Fall eine SMB-Freigabe (Begründung in
k8s/base/postgres.yaml). - Medien und Sicherungen liegen auf SMB, über
k8s/overlays/production/smb-volumes.yaml:- csi-driver-smb installieren, Befehl steht im Kopf der Datei.
- Auf dem NAS die Ordner
modelcamp-mediaundmodelcamp-backupanlegen undsourcein der Datei anpassen. - Das 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.
- Die PVCs sind statisch an die PVs gebunden. Die Rechte setzen die
mountOptions:uid=1001für die App,uid=70für den Backup-Job, der mit0700bzw.0600geschützt ist, weil die Dumps personenbezogene Daten enthalten. Auf SMB wirktfsGroupnicht. - Ohne SMB:
smb-volumes.yamlund die beiden PVC-Patches im Overlay entfernen, dann greift die Default-StorageClass. Das Medienvolume braucht dann ReadWriteMany, sonstreplicas: 1.
- PostgreSQL bekommt Blockspeicher mit ReadWriteOnce (StorageClass im StatefulSet), auf keinen Fall eine SMB-Freigabe (Begründung in
- Secret anlegen: Befehl steht in
k8s/secret.example.yaml, besser über Sealed Secrets oder den External Secrets Operator. - Anwenden:
kubectl apply -k k8s/overlays/production - Migration (siehe Abschnitt 3), danach das erste Admin-Konto:
Das einmalige Passwort wird ausgegeben. Bei der ersten Anmeldung unter
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"}}]}]}}'/adminwird die 2FA eingerichtet.
3. Update ausrollen (kontrollierter Migrationsschritt)
# 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-modelcamp-migrate
kubectl -n artista wait --for=condition=complete job/$JOB --timeout=5m
# 3. Rollout beobachten
kubectl -n artista rollout status deploy/artista-modelcamp-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_…, livesk_live_…(nur mitAPP_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 alsSTRIPE_WEBHOOK_SECRETins 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_usageunterstü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-modelcamp-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-modelcamp-db-backuperstellt täglich um 3:15 Uhr einen Dump impg_dump-Custom-Format nachartista-modelcamp-db-backupund bewahrt ihn 30 Tage auf. - Aus dem Cluster sichern (regelmäßig):
POD=$(kubectl -n artista get pod -l app.kubernetes.io/name=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:
- App stoppen:
kubectl -n artista scale deploy/artista-modelcamp-app --replicas=0und den Worker-CronJob pausieren:kubectl -n artista patch cronjob artista-modelcamp-worker -p '{"spec":{"suspend":true}}' - Einspielen:
kubectl -n artista exec -i $POD -- pg_restore -U artista -d artista --clean --if-exists < artista-YYYY-MM-DD.dump - Migration laufen lassen (Abschnitt 3), App wieder hochskalieren, Worker fortsetzen.
- 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“).
- App stoppen:
- Medien: Das Volume
artista-modelcamp-mediaseparat 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.
- 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.
- 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. - 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.
- Dokumentation: Alle Schritte stehen im Protokoll der Buchung (AuditLog) mit handelnder Person.
9. Medien
Dateien (jpg, png, webp, avif, mp4, webm) direkt in den SMB-Ordner modelcamp-media legen, z. B. über den Windows-Explorer (\\nas…\artista\modelcamp-media). Die App sieht sie sofort unter /media/<dateiname>. Im Pod ist das Volume schreibgeschützt eingehängt.
Danach in src/content/site.ts eintragen. Hotlinking fremder Bilder, etwa von Booking.com, ist nicht vorgesehen: Nur Bilder mit geklärter Nutzungsberechtigung ablegen.