Zum Inhalt

Kubernetes-Deployment

Kamerplanter wird über ein einzelnes Helm-Chart deployt, das alle Komponenten enthält: Backend, Frontend, ArangoDB und Valkey. Die Container-Images und das Helm-Chart liegen in der GitHub Container Registry (ghcr.io).


Voraussetzungen

Was Minimum
Kubernetes-Cluster Version 1.28+
Helm Version 3.12+
kubectl Konfiguriert und verbunden mit dem Cluster
Ingress-Controller Traefik, nginx-ingress oder vergleichbar
Speicher StorageClass mit ReadWriteOnce-Unterstützung (für ArangoDB + Valkey)

Überblick: Was wird deployt?

flowchart TB
    subgraph "Kubernetes Cluster"
        direction TB
        ING["Ingress<br/>(Traefik / nginx)"]

        subgraph "Kamerplanter Namespace"
            FE["Frontend<br/>(Deployment, 2 Replicas)"]
            BE["Backend<br/>(Deployment, 2 Replicas)"]
            DB["ArangoDB<br/>(StatefulSet, 1 Replica)"]
            VK["Valkey<br/>(StatefulSet, 1 Replica)"]
        end
    end

    ING -->|"/api/*"| BE
    ING -->|"/"| FE
    BE --> DB
    BE --> VK
    FE -->|"proxy /api"| BE

    style ING fill:#FF9800,color:#fff
    style FE fill:#66BB6A,color:#fff
    style BE fill:#43A047,color:#fff
    style DB fill:#2E7D32,color:#fff
    style VK fill:#1B5E20,color:#fff
Komponente Typ Replicas Beschreibung
Backend Deployment 2 FastAPI-Anwendung (API + Celery Worker)
Frontend Deployment 2 React-App hinter nginx, Proxy für /api zum Backend
ArangoDB StatefulSet 1 Dokumenten-/Graph-Datenbank mit Persistent Volume (5 Gi)
Valkey StatefulSet 1 Redis-kompatibler Cache + Celery-Broker (1 Gi)

Installation

1. Helm-Repository hinzufügen

Das Kamerplanter Helm-Chart liegt als OCI-Artefakt in der GitHub Container Registry:

# OCI-Registries benötigen kein helm repo add —
# der Pull erfolgt direkt über die OCI-URL
helm pull oci://ghcr.io/nolte/charts/kamerplanter --version 0.2.0

--version muss eine veröffentlichte Version nennen

0.2.0 ist die zuletzt veröffentlichte Chart-Version. Unter derselben OCI-Adresse liegt daneben ein Entwicklungskanal: Der develop-Baum trägt eine Vorabversion mit dem Zusatz -dev (derzeit 0.2.1-dev), und dieser Tag wird bei jedem Merge nach develop überschrieben, der helm/ berührt. Erzwungen ist dabei nur der Bezeichner dev, nicht die Nummer davor: Die -dev-Version muss der veröffentlichten Linie nicht voraus sein. Ziehe daraus nichts, was laufen soll — die Bytes wechseln unter dir, ohne dass sich die Versionsangabe ändert. Details: CI/CD — Zwei Kanäle.

Der Chart-Tag 0.2.0 wurde aus develop überschrieben

charts/kamerplanter:0.2.0 trägt nicht mehr das, was das Release v0.2.0 veröffentlicht hat. Gemessen am OCI-Manifest: Es ist mit org.opencontainers.image.created: 2026-08-18T14:09:14Z annotiert, während v0.2.0 am 13.08.2026 um 18:09:49 UTC veröffentlicht wurde — ein helm/-Merge nach develop hat den Tag fünf Tage später neu gepusht. Genau das verhindern die beiden Kanal-Prüfungen seither. Repariert wird der Tag bewusst nicht: Ihn zu überschreiben wäre ein zweiter Push unter derselben Versionsreferenz.

Für die Befehle auf dieser Seite heißt das: --version 0.2.0 löst weiterhin auf, liefert aber einen develop-Stand statt des Release-Stands. 0.1.0 ist der jüngste Chart-Tag, dessen Manifest noch seinen Release-Zeitstempel trägt (erstellt am 06.08.2026 um 13:38:04 UTC, 15 Sekunden nach der Veröffentlichung von v0.1.0), und v0.2.1 — zum Zeitpunkt dieser Zeilen noch ein Entwurf — ist das erste Release, das unter dem Schutz beider Prüfungen entsteht. Was du auch wählst: Reproduzierbar wird ein Deployment erst, wenn du den Manifest-Digest pinnst, den du geprüft hast.

Authentifizierung an der GitHub Registry

Falls die Registry privat ist, musst du dich vorher anmelden:

echo $GITHUB_TOKEN | helm registry login ghcr.io --username $GITHUB_USER --password-stdin

2. Pflicht-Secrets anlegen

Ohne dieses Secret startet kein Backend-Pod

Bevor du das Chart installierst, muss das Kubernetes-Secret kamerplanter-secrets existieren. Der Backend-Container liest ARANGODB_PASSWORD, ARANGO_ROOT_PASSWORD, JWT_SECRET_KEY, FERNET_KEY und ERASURE_TOMBSTONE_SALT ausschließlich über envFrom aus diesem Secret — nicht aus values.yaml. Fehlt eines der vier zuletzt genannten Werte (bzw. bleibt ARANGODB_PASSWORD beim Literal rootpassword), bricht der Backend-Start mit SystemExit ab, sobald DEBUG=false gesetzt ist (Fail-Fast-Gate, src/backend/app/main.py). ARANGO_ROOT_PASSWORD muss dabei identisch mit ARANGODB_PASSWORD sein — beide gehen an denselben ArangoDB-Container.

kubectl create namespace kamerplanter

kubectl create secret generic kamerplanter-secrets \
  --namespace kamerplanter \
  --from-literal=ARANGODB_PASSWORD="dein-sicheres-passwort" \
  --from-literal=ARANGO_ROOT_PASSWORD="dein-sicheres-passwort" \
  --from-literal=JWT_SECRET_KEY="$(openssl rand -hex 32)" \
  --from-literal=FERNET_KEY="$(python3 -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())')" \
  --from-literal=ERASURE_TOMBSTONE_SALT="$(openssl rand -hex 32)"

Vollständige Übersicht aller Pflicht-Secrets je aktivierter Funktion (z. B. INTERNAL_SERVICE_TOKEN sobald der KI-Assistent oder die Bilderkennung aktiv sind): Konfigurationsmatrix — Pflicht-Secrets.

3. Values-Datei erstellen

Erstelle eine values-production.yaml mit deinen Anpassungen:

values-production.yaml
controllers:
  backend:
    replicas: 2     # (1)!
    containers:
      main:
        envFrom:
          - secret: kamerplanter-secrets    # (2)!
        env:
          ARANGODB_HOST: kamerplanter-arangodb
          ARANGODB_PORT: "8529"
          ARANGODB_DATABASE: kamerplanter
          ARANGODB_USERNAME: root
          REDIS_URL: redis://kamerplanter-valkey:6379/0
          CORS_ORIGINS: '["https://pflanzen.example.com"]'
          DEBUG: "false"
          KAMERPLANTER_MODE: full    # (3)!

  frontend:
    replicas: 2

  arangodb:
    containers:
      main:
        envFrom:
          - secret: kamerplanter-secrets    # (4)!
    statefulset:
      volumeClaimTemplates:
        - name: data
          accessMode: ReadWriteOnce
          size: 10Gi    # (5)!
          globalMounts:
            - path: /var/lib/arangodb3

ingress:
  main:
    enabled: true
    hosts:
      - host: pflanzen.example.com    # (6)!
        paths:
          - path: /api
            pathType: Prefix
            service:
              identifier: backend
          - path: /
            pathType: Prefix
            service:
              identifier: frontend
  1. Zwei Replicas für Rolling Updates ohne Downtime.
  2. Zieht ARANGODB_PASSWORD, JWT_SECRET_KEY, FERNET_KEY und ERASURE_TOMBSTONE_SALT aus dem im vorigen Schritt angelegten Secret — keine Klartext-Passwörter in values.yaml.
  3. light = ohne Login/Tenant-System, ein Nutzer. full (Standard im Chart) = mit JWT-Auth und Mandantenverwaltung. Details: Betriebsprofile.
  4. ARANGO_ROOT_PASSWORD wird ebenfalls aus kamerplanter-secrets injiziert.
  5. Passe die Größe an deinen Bedarf an. Der Chart-Default für die ArangoDB-PVC liegt bei 5Gi.
  6. Dein gewünschter Hostname. Der Ingress-Controller muss darauf konfiguriert sein.

Passwörter nie in der Values-Datei

values-production.yaml referenziert Secrets ausschließlich über envFrom/secretKeyRef — nie über einen Klartext-Wert in env:. Für GitOps-Workflows eignet sich statt manuellem kubectl create secret ein Secret-Management-Tool wie Sealed Secrets oder External Secrets Operator (siehe ArgoCD — Deklaratives Secret-Management).

4. Helm-Chart installieren

helm install kamerplanter \
  oci://ghcr.io/nolte/charts/kamerplanter \
  --version 0.2.0 \
  --namespace kamerplanter \
  --create-namespace \
  --values values-production.yaml

0.2.0 ist hier dieselbe überschriebene Chart-Version

Der Tag löst auf, liefert aber einen develop-Stand und nicht den Stand des Releases v0.2.0. Warum, und was du stattdessen pinnst, steht in Schritt 1.

5. Deployment prüfen

# Pod-Status prüfen
kubectl get pods -n kamerplanter

# Auf gesunde Pods warten
kubectl wait --for=condition=ready pod \
  --all -n kamerplanter --timeout=120s

Erwartete Ausgabe:

NAME                                      READY   STATUS    RESTARTS   AGE
kamerplanter-backend-7d8f9b6c4d-abc12     1/1     Running   0          45s
kamerplanter-backend-7d8f9b6c4d-def34     1/1     Running   0          45s
kamerplanter-frontend-5c4d8e7f3b-ghi56    1/1     Running   0          45s
kamerplanter-frontend-5c4d8e7f3b-jkl78    1/1     Running   0          45s
kamerplanter-arangodb-0                   1/1     Running   0          45s
kamerplanter-valkey-0                     1/1     Running   0          45s

Updates durchführen

# Auf neue Version aktualisieren
helm upgrade kamerplanter \
  oci://ghcr.io/nolte/charts/kamerplanter \
  --version 0.3.0 \
  --namespace kamerplanter \
  --values values-production.yaml

Die Backend- und Frontend-Deployments führen ein Rolling Update durch — es gibt keine Downtime, da die alten Pods erst beendet werden, wenn die neuen bereit sind.


Deinstallation

helm uninstall kamerplanter --namespace kamerplanter

Persistent Volumes

helm uninstall entfernt die Deployments und Services, aber nicht die Persistent Volume Claims (PVCs) von ArangoDB und Valkey. Deine Daten bleiben erhalten. Um auch die Daten zu löschen:

kubectl delete pvc --all -n kamerplanter

Monitoring

Logs prüfen

# Backend-Logs
kubectl logs -l app.kubernetes.io/component=backend -n kamerplanter --tail=50

# Frontend-Logs
kubectl logs -l app.kubernetes.io/component=frontend -n kamerplanter --tail=50

# ArangoDB-Logs
kubectl logs -l app.kubernetes.io/component=arangodb -n kamerplanter --tail=50

Health-Checks

Das Backend bietet zwei Health-Endpunkte:

Endpunkt Prüft Verwendung
/api/v1/health/live Backend-Prozess läuft Kubernetes Liveness-Probe
/api/v1/health/ready Backend + Datenbank erreichbar Kubernetes Readiness-Probe
# Manuell testen (über Port-Forward)
kubectl port-forward svc/kamerplanter-backend 8000:8000 -n kamerplanter
curl http://localhost:8000/api/v1/health/ready

Fehlerbehebung

Pods bleiben im Status 'Pending'

Der Cluster hat nicht genügend Ressourcen. Prüfe die verfügbare Kapazität mit kubectl describe nodes und vergleiche mit den Resource Requests in der Values-Datei. Für kleinere Cluster kannst du die Requests reduzieren.

ArangoDB startet nicht (CrashLoopBackOff)

Häufigste Ursache: Zu wenig Speicher. ArangoDB braucht mindestens 512 Mi. Prüfe die Logs: kubectl logs kamerplanter-arangodb-0 -n kamerplanter.

Frontend zeigt 502 Bad Gateway

Das Backend ist noch nicht bereit. Warte, bis die Readiness-Probe des Backends erfolgreich ist: kubectl get pods -n kamerplanter -w. Falls der Fehler bleibt: Stimmen die Service-Namen in der nginx-Konfiguration?

Ingress funktioniert, aber die Seite lädt nicht

Prüfe: (1) Ist ein Ingress-Controller installiert? (2) Zeigt der DNS-Eintrag auf den Cluster? (3) Stimmt der Hostname in der Values-Datei mit dem DNS überein?

Backend-Pod bleibt in 'CreateContainerConfigError'

Das Secret kamerplanter-secrets existiert nicht im Ziel-Namespace, oder ein per envFrom/secretKeyRef referenzierter Schlüssel fehlt darin. Prüfe: kubectl get secret kamerplanter-secrets -n kamerplanter und vergleiche die vorhandenen Schlüssel mit Schritt 2 oben.

Backend-Pod startet und crasht sofort wieder (CrashLoopBackOff mit 'FATAL: Default secrets detected')

Das Secret existiert, enthält aber einen unveränderten Standardwert — z. B. ARANGODB_PASSWORD=rootpassword oder ein leeres FERNET_KEY. Die Logzeile benennt die betroffenen Felder direkt: kubectl logs -l app.kubernetes.io/component=backend -n kamerplanter. Details zur Prüfung: Konfigurationsmatrix — Pflicht-Secrets.


Siehe auch