fix: hochgeladene Bilder ueberleben einen Redeploy

Das uploads-Volume im App-Deployment war ein emptyDir-Platzhalter. Der ist
an die Lebensdauer des Pods gebunden: bei jedem Neustart, jedem Redeploy
und jeder Node-Verlagerung waren saemtliche hochgeladenen Produkt-,
Ernten- und Inhaltsfotos weg. Die Datenbank behaelt dabei ihre Bildpfade —
die Seite haette danach ueberall kaputte Bilder gezeigt, ohne dass
irgendwo ein Fehler aufgetaucht waere.

Neu: k8s/04-uploads-pvc.yaml (die Nummer war in der Reihenfolge noch frei)
mit einem PersistentVolumeClaim, den das Deployment jetzt bindet.
storageClassName ist bewusst auskommentiert, damit das Manifest ohne
Aenderung gegen die Default-StorageClass des Clusters anwendbar bleibt.

Damit einher geht zwingend "strategy: Recreate": Kubernetes' Standard
"RollingUpdate" startet den neuen Pod, BEVOR der alte beendet ist. Ein
ReadWriteOnce-Claim kann aber nur an einen Pod gebunden sein — der neue
Pod bliebe mit "Multi-Attach error for volume" in ContainerCreating
haengen, und das im k8s/README.md dokumentierte
"kubectl rollout restart" liefe schlicht in den Timeout. Recreate kostet
wenige Sekunden Ausfall pro Redeploy und sorgt nebenbei dafuer, dass nie
zwei Pods gleichzeitig laufen, die sich sonst die im Prozessspeicher
gehaltenen Rate-Limits teilen wuerden.

Alle Manifeste gegen einen YAML-Parser geprueft, zusaetzlich strukturell:
claimName trifft den PVC-Namen, PVC liegt im selben Namespace, und der
mountPath stimmt mit UPLOAD_DIR aus 01-configmap.yaml ueberein.

preflight: NEXT_PUBLIC_SITE_URL wird jetzt auch gegen "example.com"
geprueft. Die ausgelieferte Vorgabe ist "shop.example.com" — blieb sie
stehen, zeigten Sitemap, Canonicals und jeder Link in jeder
Bestellbestaetigung auf eine fremde Domain, ohne dass die bisherigen
HTTPS-/localhost-Pruefungen angeschlagen haetten.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5 committed 2026-09-15 10:13:06 +02:00
1 parent 91aee0d609
commit a78c1cd235
4 files changed
+93 -13

No files matched your search

+20 -1
View File
@@ -42,7 +42,13 @@ anwendbar. Nummerierung = empfohlene Reihenfolge.
4. `01-configmap.yaml`: `DATABASE_PROVIDER` (postgresql/mysql) und
`NEXT_PUBLIC_SITE_URL` (echte Domain) anpassen.
5. `image:` in `05-app-deployment.yaml` zeigt bereits auf
5. `04-uploads-pvc.yaml`: `storageClassName` auf eine im Cluster
vorhandene StorageClass setzen (`kubectl get storageclass`; die Zeile ist
auskommentiert, dann greift die als `(default)` markierte) und die
Groesse pruefen. Ohne diesen Claim gibt es keinen dauerhaften Speicher
fuer Produkt- und Erntenfotos.
6. `image:` in `05-app-deployment.yaml` zeigt bereits auf
`git.encry.de/nick.rosenloecher/wabenhain` — nur noch den Tag pruefen
(`:latest` passt fuer den jeweils neuesten `main`-Build). In
`06-ingress.yaml` `shop.example.com` durch die echte Domain ersetzen
@@ -61,6 +67,10 @@ kubectl apply -f k8s/03-postgres.yaml
kubectl wait --for=condition=ready pod -l app=wabenhain-postgres -n wabenhain --timeout=120s
# Persistenter Speicher fuer hochgeladene Bilder — MUSS vor dem Deployment
# angewendet werden, sonst bleibt der App-Pod in "Pending" haengen.
kubectl apply -f k8s/04-uploads-pvc.yaml
kubectl apply -f k8s/05-app-deployment.yaml
kubectl apply -f k8s/06-ingress.yaml
```
@@ -79,6 +89,15 @@ kubectl rollout restart deployment/wabenhain-app -n wabenhain
Migrationen + Seed laufen automatisch erneut beim Start jedes neuen Pods
(idempotent, siehe oben).
Das Deployment nutzt dabei die Strategie `Recreate`, nicht das
Kubernetes-Standardverhalten `RollingUpdate`: der alte Pod wird zuerst
beendet, dann der neue gestartet. Das ist mit dem uploads-Volume zwingend —
ein `ReadWriteOnce`-Claim kann immer nur an einen Pod gebunden sein, ein
parallel hochfahrender neuer Pod bliebe mit
`Multi-Attach error for volume` in `ContainerCreating` haengen und der
Rollout liefe in den Timeout. Der Preis sind wenige Sekunden Ausfall pro
Redeploy.
## Bekannte Grenzen dieser Manifeste
- **`replicas: 1`** in `05-app-deployment.yaml`, bewusst nicht mehr: das