README mit Projektstand (Links auf Issues #33, #32), fehlenden Befehlen und CI-Gate. BETRIEB: sha-gepinnte Manifeste, DB-Sicherung, Rollout per set image, Label-Selector im pg_dump-Befehl, Storage Box. k8s/README: Schritt 09, Warnung vor delete namespace. TESTS, OFFENE-PUNKTE, ARCHITEKTUR (ADR-8, ADR-9) aktualisiert; Kommentare in .env.example und Worker-CronJob korrigiert. Refs #31 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
ARTISTA Modelcamp in Kubernetes (RKE2)
Rohe Manifeste wie bei Wabenhain, Nummerierung = Reihenfolge. Voraussetzungen im Cluster (wie bei Wabenhain):
- Traefik als Ingress-Controller (IngressClass
traefik) - cert-manager mit dem ClusterIssuer
letsencrypt-prod - csi-driver-smb für die SMB-Volumes
- die StorageClass
smb-backupfür den Sicherungs-CronJob (09-db-backup-cronjob.yaml); im EnCry-Cluster seit 12.10.2026 vorhanden
Prüfen:
kubectl get ingressclass
kubectl get clusterissuer letsencrypt-prod
kubectl get csidrivers | grep smb
kubectl get storageclass smb-backup
Im EnCry-Cluster läuft die Seite unter modelcamp.artistasphotography.de. Der DNS-Eintrag ist bei Cloudflare bewusst nicht geproxyt (DNS-only), deshalb bekommt dieser Ingress keine cloudflare-only-Middleware. Im selben Namespace läuft Artista-Telefonie (Repo Artista-TeleTracking) mit einer eigenen Datenbank telefonie in artista-modelcamp-postgres.
Einmalige Vorbereitung
- Images:
.gitea/workflows/docker-build.ymlbaut bei jedem Push aufmaindie Imagesgit.encry.de/nick.rosenloecher/artista-modelcamp:latestund…-tools:latest. Voraussetzung ist das Repo-SecretREGISTRY_TOKEN(Personal Access Token mitpackage-Scope), wie bei Wabenhain. - csi-driver-smb (falls noch nicht vorhanden):
helm repo add csi-driver-smb https://raw.githubusercontent.com/kubernetes-csi/csi-driver-smb/master/charts helm install csi-driver-smb csi-driver-smb/csi-driver-smb -n kube-system - NAS: Die Ordner
artista/modelcamp-postgres(leer) undartista/modelcamp-mediaanlegen und SMB-Verschlüsselung erlauben.nas.example.lanin03-storage.yamlanpassen. - Domain:
modelcamp.example.orgin01-configmap.yaml(APP_URL) und06-ingress.yaml(2×) ersetzen (im EnCry-Clustermodelcamp.artistasphotography.de). Der DNS-Eintrag muss auf den Cluster zeigen, sonst stellt cert-manager kein Zertifikat aus. - Secrets:
cp k8s/02-secret.yaml.example k8s/02-secret.yamlund ausfüllen. Die Datei steht in.gitignore, bitte nie mit echten Werten committen. - Image-Tag: In
05-app-deployment.yaml(2×) und07-worker-cronjob.yamlsteht der Platzhaltersha-REPLACE_ME. Vor dem ersten Anwenden durch den Tag des letzten grünen CI-Laufs ersetzen (sha-<commit>, 7 Zeichen) oder nach dem Anwenden wie unter „Bei einem neuen Image“ setzen.
Deployment (auch für einen kompletten Neuaufbau)
kubectl apply -f k8s/00-namespace.yaml
kubectl apply -f k8s/01-configmap.yaml
kubectl apply -f k8s/02-secret.yaml
kubectl apply -f k8s/03-storage.yaml
kubectl apply -f k8s/04-postgres.yaml
kubectl wait --for=condition=ready pod -l app=artista-modelcamp-postgres -n artista --timeout=180s
kubectl apply -f k8s/05-app-deployment.yaml
kubectl apply -f k8s/06-ingress.yaml
kubectl apply -f k8s/07-worker-cronjob.yaml
kubectl apply -f k8s/08-networkpolicy.yaml
kubectl apply -f k8s/09-db-backup-cronjob.yaml
Die Migration läuft automatisch als initContainer vor dem App-Start. Den Stand zeigen:
kubectl rollout status deployment/artista-modelcamp-app -n artista
kubectl get certificate -n artista # READY muss True werden (meist < 2 Minuten)
Neuaufbau mit kubectl delete namespace artista: Achtung, das löscht auch Artista-Telefonie samt deren Secret; deren Datenbank liegt auf demselben Postgres-Volume. Die PersistentVolumes (artista-modelcamp-postgres, artista-modelcamp-media) gehören keinem Namespace und bleiben auf Released stehen. Die Daten auf dem NAS bleiben erhalten (Retain). Damit die neuen Claims sie wieder binden können, vor dem erneuten Anlegen:
kubectl patch pv artista-modelcamp-postgres -p '{"spec":{"claimRef":null}}'
kubectl patch pv artista-modelcamp-media -p '{"spec":{"claimRef":null}}'
Soll die Datenbank wirklich leer neu starten, den Ordner modelcamp-postgres auf dem NAS vorher leeren.
Erstes Admin-Konto
E-Mail und Name ersetzen. Das einmalige Passwort wird ausgegeben, die 2FA richtest du bei der ersten Anmeldung unter /admin ein.
kubectl -n artista run admin-create --rm -i --restart=Never \
--image=git.encry.de/nick.rosenloecher/artista-modelcamp-tools:latest --labels=db-client=true \
--overrides='{"spec":{"securityContext":{"runAsNonRoot":true,"runAsUser":1001,"seccompProfile":{"type":"RuntimeDefault"}},"containers":[{"name":"admin-create","image":"git.encry.de/nick.rosenloecher/artista-modelcamp-tools:latest","command":["tsx","scripts/create-admin.ts","--email","[email protected]","--name","Dein Name"],"envFrom":[{"configMapRef":{"name":"artista-modelcamp-config"}},{"secretRef":{"name":"artista-modelcamp-secrets"}}],"securityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]}}}]}}'
Bei einem neuen Image (Redeploy)
Die Manifeste sind auf einen Build gepinnt, nicht auf :latest. Den Tag sha-<commit> (7 Zeichen) zeigt der CI-Lauf in der Zusammenfassung. App und Werkzeuge immer mit demselben Tag ausrollen:
TAG=sha-<commit>
kubectl -n artista set image deployment/artista-modelcamp-app \
migrate=git.encry.de/nick.rosenloecher/artista-modelcamp-tools:$TAG \
app=git.encry.de/nick.rosenloecher/artista-modelcamp:$TAG
kubectl -n artista set image cronjob/artista-modelcamp-worker \
worker=git.encry.de/nick.rosenloecher/artista-modelcamp-tools:$TAG
kubectl -n artista rollout status deployment/artista-modelcamp-app
Zurueck auf den vorherigen Stand: kubectl -n artista rollout undo deployment/artista-modelcamp-app (den CronJob dann wieder auf den alten Tag setzen).
Fehlersuche
kubectl -n artista get certificate,certificaterequest,order,challenge # Zertifikat
kubectl -n artista describe challenge
kubectl -n artista logs deploy/artista-modelcamp-app -c migrate # Migration
kubectl -n artista logs deploy/artista-modelcamp-app -c app # App
kubectl -n artista get events --sort-by=.lastTimestamp | tail -20