refactor(k8s): Aufbau wie Wabenhain/ApiCore, Zertifikat funktionsfaehig
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 3m2s

- Dateinamen nach Wabenhain-Schema (01-configmap, 02-secret,
  03-storage, 04-postgres, 05-app-deployment, 06-ingress,
  07-worker-cronjob, 08-networkpolicy); Secret-Vorlage als
  02-secret.yaml.example, echte 02-secret.yaml gitignored.
- Namespace: Pod Security "baseline" erzwungen, "restricted" als warn/audit.
  "restricted" kann den HTTP-01-Solver-Pod von cert-manager abweisen und
  damit die Zertifikatsausstellung blockieren.
- NetworkPolicy nur noch fuer PostgreSQL; keine Policy vor der App (wie
  Wabenhain/ApiCore), da sie bei Traefik im Host-Netzwerk die Seite sperrt.
- README: kompletter Neuaufbau inkl. Freigabe der Retain-PVs nach
  "kubectl delete namespace", Zertifikats-Diagnose.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-09-28 11:26:13 +02:00
1 parent d813d56514
commit a0ba921d19
12 files changed
+68 -69

No files matched your search

+2 -2
View File
@@ -24,5 +24,5 @@ npm-debug.log*
# Laufzeit-Cache des Design-Hooks (impeccable) # Laufzeit-Cache des Design-Hooks (impeccable)
.impeccable/hook.cache.json .impeccable/hook.cache.json
# Echte Kubernetes-Secrets (Vorlage: k8s/secrets.yaml.example) # Echte Kubernetes-Secrets (Vorlage: k8s/02-secret.yaml.example)
k8s/secrets.yaml k8s/02-secret.yaml
+8 -1
View File
@@ -1,6 +1,13 @@
# Pod Security: "baseline" wird erzwungen (blockiert privilegierte Pods,
# hostPath, Host-Netzwerk usw.), laesst aber den HTTP-01-Solver-Pod von
# cert-manager zu. "restricted" wird gemeldet (warn/audit); die eigenen Pods
# dieses Namespaces erfuellen es bereits (non-root, keine Capabilities,
# seccomp RuntimeDefault).
apiVersion: v1 apiVersion: v1
kind: Namespace kind: Namespace
metadata: metadata:
name: artista name: artista
labels: labels:
pod-security.kubernetes.io/enforce: restricted pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/audit: restricted
File renamed without changes.
@@ -1,5 +1,5 @@
# Kopieren nach k8s/secrets.yaml (ist in .gitignore), Werte eintragen, # Kopieren nach k8s/02-secret.yaml (ist in .gitignore), Werte eintragen,
# dann: kubectl apply -f k8s/secrets.yaml # dann: kubectl apply -f k8s/02-secret.yaml
# Zufallswerte: openssl rand -base64 48 # Zufallswerte: openssl rand -base64 48
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
@@ -17,7 +17,7 @@ stringData:
SMTP_USER: "benutzer" SMTP_USER: "benutzer"
SMTP_PASSWORD: "passwort" SMTP_PASSWORD: "passwort"
--- ---
# Zugang zur SMB-Freigabe (fuer 02-storage.yaml) # Zugang zur SMB-Freigabe (fuer 03-storage.yaml)
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1 -1
View File
@@ -1,5 +1,5 @@
# Ingress fuer Traefik + cert-manager (gleiches Muster wie Wabenhain). # Ingress fuer Traefik + cert-manager (gleiches Muster wie Wabenhain).
# ANPASSEN: Domain (2x, auch APP_URL in 01-config.yaml). # ANPASSEN: Domain (2x, auch APP_URL in 01-configmap.yaml).
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: Ingress kind: Ingress
metadata: metadata:
-47
View File
@@ -1,47 +0,0 @@
# Datenbank nur fuer App und Worker erreichbar; App nur ueber Traefik.
# Traefik wird an Namespace (traefik oder kube-system) UND Pod-Label erkannt
# (eigene Installation "traefik" oder RKE2-Paket "rke2-traefik"). Laeuft
# Traefik in einem anderen Namespace: dessen Namen unten ergaenzen.
# Pruefen: kubectl get pods -A -l 'app.kubernetes.io/name in (traefik,rke2-traefik)'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: artista-modelcamp-postgres
namespace: artista
spec:
podSelector:
matchLabels:
app: artista-modelcamp-postgres
ingress:
- from:
- podSelector:
matchLabels:
db-client: "true"
ports:
- port: 5432
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: artista-modelcamp-app
namespace: artista
spec:
podSelector:
matchLabels:
app: artista-modelcamp-app
ingress:
- from:
# Namespace UND Pod-Label muessen passen: ein Label allein koennte
# jeder setzen, der irgendwo Pods starten darf.
- namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values: [traefik, kube-system]
podSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values: [traefik, rke2-traefik]
ports:
- port: 3000
File renamed without changes.
+23
View File
@@ -0,0 +1,23 @@
# Die Datenbank ist nur fuer Pods mit dem Label db-client=true erreichbar
# (App inkl. Migration, Worker, Admin-Anlage). Beruehrt weder Traefik noch
# cert-manager.
#
# Bewusst KEINE Policy vor der App (wie bei Wabenhain/ApiCore): laeuft
# Traefik im Host-Netzwerk, wuerde sie die Website aussperren.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: artista-modelcamp-postgres
namespace: artista
spec:
podSelector:
matchLabels:
app: artista-modelcamp-postgres
policyTypes: ["Ingress"]
ingress:
- from:
- podSelector:
matchLabels:
db-client: "true"
ports:
- port: 5432
+31 -15
View File
@@ -10,7 +10,7 @@ Prüfen:
```bash ```bash
kubectl get ingressclass kubectl get ingressclass
kubectl get clusterissuer letsencrypt-prod kubectl get clusterissuer letsencrypt-prod
kubectl get pods -A -l 'app.kubernetes.io/name in (traefik,rke2-traefik)' kubectl get csidrivers | grep smb
``` ```
## Einmalige Vorbereitung ## Einmalige Vorbereitung
@@ -21,26 +21,40 @@ kubectl get pods -A -l 'app.kubernetes.io/name in (traefik,rke2-traefik)'
helm repo add csi-driver-smb https://raw.githubusercontent.com/kubernetes-csi/csi-driver-smb/master/charts 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 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 `02-storage.yaml` anpassen. 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-config.yaml` (APP_URL) und `05-ingress.yaml` (2×) ersetzen. Der DNS-Eintrag muss auf den Cluster zeigen, sonst stellt cert-manager kein Zertifikat aus. 4. **Domain:** `modelcamp.example.org` in `01-configmap.yaml` (APP_URL) und `06-ingress.yaml` (2×) ersetzen. Der DNS-Eintrag muss auf den Cluster zeigen, sonst stellt cert-manager kein Zertifikat aus.
5. **Secrets:** `cp k8s/secrets.yaml.example k8s/secrets.yaml` und ausfüllen. Die Datei steht in `.gitignore`, bitte **nie** mit echten Werten committen. 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.
## Deployment ## Deployment (auch für einen kompletten Neuaufbau)
```bash ```bash
kubectl apply -f k8s/00-namespace.yaml kubectl apply -f k8s/00-namespace.yaml
kubectl apply -f k8s/secrets.yaml kubectl apply -f k8s/01-configmap.yaml
kubectl apply -f k8s/01-config.yaml kubectl apply -f k8s/02-secret.yaml
kubectl apply -f k8s/02-storage.yaml kubectl apply -f k8s/03-storage.yaml
kubectl apply -f k8s/03-postgres.yaml kubectl apply -f k8s/04-postgres.yaml
kubectl wait --for=condition=ready pod -l app=artista-modelcamp-postgres -n artista --timeout=180s kubectl wait --for=condition=ready pod -l app=artista-modelcamp-postgres -n artista --timeout=180s
kubectl apply -f k8s/04-app.yaml kubectl apply -f k8s/05-app-deployment.yaml
kubectl apply -f k8s/05-ingress.yaml kubectl apply -f k8s/06-ingress.yaml
kubectl apply -f k8s/06-worker.yaml kubectl apply -f k8s/07-worker-cronjob.yaml
kubectl apply -f k8s/07-networkpolicy.yaml kubectl apply -f k8s/08-networkpolicy.yaml
``` ```
Die Migration läuft automatisch als initContainer vor dem App-Start. Den Fortschritt zeigt `kubectl rollout status deployment/artista-modelcamp-app -n artista`, das Zertifikat `kubectl get certificate -n artista`. 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`:** 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 ## Erstes Admin-Konto
@@ -61,7 +75,9 @@ kubectl rollout restart deployment/artista-modelcamp-app -n artista
## Fehlersuche ## Fehlersuche
```bash ```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 migrate # Migration
kubectl -n artista logs deploy/artista-modelcamp-app -c app # App kubectl -n artista logs deploy/artista-modelcamp-app -c app # App
kubectl -n artista describe certificate artista-modelcamp-tls # Zertifikat kubectl -n artista get events --sort-by=.lastTimestamp | tail -20
``` ```