Zum Inhalt

Compose-Invocation-Discipline

Prinzip

docker compose-Invocations ohne --env-file können abhängige Services stillschweigend mit leerer Env recreaten. Bei kritischen Secrets (z.B. KC_DB_PASSWORD) führt das zu Crashloops, die schwer zu diagnostizieren sind. Beispiel: Keycloak-Crashloop während B2-03-Verifikation am 2026-04-22 — ein docker compose run ohne --env-file hatte den kora-platform-keycloak-Container mit leerem KC_DB_PASSWORD recreated, 18 Restart-Zyklen mit SCRAM-Auth-Fehler waren die Folge.

Konsequenz: Alle docker compose-Invocations für den kora-platform-Stack laufen über den Makefile-Wrapper $(KORA_COMPOSE). Ad-hoc-Invocations direkt in der Shell (ohne expliziten --env-file) sind zu vermeiden.

Makefile-Wrapper

KORA_COMPOSE = docker compose -p kora-platform -f docker-compose.platform.yml --env-file .env.platform

Alle Platform-Targets im Makefile nutzen diese Variable:

  • make dev-platform / make down-platform
  • make redeploy-platform
  • make logs-platform
  • make docs-kora / make docs-kora-build
  • make platform-exec cmd="…" — Ad-hoc-Befehl im kora-platform-api
  • make platform-bootstrap cmd="…" — Bootstrap-CLI mit Master-PW

kora-*-Convenience-Targets mit SVC=-Pattern (K-Q4-Fix-B.3)

Für Single-Service-Operationen ohne den ganzen Stack zu touchen:

Target Zweck Beispiel
make kora-up / kora-down Stack starten/stoppen (Aliase) make kora-up
make kora-ps Container-Status auflisten make kora-ps
make kora-restart SVC=<svc> Service neu starten make kora-restart SVC=demo-frontend
make kora-recreate SVC=<svc> force-recreate für Bind-Mount-Refresh make kora-recreate SVC=demo-frontend
make kora-logs SVC=<svc> Logs eines Services tailen make kora-logs SVC=api
make kora-rebuild SVC=<svc> --no-cache rebuild gegen Stale-CMD-Drift make kora-rebuild SVC=api

SVC= ist der Compose-Service-Name (z.B. demo-frontend), nicht der container_name (kora-platform-demo-frontend). Der Compose-Service- Name steht in docker-compose.platform.yml als Top-Level-Key unter services:.

kora-recreate vs. kora-restart: restart reicht nicht, wenn Linux-Bind-Mounts nach einem git checkout einen Stale-Inode haben (K-Q4-Fix-B.1-Recovery, demo-frontend zeigte leeres /usr/share/nginx/html). kora-recreate macht up -d --force-recreate, was den Mount frisch bindet.

kora-rebuild vs. redeploy-platform-api: kora-rebuild mit --no-cache ist die richtige Wahl nach Modul-Renamings oder Major- Dependency-Updates (z.B. avs_chatbotkora_platform-Rename in K-Q4-Fix-A — der normale Build hatte den alten CMD aus dem Layer-Cache gehalten).

Bind-Mount-Drift nach git checkout

Linux bind-mounts pinnen sich an den Inode des Source-Pfades zum Mount-Zeitpunkt. Schreibt git checkout einen frischen Inode für ein Verzeichnis (etwa weil ein Branch das Verzeichnis anders strukturiert), liest der Container weiter aus dem alten Inode — meist ein leeres Verzeichnis. make kora-restart reicht nicht; nur ein force-recreate refresht die Mount-Tabelle.

make refresh-mounts                       # Default: demo-frontend
make refresh-mounts SVC="demo-frontend api"  # explizite Liste

Vorfall: K-Q4-Fix-B.1 recovery 2026-05-06, kora-platform-demo-frontend zeigte leeres /usr/share/nginx/html nach Branch-Switch zwischen Feature-Branch und platform/v1.0.0. Restart half nicht, recreate schon. Helper kapselt das Pattern.

Optional als opt-in Git-Hook lokal aktivieren (NICHT im Repo, opt-in pro Dev):

cat > .git/hooks/post-checkout <<'EOF'
#!/bin/sh
if git diff-tree -r --name-only HEAD@{1} HEAD 2>/dev/null \
     | grep -q '^infra/demo-frontend/html/'; then
  echo "Branch-switch touched demo-frontend html — refreshing mounts..."
  ./infra/scripts/refresh-mounts.sh
fi
EOF
chmod +x .git/hooks/post-checkout

make platform-exec — Ad-hoc-Befehl im laufenden API-Container

Für Befehle, die im laufenden kora-platform-api-Container ausgeführt werden sollen (Diagnose, Settings-Dump, etc.):

make platform-exec cmd="kora-platform --help"
make platform-exec cmd="sh -c 'env | grep -i KEYCLOAK'"

Der Wrapper nutzt docker compose … exec api, d.h. der bestehende Container wird benutzt, kein neuer Run-Container erzeugt — kein stilles Service-Recreate.

make platform-bootstrap — Bootstrap-CLI mit Master-PW

Für einmalige Bootstrap-Operationen (z.B. bootstrap-operator-admin), die Master-Admin-Credentials gegen Keycloak brauchen:

# tmpfs-Pfad (empfohlen, weg nach Reboot)
mkdir -p /run/user/$UID
cp .env.bootstrap.example /run/user/$UID/.env.bootstrap
$EDITOR /run/user/$UID/.env.bootstrap   # Master-PW eintragen

set -a
source /run/user/$UID/.env.bootstrap
set +a

make platform-bootstrap cmd="bootstrap-operator-admin --email admin@example.com"

shred -u /run/user/$UID/.env.bootstrap 2>/dev/null || \
  rm -f /run/user/$UID/.env.bootstrap

Der Wrapper scheitert früh mit klarer Fehlermeldung, wenn KC_BOOTSTRAP_ADMIN_USERNAME oder KC_BOOTSTRAP_ADMIN_PASSWORD in der Shell-Env fehlen. Intern nutzt er docker exec -e … (keine docker compose run-Invocation), weil --env-file im run-Subcommand in unserer Compose-Version (v5.1.3) nicht unterstützt wird — und weil exec ohnehin keine Service-Graph-Re-Evaluation auslöst.

Was zu vermeiden ist

  • Nicht: docker compose run --rm api … ohne explizites --env-file .env.platform. Dieses Pattern war der Trigger für den Keycloak-Crashloop vom 2026-04-22.
  • Nicht: Direkte docker exec …-Befehle ohne Env-Awareness, wenn der Befehl Env-Variablen braucht. Für Platform-Scope immer make platform-exec bzw. make platform-bootstrap nutzen.
  • Nicht: Ad-hoc-Scripts, die docker compose …-Invocations enthalten, ohne den Wrapper zu nutzen. Stattdessen auf ein Makefile-Target zurückgreifen oder neues Target hinzufügen.

Diagnose bei Crashloop-Verdacht

Wenn ein Container aus dem Platform-Stack unerwartet in einen Crashloop geht:

# 1. Symptom-Check
docker inspect kora-platform-keycloak --format '{{.RestartCount}} restarts, exit {{.State.ExitCode}}'

# 2. Logs
docker logs kora-platform-keycloak --tail 50 2>&1 | grep -iE "error|fatal|auth"

# 3. Container-Env auf leere Secrets prüfen
docker inspect kora-platform-keycloak \
  --format '{{range .Config.Env}}{{println .}}{{end}}' | \
  grep -iE "password|secret"
# Einträge, die als 'KEY=' (leerer Wert) erscheinen, sind Hinweis auf
# eine frühere Invocation ohne --env-file.

# 4. Reparatur — immer via Makefile-Wrapper:
make redeploy-platform
# oder punktuell:
$(KORA_COMPOSE) up -d --force-recreate <service>

Warum nicht Docker-Secrets?

Docker-Secrets wären die robusteste Lösung (Secrets sind read-only Mount, nicht ENV), aber:

  • Umbau aller bestehenden Settings-Reader nötig (lesen aus Files statt ENVs)
  • Zusätzliche Docker-Swarm-/Compose-Infrastruktur
  • Für den Scope v1.0.0 Overkill — der Makefile-Wrapper eliminiert 95% der Fehlerklasse mit 20 Zeilen Makefile.

Als v1.x-Kandidat im Backlog, falls die Wrapper-Disziplin nicht hält oder neue Secret-Kategorien dazukommen.

Referenzen

  • Wrapper-Variable: Makefile:105 (KORA_COMPOSE)
  • Neue Ad-hoc-Targets: Makefile:151-176 (platform-exec, platform-bootstrap)
  • Trigger-Vorfall: Keycloak-Crashloop vom 2026-04-22, dokumentiert im TODO-Cleanup03-02-Archiv-Eintrag und im Changelog unter [Unreleased].
  • Verwandt: bind-mount-discipline.md, keycloak-service-account.md.