# 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-backup`** für den Sicherungs-CronJob (`09-db-backup-cronjob.yaml`); im EnCry-Cluster seit 12.10.2026 vorhanden Prüfen: ```bash 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 1. **Images:** `.gitea/workflows/docker-build.yml` baut bei jedem Push auf `main` die Images `git.encry.de/nick.rosenloecher/artista-modelcamp:latest` und `…-tools:latest`. Voraussetzung ist das Repo-Secret `REGISTRY_TOKEN` (Personal Access Token mit `package`-Scope), wie bei Wabenhain. 2. **csi-driver-smb** (falls noch nicht vorhanden): ```bash 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 ``` 3. **NAS:** Die Ordner `artista/modelcamp-postgres` (leer) und `artista/modelcamp-media` anlegen und SMB-Verschlüsselung erlauben. `nas.example.lan` in `03-storage.yaml` anpassen. 4. **Domain:** `modelcamp.example.org` in `01-configmap.yaml` (APP_URL) und `06-ingress.yaml` (2×) ersetzen (im EnCry-Cluster `modelcamp.artistasphotography.de`). Der DNS-Eintrag muss auf den Cluster zeigen, sonst stellt cert-manager kein Zertifikat aus. 5. **Secrets:** `cp k8s/02-secret.yaml.example k8s/02-secret.yaml` und ausfüllen. Die Datei steht in `.gitignore`, bitte **nie** mit echten Werten committen. 6. **Image-Tag:** In `05-app-deployment.yaml` (2×) und `07-worker-cronjob.yaml` steht der Platzhalter `sha-REPLACE_ME`. Vor dem ersten Anwenden durch den Tag des letzten grünen CI-Laufs ersetzen (`sha-`, 7 Zeichen) oder nach dem Anwenden wie unter „Bei einem neuen Image“ setzen. ## Deployment (auch für einen kompletten Neuaufbau) ```bash 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: ```bash 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: ```bash 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. ```bash 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","du@example.org","--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-` (7 Zeichen) zeigt der CI-Lauf in der Zusammenfassung. App und Werkzeuge immer mit demselben Tag ausrollen: ```bash TAG=sha- 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 ```bash 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 ```