services: prometheus: image: prom/prometheus:v3.3.1 container_name: prometheus restart: unless-stopped volumes: # Verzeichnis- statt Einzeldatei-Mount: Docker haengt Einzeldatei-Mounts am # Inode auf, und `git pull` ersetzt Dateien per Rename (neuer Inode) - der # Container sah die Aenderung dann nie, waehrend SIGHUP brav Erfolg meldete. # Verzeichnis-Mounts loesen bei jedem Zugriff ueber den Pfad auf. # Config-Pfade bleiben unveraendert (--config.file zeigt weiter dorthin). - ./prometheus:/etc/prometheus:ro - prometheus_data:/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.path=/prometheus' - '--storage.tsdb.retention.time=45d' - '--storage.tsdb.retention.size=15GB' # Remote-Write-Receiver: k3s-Cluster und Matrix-Server pushen hierher. # ACHTUNG: Port 9090 ist oeffentlich und ohne Auth erreichbar -- # per Hetzner Cloud Firewall auf die Absender-IPs einschraenken! - '--web.enable-remote-write-receiver' ports: - "9090:9090" networks: - traefik alertmanager: image: prom/alertmanager:v0.28.1 container_name: alertmanager restart: unless-stopped volumes: # Verzeichnis-Mount, gleicher Grund wie bei Prometheus. Die beiden .py aus # diesem Ordner liegen dadurch mit unter /etc/alertmanager - ungenutzt und # harmlos, alertmanager laedt ausschliesslich --config.file. - ./alertmanager:/etc/alertmanager:ro - alertmanager_data:/alertmanager command: - '--config.file=/etc/alertmanager/alertmanager.yml' - '--storage.path=/alertmanager' # bewusst kein oeffentlicher Port - nur Prometheus/Receiver im traefik-Netz networks: - traefik # Alertmanager-Webhook -> Matrix (wartung-Raum), gleiche Machart wie # maintenance-notify (gitops Issue #24). Secrets kommen aus .env. matrix-alerts: image: python:3.13-slim container_name: matrix-alerts restart: unless-stopped environment: - MATRIX_HOMESERVER=${MATRIX_ALERT_HOMESERVER} - MATRIX_ROOM_ID=${MATRIX_ALERT_ROOM_ID} - MATRIX_TOKEN=${MATRIX_ALERT_TOKEN} # Raum-Routing (gitops#47): Alerts mit Label room=security -> Security-Raum - MATRIX_ROOM_SECURITY=${MATRIX_RELEASE_ROOM_ID:-} # Ohne das liegt der State unter /tmp im Writable Layer: der ueberlebt # zwar ein "compose restart", aber kein "up -d", das den Container neu # baut -- also genau jeden Deploy. Dann verlieren offene Alarme ihre # Event-Zuordnung und loesen sich ueber den Fallback als separate # Nachricht auf, statt die Firing-Nachricht abzuhaken. - MATRIX_STATE_FILE=/state/matrix-alerts-state.json volumes: - ./alertmanager/matrix-alerts.py:/app/matrix-alerts.py:ro - matrix_alerts_data:/state command: python3 /app/matrix-alerts.py networks: - traefik # Release-/Advisory-Watch (gitops#22): meldet neue Releases der Element-Stack- # Upstreams in den Alerts-Raum (🚨 bei Security-Verdacht). Gleicher Bot/Raum # wie matrix-alerts, eigener State (Erstlauf merkt nur, flutet nicht). release-watch: image: python:3.13-slim container_name: release-watch restart: unless-stopped environment: - MATRIX_HOMESERVER=${MATRIX_ALERT_HOMESERVER} - MATRIX_ROOM_ID=${MATRIX_ALERT_ROOM_ID} # CVE-/Release-Raum (gitops#47): leer lassen = Fallback Alerts-Raum - MATRIX_RELEASE_ROOM_ID=${MATRIX_RELEASE_ROOM_ID:-} - MATRIX_TOKEN=${MATRIX_ALERT_TOKEN} volumes: - ./alertmanager/release-watch.py:/app/release-watch.py:ro - release_watch_data:/state command: python3 /app/release-watch.py networks: - traefik # CVE-Pipeline (gitops#47), Teil 1: Trivy scannt die real deployten Images # (cve/images.txt - Inventur aus dem Cluster, bei Stack-Aenderungen nachziehen) cve-scan: image: aquasec/trivy:0.58.2 container_name: cve-scan restart: unless-stopped entrypoint: ["/bin/sh", "/config/scan-loop.sh"] volumes: - ./cve:/config:ro - cve_results:/results - cve_trivy_cache:/root/.cache networks: - traefik # Teil 2: Reports -> Prometheus-Metriken (Schema + Pflichtfelder siehe gitops#47) cve-exporter: image: python:3.13-slim container_name: cve-exporter restart: unless-stopped environment: - RESULTS_DIR=/results - STATE_FILE=/state/first-seen.json volumes: - ./cve/cve-exporter.py:/app/cve-exporter.py:ro - cve_results:/results:ro - cve_exporter_state:/state command: python3 /app/cve-exporter.py networks: - traefik loki: image: grafana/loki:3.7.1 container_name: loki restart: unless-stopped volumes: - ./loki/loki-config.yaml:/etc/loki/local-config.yaml:ro - loki_data:/loki command: -config.file=/etc/loki/local-config.yaml # Port 3100 oeffentlich: externe Alloys pushen Logs hierher. # Ebenfalls per Firewall auf bekannte Absender-IPs einschraenken. ports: - "3100:3100" networks: - traefik alloy: image: grafana/alloy:v1.16.0 container_name: alloy restart: unless-stopped volumes: - ./alloy/config.alloy:/etc/alloy/config.alloy:ro - /var/log:/var/log:ro - /var/lib/docker/containers:/var/lib/docker/containers:ro - /var/run/docker.sock:/var/run/docker.sock:ro # Muss persistent sein, sonst ist die Positions-Datei nach jedem # Recreate weg und Alloy liest alle Container-Logs von vorn. Loki # weist die alten Eintraege dann ab (reject_old_samples, Default 7d). - alloy_data:/var/lib/alloy command: run --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy cap_add: - DAC_READ_SEARCH - NET_RAW networks: - traefik depends_on: - prometheus - loki grafana: image: grafana/grafana:12.0.0 container_name: grafana restart: unless-stopped environment: # Greift nur beim allerersten Start mit leerem Volume. # Der Live-Admin ist davon unabhaengig (Passwortaenderungen in der UI). - GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER} - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD} volumes: - grafana_data:/var/lib/grafana - ./grafana/provisioning:/etc/grafana/provisioning:ro - ./grafana/dashboards:/var/lib/grafana/dashboards:ro labels: - "traefik.enable=true" - "traefik.docker.network=traefik" - "traefik.http.routers.grafana.rule=Host(`selendis.axion1337.de`)" - "traefik.http.routers.grafana.entrypoints=websecure" - "traefik.http.routers.grafana.tls.certresolver=letsencrypt" - "traefik.http.services.grafana.loadbalancer.server.port=3000" depends_on: - prometheus - loki networks: - traefik node-exporter: image: prom/node-exporter:v1.9.1 container_name: node-exporter restart: unless-stopped volumes: - /proc:/host/proc:ro - /sys:/host/sys:ro - /:/rootfs:ro command: - '--path.procfs=/host/proc' - '--path.sysfs=/host/sys' - '--path.rootfs=/rootfs' - '--web.listen-address=:9100' - '--web.disable-exporter-metrics' ports: - "9100:9100" networks: - traefik volumes: prometheus_data: alertmanager_data: matrix_alerts_data: release_watch_data: cve_results: cve_trivy_cache: cve_exporter_state: grafana_data: loki_data: alloy_data: networks: traefik: external: true