Files
Artista-Filmcamp/k8s/base/app.yaml
T
Nick SzaunigandClaude Opus 5.5 95e3733ab8
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 2m19s
refactor: Kubernetes-Ressourcen und Images heissen artista-modelcamp
Alle Ressourcen tragen den Praefix artista-modelcamp- im Namespace
artista; Images <owner>/artista-modelcamp und
<owner>/artista-modelcamp-tools. DATABASE_URL-Host, Backup-Host,
Traefik-Middleware-Referenz und Betriebsdoku entsprechend angepasst.

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

130 lines
4.0 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 NICHT beim Start, sondern als eigener, kontrollierter
# Schritt (jobs.yaml, CronJob "artista-modelcamp-migrate", siehe docs/BETRIEB.md).
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
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.
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: artista-modelcamp-app
spec:
minAvailable: 1
selector:
matchLabels:
app.kubernetes.io/name: artista-modelcamp-app