# PostgreSQL wie im Cluster (uebernommen 2026-10-13 per kubectl get, nur # lesend). Selektor und Volumes eines StatefulSets sind unveraenderlich, und # das Repo darf die Live-Haertung nie zuruecknehmen: Aenderungen hier immer # erst gegen `kubectl -n apicore get sts apicore-postgres -o yaml` pruefen. apiVersion: apps/v1 kind: StatefulSet metadata: name: apicore-postgres labels: app.kubernetes.io/name: apicore-postgres spec: serviceName: apicore-postgres replicas: 1 persistentVolumeClaimRetentionPolicy: whenDeleted: Retain whenScaled: Retain selector: matchLabels: app.kubernetes.io/name: apicore-postgres template: metadata: labels: app.kubernetes.io/name: apicore-postgres encry.de/tier: database spec: automountServiceAccountToken: false enableServiceLinks: false terminationGracePeriodSeconds: 120 securityContext: seccompProfile: type: RuntimeDefault affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 preference: matchExpressions: - key: media-gpu operator: NotIn values: ["true"] - weight: 50 preference: matchExpressions: - key: kubernetes.io/hostname operator: In values: [rkem01] topologySpreadConstraints: - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: encry.de/tier: database volumes: # Statisches Volume (data-pvc.yaml), keine volumeClaimTemplates. - name: data persistentVolumeClaim: claimName: apicore-postgres-data # Root-Dateisystem ist nur lesbar: Socket, /tmp und Shared Memory # liegen in begrenzten emptyDirs. - name: postgres-runtime emptyDir: medium: Memory sizeLimit: 16Mi - name: tmp emptyDir: sizeLimit: 64Mi - name: dshm emptyDir: medium: Memory sizeLimit: 256Mi containers: - name: postgres image: postgres:18.6-alpine imagePullPolicy: IfNotPresent ports: - name: postgres containerPort: 5432 envFrom: - secretRef: name: apicore-postgres-secret env: # Unterordner statt Mount-Root (Layout von Postgres 18): initdb # verlangt ein leeres Verzeichnis, der Mount-Root ist es nicht immer. - name: PGDATA value: /var/lib/postgresql/18/docker securityContext: # uid/gid 70 = postgres im Alpine-Image, passend zum SMB-Mount # (uid=70,gid=70) des Volumes. runAsUser: 70 runAsGroup: 70 runAsNonRoot: true readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: ["ALL"] volumeMounts: - name: data mountPath: /var/lib/postgresql - name: postgres-runtime mountPath: /var/run/postgresql - name: tmp mountPath: /tmp - name: dshm mountPath: /dev/shm # Bis zu 10 Minuten Start (Recovery nach Absturz), danach liveness # mit viel Geduld: ein Neustart mitten in einer Recovery schadet mehr. startupProbe: exec: command: ["/bin/sh", "-ec", 'exec pg_isready -q -h 127.0.0.1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"'] failureThreshold: 60 periodSeconds: 10 timeoutSeconds: 5 livenessProbe: exec: command: ["/bin/sh", "-ec", 'exec pg_isready -q -h 127.0.0.1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"'] failureThreshold: 10 periodSeconds: 20 timeoutSeconds: 10 readinessProbe: exec: command: ["/bin/sh", "-ec", 'exec pg_isready -q -h 127.0.0.1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"'] failureThreshold: 3 periodSeconds: 10 timeoutSeconds: 5 resources: requests: cpu: 30m memory: 128Mi limits: cpu: "1" memory: 1Gi