infra: Compose-Stack mit Monitoring, ohne LiteLLM (Task 5.2)
ci / test (push) Successful in 28s

- deploy/compose/infra: Dienst litellm entfernt (D4), Router auf LITELLM_BASE_URL
  (appsrv08, Port noch offen), alle Images gepinnt, Grafana und pve-exporter
  ohne Profile, node-exporter für den LXC, Zustand über KNASTER_STATE_DIR
- Prometheus: GPU-VMs per file_sd (node, gpu), Proxmox-Knoten per IP, infra-node
- Grafana: Datasource und Dashboards provisioniert (grafana.com 10347, 1860,
  14574 sowie knaster-Übersicht)
- Alertmanager: n8n-Receiver nur vorbereitet (auskommentiert)
- Ansible: Rollen infra-stack und gpu-exporters, Playbooks monitoring.yml und
  monitoring-targets.yml, Skript monitoring-targets.py (tofu output → file_sd)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Rapp
2026-10-12 20:22:26 +02:00
co-authored by Claude Opus 5.5
parent fd48fd1c7f
commit bf7c137adf
22 changed files with 23101 additions and 66 deletions
+13 -5
View File
@@ -1,13 +1,21 @@
# Vorlage für deploy/compose/infra. Im Cluster schreibt die Ansible-Rolle infra-stack die Datei
# /opt/knaster/infra/.env (0600) im LXC infra; Geheimnisse erzeugt sie dort mit `openssl rand`.
KNASTER_VERSION=dev
KNASTER_SITE=knaster
KNASTER_STATE_DIR=.
KNASTER_API_TOKEN=change-me
KNASTER_DRY_RUN=true
KNASTER_CANARY_INTERVAL=60
POSTGRES_PASSWORD=change-me
LITELLM_API_KEY=change-me
LITELLM_MASTER_KEY=change-me
OPENROUTER_API_KEY=
RERANK_URL=http://gpu-embed-1:8082
GRAFANA_ADMIN_PASSWORD=change-me
GRAFANA_KIOSK_PASSWORD=change-me
# D4: LiteLLM mit PSE auf appsrv08, kein eigener LiteLLM im Stack.
# TODO (Nutzer, Task 6.3): Port festlegen. Der LiteLLM läuft auf appsrv08 nur im internen Netz
# (`litellm:4000`); am 12.10. waren von ops aus 4000/4001/8000 auf 192.168.100.29 geschlossen.
# Port auf appsrv08 für 192.168.100.0/24 freigeben (oder per Traefik) und hier eintragen.
LITELLM_BASE_URL=http://192.168.100.29:4000
LITELLM_API_KEY=
RERANK_URL=
NOTIFY_WEBHOOK_URL=
KNASTER_JOB_WEBHOOK_URL=
PORTAINER_HOST=
@@ -15,4 +23,4 @@ PORTAINER_KEY=
PROXMOX_URL=
PROXMOX_TOKEN_ID=
PROXMOX_TOKEN_SECRET=
KNASTER_WEBHOOK_AUTH=Bearer change-me
KNASTER_WEBHOOK_AUTH=
+52 -30
View File
@@ -1,5 +1,10 @@
# Rolle "infra" (LXC/VM auf einem i7-Node): Router, LiteLLM, Postgres, Monitoring.
# Start: cp .env.example .env && docker compose up -d
# Rolle "infra" (LXC 101 auf pve44, HA nach pve48/pve52): Router, TTS, Postgres und Monitoring in einem Stack.
# Kein eigener LiteLLM (D4): Der Router nutzt den LiteLLM mit PSE auf appsrv08 (LITELLM_BASE_URL).
#
# Lokal: cp .env.example .env && docker compose up -d
# Cluster: Ansible-Rolle infra-stack (platform/proxmox/ansible). Zustand und Geheimnisse liegen dort
# außerhalb des Repos in KNASTER_STATE_DIR=/opt/knaster/infra (.env, secrets/, file_sd/), Aufruf:
# docker compose -p knaster-infra --env-file /opt/knaster/infra/.env -f <repo>/deploy/compose/infra/docker-compose.yml up -d
name: knaster-infra
x-restart: &restart
@@ -13,29 +18,15 @@ services:
context: ../../..
dockerfile: deploy/docker/router.Dockerfile
image: knaster/router:${KNASTER_VERSION:-dev}
env_file: [{path: .env, required: false}]
env_file: [{path: "${KNASTER_STATE_DIR:-.}/.env", required: false}]
environment:
KNASTER_DB_URL: postgresql+psycopg://knaster:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD fehlt}@postgres:5432/knaster
KNASTER_SITE_DIR: /app/sites/${KNASTER_SITE:-knaster}
LITELLM_BASE_URL: http://litellm:4000
KNASTER_DRY_RUN: ${KNASTER_DRY_RUN:-true}
LITELLM_BASE_URL: ${LITELLM_BASE_URL:?LITELLM_BASE_URL fehlt (LiteLLM auf appsrv08, D4)}
ports: ["8080:8080"]
depends_on:
postgres: {condition: service_healthy}
litellm: {condition: service_started}
litellm:
<<: *restart
image: ghcr.io/berriai/litellm:${LITELLM_TAG:-main-stable} # vor Deployment auf feste Version pinnen
command: ["--config", "/app/config.yaml", "--port", "4000"]
env_file: [{path: .env, required: false}]
volumes:
- ../../litellm/config.yaml:/app/config.yaml:ro
ports: ["4000:4000"]
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:4000/health/liveliness', timeout=4)"]
interval: 30s
timeout: 5s
retries: 3
tts-piper:
<<: *restart
@@ -53,7 +44,7 @@ services:
postgres:
<<: *restart
image: pgvector/pgvector:pg16
image: pgvector/pgvector:0.8.7-pg16-trixie
environment:
POSTGRES_USER: knaster
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD fehlt}
@@ -66,42 +57,72 @@ services:
timeout: 5s
retries: 5
# --- Monitoring ---------------------------------------------------------------------------------
prometheus:
<<: *restart
image: prom/prometheus:v2.53.0
image: prom/prometheus:v3.13.4
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention.time=15d
- --web.enable-lifecycle
volumes:
- ../../prometheus:/etc/prometheus:ro
# file_sd: Ziele der GPU-VMs (DHCP, IPs wechseln), erzeugt auf ops aus `tofu output -json gpu_vms`
- ${KNASTER_STATE_DIR:-.}/file_sd:/etc/knaster-sd:ro
- prometheus:/prometheus
extra_hosts: ["host.docker.internal:host-gateway"] # node-exporter des LXC läuft im Host-Netz
ports: ["9090:9090"]
alertmanager:
<<: *restart
image: prom/alertmanager:v0.27.0
command: ["--config.file=/etc/alertmanager/alertmanager.yml"]
image: prom/alertmanager:v0.34.1
command:
- --config.file=/etc/alertmanager/alertmanager.yml
- --storage.path=/alertmanager
volumes:
- ../../alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
- ./secrets/router_token:/etc/alertmanager/router_token:ro
- ${KNASTER_STATE_DIR:-.}/secrets/router_token:/etc/alertmanager/router_token:ro
- alertmanager:/alertmanager
ports: ["9093:9093"]
grafana:
<<: *restart
image: grafana/grafana:11.1.0
profiles: ["dashboards"]
image: grafana/grafana:12.4.12
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:?GRAFANA_ADMIN_PASSWORD fehlt} # wirkt nur beim ersten Start
GF_USERS_ALLOW_SIGN_UP: "false"
GF_USERS_DEFAULT_THEME: dark
GF_ANALYTICS_REPORTING_ENABLED: "false"
GF_ANALYTICS_CHECK_FOR_UPDATES: "false"
GF_ANALYTICS_CHECK_FOR_PLUGIN_UPDATES: "false"
GF_NEWS_NEWS_FEED_ENABLED: "false"
GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH: /var/lib/grafana/dashboards/knaster-uebersicht.json
volumes:
- ../../grafana/provisioning:/etc/grafana/provisioning:ro
- ../../grafana/dashboards:/var/lib/grafana/dashboards:ro
- grafana:/var/lib/grafana
ports: ["3000:3000"]
depends_on: [prometheus]
pve-exporter:
<<: *restart
image: prompve/prometheus-pve-exporter:${PVE_EXPORTER_TAG:-latest} # vor Deployment pinnen
profiles: ["proxmox"]
image: prompve/prometheus-pve-exporter:3.10.1
# Token monitoring@pve!prom (PVEAuditor) als PVE_USER/PVE_TOKEN_NAME/PVE_TOKEN_VALUE/PVE_VERIFY_SSL
env_file: [{path: "${KNASTER_STATE_DIR:-.}/secrets/pve-exporter.env", required: false}]
expose: ["9221"]
node-exporter:
<<: *restart
image: prom/node-exporter:v1.12.1
command:
- --path.rootfs=/host
- --web.listen-address=:9100
network_mode: host
pid: host
volumes:
- ./secrets/pve.yml:/etc/prometheus/pve.yml:ro
ports: ["9221:9221"]
- /:/host:ro # ohne rslave: im unprivilegierten LXC nicht erlaubt
autoheal:
restart: unless-stopped
@@ -115,4 +136,5 @@ services:
volumes:
postgres: {}
prometheus: {}
alertmanager: {}
grafana: {}