Files
Wabenhain/k8s/05-app-deployment.yaml
T
Nick SzaunigandClaude Sonnet 5 d0c1d0840c
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m29s
fix: Admin-Uploads auf dem Live-Server (Berechtigung, Persistenz, Body-Size-Limit)
Uploads (Bilder + Hero-Video) schlugen live fehl, lokal aber nicht: der
Container laeuft als non-root "nextjs", /app/uploads existierte nirgends
vorbereitet und gehoert root -> mkdir() scheiterte mit EACCES, unbehandelt
als HTML-500 statt JSON, der Client zeigte nur einen haengenden
"wird hochgeladen"-Zustand ohne Fehlermeldung.

- Dockerfile legt /app/uploads jetzt vor dem User-Wechsel an und
  uebereignet es dem nextjs-Nutzer
- docker-compose.yml persistiert Uploads jetzt ueber ein eigenes Volume
  (vorher gingen sie bei jedem Redeploy verloren) und setzt UPLOAD_DIR
  explizit
- Beide Upload-Routen fangen Dateisystemfehler jetzt ab und liefern eine
  echte JSON-Fehlermeldung statt eine unbehandelte Exception
- k8s-Manifeste vorsorglich mitgefixt (fsGroup fuer den Volume-Mount,
  Ingress-Body-Size-Limit) fuer den Fall eines spaeteren
  Kubernetes-Wechsels

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-11 15:48:14 +02:00

113 lines
4.6 KiB
YAML

# image: unten mit der tatsaechlichen Registry/Tag aus der Gitea-Actions-
# Pipeline ersetzen. Kein separater Migrations-Job mehr noetig: der
# Container fuehrt "prisma migrate deploy" + "prisma db seed" selbst aus,
# BEVOR er den Server startet (siehe Dockerfile-CMD in der runner-Stage) —
# beide Schritte sind idempotent, laufen also bei jedem Neustart gefahrlos
# erneut. Readiness-/Liveness-Probes greifen automatisch erst, sobald Port
# 3000 offen ist, also nach abgeschlossener Migration/Seed.
apiVersion: apps/v1
kind: Deployment
metadata:
name: wabenhain-app
namespace: wabenhain
spec:
# ACHTUNG bevor du hochskalierst: lib/rate-limit.ts haelt Login-/2FA-/
# Checkout-Limits aktuell im Prozessspeicher, nicht in einem gemeinsamen
# Store. Mit replicas > 1 verteilt sich derselbe Nutzer per Load-Balancing
# auf mehrere Prozesse mit je eigenem Zaehler — das Limit wird effektiv um
# den Skalierungsfaktor schwaecher (2 Replicas = 2x mehr Versuche moeglich),
# ohne dass das im Frontend sichtbar waere. Fuer replicas > 1 zuerst
# lib/rate-limit.ts auf einen gemeinsamen Store (z.B. Redis) umstellen.
replicas: 1
selector:
matchLabels:
app: wabenhain-app
template:
metadata:
labels:
app: wabenhain-app
spec:
# fsGroup=1001 muss zur "nodejs"-Gruppe (gid 1001) passen, die das
# Dockerfile fuer den non-root "nextjs"-Laufzeitbenutzer anlegt: ohne
# dieses Feld mountet Kubernetes das "uploads"-Volume (egal ob emptyDir
# oder ein echtes PVC) mit root:root-Besitz — das ueberschreibt den im
# Image bereits per chown gesetzten Besitzer von /app/uploads
# vollstaendig, und der nicht-root laufende Container koennte trotzdem
# keine Datei dort anlegen (EACCES bei jedem Admin-Upload). fsGroup
# sorgt dafuer, dass Kubernetes JEDES gemountete Volume zusaetzlich der
# Gruppe 1001 zuordnet und gruppen-schreibbar macht.
securityContext:
fsGroup: 1001
containers:
- name: app
image: git.encry.de/nick.rosenloecher/wabenhain:latest
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: wabenhain-config
- secretRef:
name: wabenhain-secrets
volumeMounts:
# Muss auf denselben Pfad zeigen wie UPLOAD_DIR in
# 01-configmap.yaml. Das "uploads"-Volume selbst wird HIER
# bewusst nicht definiert (kein PV/PVC in diesem Manifest) — das
# haengt vom tatsaechlichen Storage-Backend ab (SMB/NFS/
# hostPath/...), das die Betreiberin bereitstellt. Unten im
# "volumes:"-Block einen passenden Eintrag mit demselben Namen
# ("uploads") ergaenzen, z.B. ueber eine
# persistentVolumeClaim.claimName, sonst startet der Pod nicht
# (fehlende Volume-Referenz).
- name: uploads
mountPath: /app/uploads
readinessProbe:
httpGet:
path: /
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 3000
# initialDelaySeconds/failureThreshold grosszuegiger als sonst
# ueblich: der Container migriert/seedet die DB selbst vor dem
# Serverstart (siehe Kommentar oben) — genug Spielraum, damit ein
# laengerer erster Migrationslauf nicht faelschlich als haengender
# Prozess gewertet und der Container neu gestartet wird.
initialDelaySeconds: 30
periodSeconds: 30
failureThreshold: 5
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 512Mi
volumes:
# Platzhalter — die Betreiberin ergaenzt hier ihre eigene
# Volume-Quelle fuer den Namen "uploads" (z.B. ein PVC, das gegen
# ein SMB-/NFS-Backend oder einen hostPath gebunden ist), etwa:
# - name: uploads
# persistentVolumeClaim:
# claimName: wabenhain-uploads-pvc
- name: uploads
# CHANGE-ME: obige emptyDir-Zeile durch die echte Volume-Quelle
# ersetzen. emptyDir ist NICHT persistent (Inhalt geht bei jedem
# Pod-Neustart verloren) und dient hier nur als Platzhalter, damit
# dieses Manifest ohne weitere Aenderung "kubectl apply"-faehig
# bleibt.
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: wabenhain-app
namespace: wabenhain
spec:
selector:
app: wabenhain-app
ports:
- port: 80
targetPort: 3000