Files
Artista-Filmcamp/k8s/base/app.yaml
T
Nick SzaunigandClaude Opus 5.5 c84eb1e740
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 2m17s
refactor(k8s): schlanker Umfang, Postgres und Medien auf SMB
Auf Wunsch des Betreibers auf das Noetige reduziert: StatefulSet
PostgreSQL, Services, Ingress, App-Deployment, zwei SMB-PersistentVolumes
(Postgres-Daten, Medien). Beibehalten nach Absprache: Worker-CronJob
(Folgeraten, Reservierungsfreigabe, Mail-Wiederholung), ConfigMap und die
NetworkPolicies (Sicherheitsbefund Rate-Limit-Umgehung).

- Migration als initContainer im App-Deployment statt eigenem Job
- Entfernt: Backup-CronJob/-Volume (NAS sichert die Freigabe),
  Migrations-CronJob, PodDisruptionBudget, HTTP-Redirect-Ingress und
  Middleware, Namespace-Objekt (einmalig von Hand anzulegen)
- PostgreSQL auf SMB als bewusste, dokumentierte Ausnahme mit
  hard, cache=strict, SMB 3.1.1 + seal, uid 70 / 0700

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-27 20:36:30 +02:00

134 lines
4.3 KiB
YAML

# Next.js-Anwendung. Zustandslos: Buchungen, Kapazitaet, Gutscheine,
# Reservierungen, Rate Limits, E-Mail-Ausgang und Zahlungsjobs liegen in
# PostgreSQL — beliebig viele Replikate moeglich.
#
# Migrationen laufen als eigener Schritt VOR dem App-Container: initContainer
# "migrate" (Werkzeug-Image, `prisma migrate deploy`). Scheitert er, startet
# die App nicht. Mehrere Replikate gleichzeitig sind unkritisch — Prisma
# serialisiert Migrationen ueber eine Datenbank-Sperre.
apiVersion: apps/v1
kind: Deployment
metadata:
name: artista-modelcamp-app
spec:
replicas: 2
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
rollingUpdate: { maxSurge: 1, maxUnavailable: 0 }
selector:
matchLabels:
app.kubernetes.io/name: artista-modelcamp-app
template:
metadata:
labels:
app.kubernetes.io/name: artista-modelcamp-app
artista/db-client: "true"
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1001
runAsGroup: 1001
fsGroup: 1001
seccompProfile: { type: RuntimeDefault }
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: artista-modelcamp-app
initContainers:
- name: migrate
image: artista-modelcamp-tools
command: ["prisma", "migrate", "deploy"]
envFrom:
- secretRef: { name: artista-modelcamp-secrets }
resources:
requests: { cpu: 50m, memory: 128Mi }
limits: { memory: 512Mi }
securityContext:
allowPrivilegeEscalation: false
capabilities: { drop: ["ALL"] }
containers:
- name: app
image: artista-modelcamp
ports:
- name: http
containerPort: 3000
envFrom:
- configMapRef: { name: artista-modelcamp-config }
- secretRef: { name: artista-modelcamp-secrets }
readinessProbe:
httpGet: { path: /api/health/ready, port: http }
periodSeconds: 10
timeoutSeconds: 5
livenessProbe:
# Bewusst ohne Datenbank: eine DB-Stoerung soll nicht zu
# Neustartschleifen fuehren.
httpGet: { path: /api/health, port: http }
initialDelaySeconds: 20
periodSeconds: 20
timeoutSeconds: 5
failureThreshold: 3
resources:
requests: { cpu: 200m, memory: 256Mi }
limits: { memory: 768Mi }
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
volumeMounts:
- name: media
mountPath: /app/media
readOnly: true
# Reiner Zwischenspeicher fuer optimierte Bilder: fluechtig ok.
- name: next-cache
mountPath: /app/.next/cache
- name: tmp
mountPath: /tmp
volumes:
- name: media
persistentVolumeClaim: { claimName: artista-modelcamp-media }
- name: next-cache
emptyDir: { sizeLimit: 512Mi }
- name: tmp
emptyDir: { sizeLimit: 64Mi }
---
apiVersion: v1
kind: Service
metadata:
name: artista-modelcamp-app
spec:
selector:
app.kubernetes.io/name: artista-modelcamp-app
ports:
- name: http
port: 80
targetPort: http
---
# Die App nimmt Verbindungen nur vom Ingress-Controller an. Ohne diese Regel
# koennte jeder Pod im Cluster den Service direkt ansprechen und per
# gefaelschtem X-Real-Ip-Header die Rate Limits (inkl. Admin-Login/2FA)
# umgehen. Namespace des Traefik-Controllers anpassen
# ("kubectl get pods -A | grep traefik").
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: artista-modelcamp-app-ingress
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: artista-modelcamp-app
policyTypes: ["Ingress"]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: traefik
ports:
- port: 3000
# Kubelet-Probes kommen vom Knoten und sind von NetworkPolicies je nach
# CNI (Canal/Calico in RKE2) ausgenommen.