Lokales Setup¶
Diese Seite beschreibt, wie du eine vollständige lokale Entwicklungsumgebung für Kamerplanter aufsetzt. Skaffold ist das einzige autorisierte Werkzeug für den Entwicklungsprozess — es übernimmt Image-Building und Deployment im Kubernetes-Cluster. Manuelle docker build- oder kubectl apply-Befehle werden nicht verwendet.
Voraussetzungen¶
Installiere folgende Werkzeuge, bevor du beginnst:
| Werkzeug | Mindestversion | Zweck |
|---|---|---|
| Docker | 24+ | Container-Runtime für Kind |
| Kind | 0.20+ | Lokaler Kubernetes-Cluster |
| kubectl | 1.28+ | Kubernetes CLI |
| Skaffold | 2.10+ | Build- und Deploy-Automatisierung |
| Helm | 3.14+ | Kubernetes Package Manager |
| asdf | 0.14+ | Node.js-Versionsverwaltung |
| Python | 3.14+ | Backend-Entwicklung ohne Cluster |
| Node.js | 25.1.0 | Frontend-Entwicklung ohne Cluster |
Node.js-Version via asdf
Im src/frontend/-Verzeichnis liegt eine .tool-versions-Datei mit nodejs 26.10.0. Nach der asdf-Installation wird die korrekte Version automatisch aktiviert:
Kind-Cluster erstellen¶
Das Repository enthält eine vorkonfigurierte Kind-Konfiguration mit drei Knoten (1 Control Plane, 2 Worker) und vordefinierten Port-Mappings:
Der Cluster veröffentlicht auf dem Host-System nur die Ingress-Ports 80 und 443. Der Zugriff auf Backend (8000), Frontend (3000) und ArangoDB (8529) läuft über Skaffolds --port-forward (siehe Tabelle unten) — diese Host-Ports müssen beim Start also frei sein.
Beide Ingress-Ports lauschen nur auf 127.0.0.1 (listenAddress in kind-config.yaml). Ohne diese Angabe bindet Kind auf 0.0.0.0 — dann wäre die Entwicklungs-App auf jeder Netzwerkschnittstelle deines Rechners erreichbar, im Light-Modus sogar ohne Anmeldung, für jedes Gerät im selben Netz.
Änderung greift erst nach dem Neuanlegen
Kind legt die Port-Bindung beim Erstellen des Clusters fest. Ein Cluster, den du vor dieser Einstellung angelegt hast, lauscht weiter auf allen Schnittstellen, bis du ihn löschst und neu erstellst. Prüfen kannst du das mit docker port kamerplanter-control-plane — die Ausgabe muss 127.0.0.1:80 und 127.0.0.1:443 zeigen, nicht 0.0.0.0.
Cluster vom Smartphone oder einem anderen Rechner aus erreichen
Kind wertet in seiner Konfiguration keine Umgebungsvariablen aus, deshalb gibt es keinen Schalter wie KAMERPLANTER_BIND_ADDRESS beim Docker-Compose-Betrieb. Wenn du den Entwicklungs-Cluster bewusst im Netz freigeben willst, lege ihn aus einer nicht eingecheckten Kopie an, in der listenAddress auf 0.0.0.0 steht:
Bereits vorhandener Cluster
Falls ein Cluster mit dem Namen kamerplanter existiert, lösche ihn zunächst:
Nach dem Erstellen prüfst du, ob kubectl auf den neuen Cluster zeigt:
Secret anlegen¶
Backend, Celery-Worker und die KI-Dienste referenzieren das Kubernetes-Secret kamerplanter-secrets per envFrom bzw. secretKeyRef (definiert in helm/kamerplanter/values.yaml). Die Dev-Overrides heben diese Referenz nicht auf, daher muss das Secret im Namespace default existieren, bevor du Skaffold startest — sonst bleiben die betroffenen Pods in CreateContainerConfigError hängen. FERNET_KEY, ERASURE_TOMBSTONE_SALT und LOG_PSEUDONYM_SALT werden zusätzlich beim Backend-Start als Pflichtwerte geprüft (Fail-Fast); LOG_PSEUDONYM_SALT prüft auch der Celery-Worker.
Lege das Secret einmalig pro frisch erstelltem Cluster an:
kubectl create secret generic kamerplanter-secrets -n default \
--from-literal=ARANGODB_PASSWORD=dein-sicheres-passwort \
--from-literal=ARANGO_ROOT_PASSWORD=dein-sicheres-passwort \
--from-literal=JWT_SECRET_KEY=dev-only-not-for-production-secret-key-1234 \
--from-literal=POSTGRES_PASSWORD=foba123456 \
--from-literal=VECTORDB_PASSWORD=foba123456 \
--from-literal=FERNET_KEY=$(python3 -c "import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())") \
--from-literal=ERASURE_TOMBSTONE_SALT=$(openssl rand -hex 32) \
--from-literal=LOG_PSEUDONYM_SALT=$(openssl rand -hex 32) \
--from-literal=INTERNAL_SERVICE_TOKEN=$(openssl rand -hex 32)
FERNET_KEY-Format
FERNET_KEY muss ein gültiger Fernet-Schlüssel sein: 32 Bytes, url-safe base64-kodiert (44 Zeichen) — genau das, was cryptography.fernet.Fernet.generate_key() erzeugt. Ein openssl rand -hex 32 (64 Hex-Zeichen) ist kein gültiger Fernet-Schlüssel: Das Backend startet zwar, aber jeder verschlüsselnde Endpunkt scheitert später mit ValueError: Fernet key must be 32 url-safe base64-encoded bytes. Nur FERNET_KEY braucht dieses Format; ERASURE_TOMBSTONE_SALT, LOG_PSEUDONYM_SALT und INTERNAL_SERVICE_TOKEN sind beliebige Zufallswerte.
ArangoDB-Passwort
ARANGODB_PASSWORD und ARANGO_ROOT_PASSWORD müssen denselben Wert haben: ArangoDB initialisiert sein Root-Passwort aus ARANGO_ROOT_PASSWORD, während Backend und Celery sich mit ARANGODB_PASSWORD verbinden. Beide kommen ausschließlich aus diesem Secret — values-dev.yaml setzt sie nicht mehr inline. Weichen die Werte voneinander ab, scheitert der Backend-Start mit HTTP 401.
Nur für die lokale Entwicklung
Diese Werte sind bewusst schwach und gehören niemals in eine Produktivumgebung. Für Produktion und GitOps beschreibt das ArgoCD-Deployment die sichere Secret-Verwaltung (Sealed Secrets, External Secrets Operator).
Entwicklungsstart mit Skaffold¶
Skaffold baut die Container-Images lokal (ohne Push in eine Registry) und deployt sie per Helm ins Kind-Cluster. Dateiänderungen werden per sync direkt in laufende Container übertragen, ohne ein komplettes Rebuild.
Vollstack starten¶
Mit --trigger=manual erfolgt ein Rebuild nur, wenn du r drückst. Das verhindert ungewollte Neustarts bei schnellen Dateiänderungen. Skaffold aktiviert die Port-Weiterleitungen automatisch (siehe Tabelle unten).
Nur Backend¶
Das backend-only-Profil entfernt das Frontend-Artifact und dessen Port-Weiterleitung aus dem Build-Prozess.
Nur Frontend¶
Mit Debugger (debugpy)¶
Das debug-Profil aktiviert DEBUGPY_ENABLED=true als Build-Argument im Backend-Container. Der debugpy-Port 5678 ist dann erreichbar (siehe Debugging).
Mit KI-Stack (RAG / Knowledge-Service)¶
# Hauptapp + KI-Stack gleichzeitig
skaffold dev --trigger=manual --port-forward -m kamerplanter,ki
# Nur KI-Stack (fuer RAG-Entwicklung)
skaffold dev --trigger=manual --port-forward -m ki
Das KI-Modul (-m ki) ist eine eigenstaendige Skaffold-Konfiguration im selben skaffold.yaml. Es deployt Knowledge-Service (Port 8090), Embedding-Service (Port 8080) und VectorDB/TimescaleDB (Port 5433) unabhaengig von der Hauptapplikation. Weitere Details unter Infrastruktur — KI-Modul.
Port-Weiterleitungen¶
Skaffold leitet folgende Ports automatisch weiter, sobald --port-forward gesetzt ist:
| Service | Lokaler Port | Ziel im Cluster | Modul |
|---|---|---|---|
| Frontend | 3000 | Vite-Dev-Server auf 5173 | kamerplanter |
| Backend API | 8000 | FastAPI auf 8000 | kamerplanter |
| ArangoDB Web-UI | 8529 | ArangoDB auf 8529 | kamerplanter |
| Home Assistant | 8123 | Home Assistant auf 8123 | kamerplanter |
| VectorDB (TimescaleDB) | 5433 | TimescaleDB auf 5432 | ki |
| Knowledge-Service | 8090 | Knowledge-Service auf 8000 | ki |
| Embedding-Service | 8080 | Embedding-Service auf 8080 | ki |
API-Dokumentation
Nach dem Start ist die automatisch generierte Swagger-UI unter http://localhost:8000/docs erreichbar. ReDoc ist unter http://localhost:8000/redoc verfügbar.
Demo-Zugangsdaten¶
Der Backend-Container führt beim ersten Start automatisch Seed-Skripte aus. Danach steht ein Demo-Benutzer bereit:
| Feld | Wert |
|---|---|
demo@kamerplanter.local | |
| Passwort | demo-passwort-2024 |
| Tenant-Slug | demo |
Backend lokal ohne Kubernetes¶
Für reine Backend-Entwicklung ohne Cluster-Overhead kannst du den FastAPI-Server direkt starten. Voraussetzung ist eine laufende ArangoDB-Instanz (z. B. per Docker Compose).
# ArangoDB und Redis mit Docker Compose starten
docker-compose up -d arangodb valkey
# Python-Abhängigkeiten aus dem Lock installieren (uv: https://docs.astral.sh/uv/)
cd src/backend
uv sync --locked --extra dev
source .venv/bin/activate
# Umgebungsvariablen setzen
export ARANGODB_HOST=localhost
export ARANGODB_PORT=8529
export ARANGODB_DATABASE=kamerplanter
export ARANGODB_USERNAME=root
export ARANGODB_PASSWORD=rootpassword
export REDIS_URL=redis://localhost:6379/0
export DEBUG=true
export REQUIRE_EMAIL_VERIFICATION=false
# Server starten
uvicorn app.main:app --reload --port 8000
Hot-Reload
uvicorn --reload überwacht alle .py-Dateien im app/-Verzeichnis und startet den Server bei Änderungen automatisch neu. Im Kind-Cluster übernimmt Skaffold diese Aufgabe über den sync-Mechanismus.
Frontend lokal ohne Kubernetes¶
Der Vite-Dev-Server kann unabhängig vom Cluster laufen. Er proxyt alle /api-Anfragen an das Backend (Standard: http://127.0.0.1:8000).
Der Dev-Server startet auf Port 5173. Die Backend-URL kann über die Umgebungsvariable VITE_BACKEND_URL überschrieben werden:
Home Assistant Integration¶
Die HA-Integration in custom_components/kamerplanter/ wird nicht automatisch von Skaffold deployed. Nach Änderungen an den HA-Integrationsdateien muss der Inhalt manuell in den laufenden Pod kopiert werden:
# Dateien in den Pod kopieren
kubectl cp custom_components/kamerplanter/ \
default/homeassistant-0:/config/custom_components/kamerplanter/
# Python-Cache löschen (MUSS immer gemacht werden, sonst lädt HA alten Bytecode)
kubectl exec default/homeassistant-0 -- \
rm -rf /config/custom_components/kamerplanter/__pycache__
# HA-Prozess neustarten (NICHT kubectl delete pod!)
# kill 1 startet nur den Container neu — InitContainers laufen NICHT erneut,
# d.h. die per kubectl cp kopierten Dateien bleiben erhalten.
kubectl exec homeassistant-0 -n default -- kill 1
Fehlerbehebung¶
Skaffold findet den Kind-Cluster nicht
Stelle sicher, dass der kubectl-Kontext korrekt gesetzt ist:
Backend-Pod startet nicht (CrashLoopBackOff)
Sieh dir die Pod-Logs an:
Häufige Ursache: ArangoDB ist noch nicht bereit. Die Liveness-Probe wartet bis zu 150 Sekunden (initialDelaySeconds: 30, failureThreshold: 10). Port 8000 oder 3000 ist belegt
Beende alle Prozesse auf dem betreffenden Port:
Seed-Daten fehlen nach Neustart
Die Seed-Skripte laufen nur beim ersten Start oder wenn die ArangoDB-Datenbank neu erstellt wird. Um die Seeds erneut auszuführen: