Files
Wabenhain/k8s/README.md
T
Nick Szaunig 04da561380
Docker Image bauen und veroeffentlichen / build-and-push (push) Failing after 3m2s
fix: Gitea-Registry-URL nicht aus gitea.server_url ableiten
gitea.server_url ist in manchen Gitea-Versionen leer (go-gitea/gitea#23276)
und liefert selbst im Erfolgsfall eine volle URL mit https://-Schema statt
des fuer docker login/Image-Tags benoetigten reinen Hostnamens. Registry
jetzt fest als git.encry.de hinterlegt, k8s-Manifeste entsprechend
vorausgefuellt.
2026-08-18 01:36:03 +02:00

4.5 KiB

Wabenhain in Kubernetes

Rohe Manifeste, mit kubectl apply -f k8s/<datei> einzeln oder als Ordner anwendbar. Nummerierung = empfohlene Reihenfolge.

Einmalige Vorbereitung

  1. Gitea Actions: .gitea/workflows/docker-build.yml baut bei jedem Push auf main zwei Image-Tags in der Gitea-Container-Registry:

    • <registry>/<owner>/wabenhain:latest (+ :sha-<kurzhash>, :vX.Y.Z bei Tags) — das schlanke Laufzeit-Image fuer 05-app-deployment.yaml.
    • <registry>/<owner>/wabenhain:migrate-<sha> — enthaelt zusaetzlich die Prisma-CLI/tsx, fuer 04-migrate-job.yaml.

    Voraussetzung: ein Repo-/Org-Secret REGISTRY_TOKEN in Gitea anlegen (Personal Access Token mit package-Scope, unter Benutzereinstellungen → Anwendungen → Access Tokens erzeugen, dann unter Repo-Einstellungen → Actions → Secrets als REGISTRY_TOKEN hinterlegen). Der automatisch von Gitea Actions bereitgestellte GITEA_TOKEN reicht dafuer aktuell NICHT aus (fehlendes Package-Write-Recht, Stand heute).

  2. Ein Docker-Image ist Provider-uebergreifend: Prisma generiert den Client Provider-spezifisch — ein .next/standalone-Build kann seinen generierten Client nicht zur Laufzeit wechseln. Deshalb generiert scripts/generate-prisma-clients.mjs (Teil von pnpm build, laeuft also automatisch bei jedem Image-Build) BEIDE Varianten ins selbe Image; die Wahl zwischen ihnen passiert zur Laufzeit ueber DATABASE_PROVIDER, siehe src/lib/prisma.ts. Ein Image reicht also fuer Postgres UND MySQL — nur DATABASE_PROVIDER/DATABASE_URL in 01-configmap.yaml/ 02-secret.yaml muessen zur tatsaechlich gewaehlten Datenbank passen.

  3. k8s/02-secret.example.yaml nach k8s/02-secret.yaml kopieren (per .gitignore ausgeschlossen — NIE mit echten Werten committen) und ausfuellen. Alternativ direkt per kubectl create secret, siehe Kommentar in der Beispieldatei.

  4. 01-configmap.yaml: DATABASE_PROVIDER (postgresql/mysql) und NEXT_PUBLIC_SITE_URL (echte Domain) anpassen.

  5. image: in 04-migrate-job.yaml/05-app-deployment.yaml zeigt bereits auf git.encry.de/nick.rosenloecher/wabenhain — nur noch den Tag pruefen (:latest in 05-app-deployment.yaml passt fuer den jeweils neuesten main-Build; :migrate-REPLACE-ME in 04-migrate-job.yaml durch den echten Commit-Kurzhash aus dem Gitea-Actions-Lauf ersetzen, sichtbar unter Repo → Actions → Workflow-Run). In 06-ingress.yaml shop.example.com durch die echte Domain ersetzen (zweimal: host: und tls.hosts).

Deployment

kubectl apply -f k8s/00-namespace.yaml
kubectl apply -f k8s/01-configmap.yaml
kubectl apply -f k8s/02-secret.yaml

# Nur EINE der beiden Datenbanken anwenden, passend zu DATABASE_PROVIDER:
kubectl apply -f k8s/03-postgres.yaml
# kubectl apply -f k8s/03-mysql.yaml

kubectl wait --for=condition=ready pod -l app=wabenhain-postgres -n wabenhain --timeout=120s

# Migrationen + Seed (Admin-Konto, Beispieldaten) — vor jedem neuen
# Image-Tag erneut ausfuehren:
kubectl delete job wabenhain-migrate -n wabenhain --ignore-not-found
kubectl apply -f k8s/04-migrate-job.yaml
kubectl wait --for=condition=complete job/wabenhain-migrate -n wabenhain --timeout=120s

kubectl apply -f k8s/05-app-deployment.yaml
kubectl apply -f k8s/06-ingress.yaml

Bei einem neuen Image (Redeploy)

kubectl delete job wabenhain-migrate -n wabenhain --ignore-not-found
kubectl apply -f k8s/04-migrate-job.yaml
kubectl wait --for=condition=complete job/wabenhain-migrate -n wabenhain --timeout=120s
kubectl rollout restart deployment/wabenhain-app -n wabenhain

Bekannte Grenzen dieser Manifeste

  • replicas: 1 in 05-app-deployment.yaml, bewusst nicht mehr: das Login-/2FA-/Checkout-Rate-Limiting (lib/rate-limit.ts) haelt seinen Zustand im Prozessspeicher, nicht in einem gemeinsamen Store. Mehrere Replicas wuerden das Limit unbemerkt proportional aufweichen. Fuer horizontale Skalierung zuerst lib/rate-limit.ts auf Redis o.ae. umstellen.
  • Kein HorizontalPodAutoscaler, kein PodDisruptionBudget, keine NetworkPolicy — je nach Cluster/Anforderungen ergaenzen.
  • Die Postgres-/MySQL-StatefulSets sind Single-Instance ohne Replikation/ Backup-Automation. Fuer produktiven Betrieb einen Managed-Datenbank-Dienst oder einen dedizierten DB-Operator (z.B. CloudNativePG) in Erwaegung ziehen — DATABASE_URL in 02-secret.yaml einfach auf den externen Endpunkt zeigen lassen, 03-postgres.yaml/03-mysql.yaml dann nicht anwenden.