diff --git a/docs/MIGRATION_LOG.md b/docs/MIGRATION_LOG.md index f2ca7ff..22a238a 100644 --- a/docs/MIGRATION_LOG.md +++ b/docs/MIGRATION_LOG.md @@ -17,6 +17,16 @@ Dieses Dokument ist nur noch ein historischer Verlauf. Der aktuelle operative Ab ## Historische Meilensteine +### 2026-05-31 - Komodo Deploy-Drift strukturell abgesichert + +Nach dem Renovate-Block wurde die Ursache fuer den kurzzeitigen `nextcloud-postgres`-Drift nachgezogen: Komodo hatte den `nextcloud`-Stack beim Postgres-17.10-Deploy gestartet, aber `docker compose up -d` scheiterte zunaechst an einem Docker-Recreate-Namenskonflikt (`nextcloud-postgres` Ersatzcontainer). Der Workspace war dadurch bereits auf dem neuen Commit, waehrend der laufende DB-Container noch das alte Image nutzte. Der spaetere gezielte `docker compose up -d` aus dem aktualisierten Workspace hat den Zwischenzustand sauber aufgeloest; es blieben keine exited/dead Containerreste. + +- Komodo-Update-Historie bestaetigt: ein fehlgeschlagener `DeployStack` fuer `nextcloud` mit Compose-Up-Konflikt, danach ein erfolgreicher `DeployStack`. +- Aktueller Runtime-Stand nach Pruefung: `nextcloud`, `nextcloud-postgres`, `nextcloud-redis`, `postgresql17`, `mealie-postgres`, `gitea`, `bentopdf`, `ddns-updater` und Komodo-Self-Stack laufen ohne `unhealthy`-Status; die erwarteten Images stimmen mit den Compose-Dateien ueberein. +- `services/posture-check/export-prometheus-textfile.sh` exportiert jetzt `homelab_gitops_runtime_image_match{name,project,service}` fuer laufende Compose-Container. Die Metrik vergleicht das Image aus `docker compose config --format json` gegen `docker inspect .Config.Image` des laufenden Containers und faengt damit genau den Zustand "Workspace/Compose neu, Runtime alt" ab. +- Neue Prometheus-Regel `HomelabGitOpsRuntimeImageDrift`: feuert als Warning, wenn ein laufender Compose-Container laenger als 10 Minuten nicht dem Compose-Image entspricht. +- Smoke: Exporter-Test in `/tmp/kallilab-textfile-test/homelab.prom` lieferte fuer alle erkannten Compose-Container `homelab_gitops_runtime_image_match = 1`; `promtool check rules` meldete `SUCCESS: 17 rules found`. + ### 2026-05-31 - Renovate PRs #1 bis #5 gemerged und deployed Die ersten fuenf Renovate-PRs wurden einzeln in `master` uebernommen, mit Policy-Check und Live-Smoke nach den Datenhalter-Aenderungen. Major-Branches wurden bewusst nicht gemerged. diff --git a/docs/REPO_MAP.md b/docs/REPO_MAP.md index c4453bb..4300bff 100644 --- a/docs/REPO_MAP.md +++ b/docs/REPO_MAP.md @@ -71,6 +71,7 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam | `ops/glance/config/glance.yml` | Glance Dashboard-Konfiguration fuer Homelab-Monitore, Internet-/DNS-/VPN-Widgets, Community-Widgets, Docker-Containergruppen, Zeitfortschritt, Host-Snapshot, Bookmarks und zweite Infrastruktur-Seite | | `ops/borg-ui/scripts/pre-backup-dumps.sh` | Host-seitiges Dump-Skript fuer PostgreSQL, SQLite-Container-Dumps und Komodo Mongo | | `services/posture-check/posture-check.sh` | Host-seitiger Posture-Check fuer Filesystem, Mover-Drift, NVMe-SMART, Fuellstand, Authelia-Repo<->Host-Drift und ntfy-Alarmierung | +| `services/posture-check/export-prometheus-textfile.sh` | Host-seitiger Textfile-Exporter fuer Borg-, Critical-Container- und GitOps-Runtime-Image-Drift-Metriken | | `services/posture-check/docker-critical-events.sh` | Host-seitiger Docker-Event-Watcher fuer kritische ntfy-Alarme | | `services/posture-check/posture_check.sh` | Kompatibilitaets-Wrapper fuer die historische Schreibweise aus `STORAGE_LAYOUT.draft.md` | | `services/authelia-diff.sh` | Vergleicht `access_control:`-Sektion zwischen Repo-Baseline und Host-Datei; wird vom Posture-Check als Check `authelia_config_drift` aufgerufen | @@ -237,6 +238,7 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam |---|---|---| | `ops/borg-ui/scripts/pre-backup-dumps.sh` | Unraid Host, nicht Borg-UI Inline-Hook | erzeugt aktuelle Dumps unter `/mnt/user/backups/borg/dumps/latest` | | `services/posture-check/posture-check.sh` | Unraid Host | schreibt `/mnt/user/services/posture-check/last.json` und alarmiert via ntfy bei Warning/Critical | +| `services/posture-check/export-prometheus-textfile.sh` | Unraid Host, Cron/Textfile-Collector | schreibt Borg-, Critical-Container- und GitOps-Runtime-Image-Drift-Metriken fuer Prometheus | | `services/posture-check/docker-critical-events.sh` | Unraid Host | beobachtet Docker `die`/`oom`/`kill` Events und alarmiert via `homelab-alerts` | Das Skript liest Secret-Dateien auf dem Host und schreibt Dump-Artefakte. Bei Analyse niemals Secret-Inhalte ausgeben. diff --git a/monitoring/prometheus/alerts.yml b/monitoring/prometheus/alerts.yml index b486961..970d3ba 100644 --- a/monitoring/prometheus/alerts.yml +++ b/monitoring/prometheus/alerts.yml @@ -140,6 +140,15 @@ groups: summary: "Critical container is down: {{ $labels.name }}" description: "The host textfile exporter reports that critical container {{ $labels.name }} is not running." + - alert: HomelabGitOpsRuntimeImageDrift + expr: homelab_gitops_runtime_image_match == 0 + for: 10m + labels: + severity: warning + annotations: + summary: "Runtime image drift: {{ $labels.name }}" + description: "Container {{ $labels.name }} is not running the image declared by its Compose config in project {{ $labels.project }}." + - name: homelab-meta rules: - alert: HomelabPrometheusTargetDown diff --git a/services/posture-check/export-prometheus-textfile.sh b/services/posture-check/export-prometheus-textfile.sh index d72be19..1c0adea 100755 --- a/services/posture-check/export-prometheus-textfile.sh +++ b/services/posture-check/export-prometheus-textfile.sh @@ -15,6 +15,51 @@ trap cleanup EXIT now="$(date +%s)" +emit_gitops_runtime_image_metrics() { + cat <<'EOF' +# HELP homelab_gitops_runtime_image_match Whether a running Compose container uses the image currently declared by its Compose config. +# TYPE homelab_gitops_runtime_image_match gauge +EOF + + if ! command -v jq >/dev/null 2>&1; then + return + fi + + docker ps \ + --filter label=com.docker.compose.project \ + --format '{{.Names}}\t{{.Label "com.docker.compose.project"}}\t{{.Label "com.docker.compose.service"}}\t{{.Label "com.docker.compose.project.config_files"}}\t{{.Label "com.docker.compose.project.environment_file"}}' | + while IFS="$(printf '\t')" read -r container project service config_files env_file; do + [ -n "$container" ] || continue + [ -n "$service" ] || continue + [ -n "$config_files" ] || continue + + config_file="${config_files%%,*}" + [ -f "$config_file" ] || continue + + compose_args=(-f "$config_file") + if [ -n "$env_file" ] && [ -f "$env_file" ]; then + compose_args+=(--env-file "$env_file") + fi + + expected="$( + docker compose "${compose_args[@]}" config --format json 2>/dev/null | + jq -r --arg service "$service" '.services[$service].image // empty' 2>/dev/null || true + )" + [ -n "$expected" ] || continue + + running="$(docker inspect -f '{{.Config.Image}}' "$container" 2>/dev/null || true)" + [ -n "$running" ] || continue + + match="0" + if [ "$running" = "$expected" ]; then + match="1" + fi + + printf 'homelab_gitops_runtime_image_match{name="%s",project="%s",service="%s"} %s\n' \ + "$container" "$project" "$service" "$match" + done +} + { cat <<'EOF' # HELP homelab_textfile_exporter_last_run_timestamp_seconds Unix timestamp of the last successful homelab textfile exporter run. @@ -34,6 +79,8 @@ EOF printf 'homelab_critical_container_running{name="%s"} %s\n' "$container" "$running" done + emit_gitops_runtime_image_metrics + cat <<'EOF' # HELP homelab_borg_last_completed_timestamp_seconds Unix timestamp of the most recent completed Borg backup job known to Borg UI. # TYPE homelab_borg_last_completed_timestamp_seconds gauge