diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index a1aa79c..7cd4db7 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -105,7 +105,7 @@ Fachliche Annahmen, getroffen weil das Lastenheft an der Stelle keine eindeutige ## 8. Backup & Restore -**Schema-Migrationen (Reproduzierbarkeit):** Ausschließlich `prisma/migrations/` als Quelle der Wahrheit. Lokal/Entwicklung: `npx prisma migrate dev`. Gegen jede Produktions- oder Staging-Datenbank ausschließlich `npx prisma migrate deploy` (idempotent, wendet nur fehlende Migrationen an, kein Schema-Drift). `prisma db push` wird an keiner Stelle im Code, in Skripten oder im Dockerfile verwendet und ist für Produktions-/Staging-Umgebungen untersagt. `migrate deploy` läuft als eigener Schritt vor dem Rollout, nicht im `CMD` des Runtime-Images, damit mehrere Pod-Replicas nicht gleichzeitig Migrationen ausführen: `deploy/k8s/backend/migrate-job.yaml` ist ein Kubernetes-`Job`, der dasselbe Backend-Image mit überschriebenem `command` (`npm run migrate:deploy`) nutzt – kein separates Migrations-Image. Reihenfolge und `kubectl`-Befehle stehen als Kommentar in der Datei. +**Schema-Migrationen (Reproduzierbarkeit):** Ausschließlich `prisma/migrations/` als Quelle der Wahrheit. Lokal/Entwicklung: `npx prisma migrate dev`. Gegen jede Produktions- oder Staging-Datenbank ausschließlich `npx prisma migrate deploy` (idempotent, wendet nur fehlende Migrationen an, kein Schema-Drift). `prisma db push` wird an keiner Stelle im Code, in Skripten oder im Dockerfile verwendet und ist für Produktions-/Staging-Umgebungen untersagt. Der Backend-Container führt `migrate deploy` selbst beim Start aus, bevor der Server Verbindungen annimmt (Dockerfile-`CMD`: `npm run migrate:deploy && node dist/main.js`) – kein separater Kubernetes-`Job`. Bei `replicas: 2` starten mehrere Pods potenziell gleichzeitig; das ist unproblematisch, da Prisma für `migrate deploy` einen datenbankseitigen Advisory-Lock nutzt – eine Instanz wendet die Migrationen an, die anderen warten kurz und finden danach nichts mehr zu tun. `deploy/k8s/backend/deployment.yaml` hat dafür eine `startupProbe` mit großzügigem `failureThreshold`, damit liveness/readiness den Container nicht neu starten, während die (beim allerersten Deploy ggf. länger laufende) Migration noch durchläuft. **Datensicherung:** Seit der Entscheidung, PostgreSQL selbst im Cluster zu betreiben (siehe CLAUDE.md), liegt die Backup-Verantwortung bei diesem Projekt, nicht mehr bei einem externen Cluster-/DBA-Betrieb. Aktuell **noch nicht umgesetzt** (offener Punkt, siehe TASKS.md) – empfohlene Strategie: diff --git a/ApiCore-Backend/Dockerfile b/ApiCore-Backend/Dockerfile index ebb83e9..554eafc 100644 --- a/ApiCore-Backend/Dockerfile +++ b/ApiCore-Backend/Dockerfile @@ -16,11 +16,14 @@ RUN npm ci --omit=dev COPY --from=builder /app/dist ./dist COPY --from=builder /app/generated ./generated # schema.prisma, migrations/ und prisma.config.ts: noetig fuer -# `npm run migrate:deploy` (siehe deploy/k8s/backend/migrate-job.yaml), das -# dasselbe Image mit ueberschriebenem command nutzt statt eines eigenen -# Migrations-Images. +# `npm run migrate:deploy`, das der Container beim Start selbst ausfuehrt +# (siehe CMD unten), bevor der Server Verbindungen annimmt. COPY --from=builder /app/prisma ./prisma COPY --from=builder /app/prisma.config.ts ./prisma.config.ts EXPOSE 3001 USER node -CMD ["node", "dist/main.js"] +# migrate deploy ist idempotent und nutzt einen DB-seitigen Advisory-Lock, +# daher unproblematisch bei mehreren gleichzeitig startenden Pod-Replicas: +# eine Instanz wendet die Migrationen an, die anderen warten kurz und finden +# danach nichts mehr zu tun. +CMD ["sh", "-c", "npm run migrate:deploy && node dist/main.js"] diff --git a/deploy/k8s/backend/deployment.yaml b/deploy/k8s/backend/deployment.yaml index 670a2e5..e54b4b6 100644 --- a/deploy/k8s/backend/deployment.yaml +++ b/deploy/k8s/backend/deployment.yaml @@ -24,6 +24,16 @@ spec: name: apicore-backend-config - secretRef: name: apicore-backend-secret + # Container fuehrt beim Start selbst `prisma migrate deploy` aus, + # bevor der Server Verbindungen annimmt (siehe Dockerfile CMD) - + # startupProbe gibt dafuer bis zu 5 Minuten Zeit, ohne dass + # liveness/readiness in der Zwischenzeit einen Neustart ausloesen. + startupProbe: + httpGet: + path: /health/ready + port: 3001 + failureThreshold: 30 + periodSeconds: 10 livenessProbe: httpGet: path: /health diff --git a/deploy/k8s/backend/migrate-job.yaml b/deploy/k8s/backend/migrate-job.yaml deleted file mode 100644 index 40927a5..0000000 --- a/deploy/k8s/backend/migrate-job.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# Job statt Teil des Deployment-CMD: laeuft einmalig vor dem Rollout, damit -# nicht mehrere Backend-Pod-Replicas gleichzeitig Migrationen ausfuehren -# (siehe ARCHITECTURE.md Abschnitt 8). Jobs sind unveraenderlich, daher vor -# jedem Deploy mit neuem Image-Tag zuerst loeschen: -# -# kubectl delete job apicore-backend-migrate --ignore-not-found -# kubectl apply -f deploy/k8s/backend/migrate-job.yaml -# kubectl wait --for=condition=complete job/apicore-backend-migrate --timeout=120s -# kubectl apply -f deploy/k8s/backend/deployment.yaml -apiVersion: batch/v1 -kind: Job -metadata: - name: apicore-backend-migrate -spec: - backoffLimit: 2 - ttlSecondsAfterFinished: 3600 - template: - spec: - restartPolicy: Never - containers: - - name: migrate - image: git.encry.de/nick.rosenloecher/apicore-backend:latest - command: ["npm", "run", "migrate:deploy"] - envFrom: - - secretRef: - name: apicore-backend-secret