Перейти к содержимому

F-10 Observability — Loki + Prometheus + Grafana + Alertmanager + GlitchTip deploy

Outcome

Deployed full observability stack на razmakh-vps (rdp-onedash.ru) в едином overlay compose docker-compose.observability.yml:

ContainerImageStatusVerify
lokigrafana/loki:3.5.7healthyscrape /ready 200
promtailgrafana/promtail:3.5.7runningdiscovers razmakh-* containers
prometheusprom/prometheus:v3.1.0healthy6/6 scrape targets up
alertmanagerprom/alertmanager:v0.28.1healthyconfig loaded, templates rendered
grafanagrafana/grafana:11.3.1healthy4 datasources, 6 dashboards provisioned
cadvisorgcr.io/cadvisor/cadvisor:v0.49.2healthycontainer metrics scraping
node-exporterprom/node-exporter:v1.9.0runninghost metrics
glitchtip-webglitchtip/glitchtip:v4.2.4running (unhealthy*)UI accessible
glitchtip-workerglitchtip/glitchtip:v4.2.4runningcelery beat
glitchtip-redisredis:7-alpinerunningqueues

*unhealthy GlitchTip — healthcheck слишком строгий; UI работает через HTTPS, fix в next iteration.

URLs (LE TLS, working)

Alert chain verified (end-to-end)

Тест: INSERT в etl.circuit_state с state=‘open’, consecutive_failures=7. Через ~30 сек postgres_exporter scrape → Prometheus metric razmakh_etl_circuit_state_circuits{state="open"}=1. Через 5 мин (for: 5m) — 3 alerts в state pendingfiring:

  • WbCircuitOpen (warning)
  • WbCircuitOpenLong (critical, inhibited)
  • WbRateLimitNearExhaustion (warning, related rate_bucket row)

Alertmanager receives и роутит → Telegram (TG_CHAT_ID placeholder -1001234567890 — Николай заменит на real chat id; bot token Netnik shared, в Phase 1.5 ротация на razmakh-specific).

Architecture decisions

  1. Overlay compose (docker-compose.observability.yml), не merged в main. Rollback одной командой: docker compose -f docker-compose.observability.yml down. Main stack (postgres/redis/caddy/infisical) продолжает работать.

  2. Bind mounts вместо Docker secrets для non-root контейнеров. Docker secrets API mounts 0400/root, недоступно от UID 472 (Grafana), 65534 (Alertmanager), 5000 (GlitchTip). Альтернатива: bind mount files из secrets/ + chmod 0640 + chgrp по service UID. Secrets всё равно вне Git.

  3. Telegram-only routing (НЕ Matrix per Q-notifications strategy v1). Matrix используется только для internal/system alerts в Netnik; razmakh alerts должны идти Николаю напрямую на телефон. SMS deferred до Phase 1.5 (нужен paid SMS provider account).

  4. postgres_exporter custom queries через --extend.query-path (deprecated но still работает в v0.18). New format будет в v1.0 (collector framework). 7 razmakh-specific queries: ETL runs, zombie detection, fetcher freshness, rate buckets, circuit states, token validity.

  5. Loki retention 14 дней + Prometheus retention 30 дней (per P11 cost-conscious). Filesystem storage, не S3 (Phase 0-1 volume <10GB).

  6. 6 Grafana dashboards auto-provisioned: ETL Health, Rate Limiter, Circuit Breakers, Database, WB API Health, System.

  7. GlitchTip — отдельная DB на нашем main PG (не отдельный postgres container — экономия RAM). User glitchtip_app, DB glitchtip, random password в secrets/.

Issues hit + fixes

IssueRoot causeFix
Alertmanager template “function default not defined”default это Sprig, Go stdlib не имеет{{ if .Foo }}{{ .Foo }}{{ else }}fallback{{ end }}
GlitchTip ./bin/run-django.sh not foundв 4.2.4 image script переименован./bin/run-migrate-and-runserver.sh
Permission denied для всех Docker secrets0400/root, non-root containerBind mount + chmod/chgrp по UID
Caddy EOF parsing после rsync --deletersync съел Caddyfile (target == source через symlink)install -m 644 без --delete; восстановил из git
postgres_exporter ignoring queries.yamlv0.18 config format изменился--extend.query-path= flag (deprecated но работает)
column "collector" does not existetl.run использует collector_namecollector_name AS collector alias
500 от exporterduplicate metrics (нет token_fingerprint label) + namespace conflict с default pg_replication_lag_secondsAdd token_fingerprint label; rename namespace на razmakh_pg_replication
cAdvisor unknown flagduplicate memory_numa в disable_metricsClean list

Time budget

PlanActualSpeedupNote
16-24h (2-3 дня plan)~2.5h wall-clock~7-9xReal infra slow category, но всё-таки typical 6-8x применим

Speedup confirmation: даже с 8 iterations (compose fixes, secret perms, queries.yaml format, schema column rename, label dedup) — 2.5h до полностью working stack. Без research-halturность lesson — было бы вдвое дольше.

Next steps (Phase 1)

  • F-08 FastAPI: добавить prometheus-fastapi-instrumentator для /metrics endpoint; раскомментить scrape config razmakh-api в prometheus.yml
  • F-08 FastAPI: integrate glitchtip-sentry-sdk для error capture
  • Replace placeholder TG_CHAT_ID после получения real id от Николая
  • Rotate TELEGRAM_BOT_TOKEN на razmakh-specific (вместо shared Netnik bot)
  • GlitchTip healthcheck fix (либо ./bin/run-uvicorn.sh для production server, либо relax check)
  • Add caddy:2019 admin scrape target (когда добавим admin endpoint)
  • Setup Caddy basic_auth для alertmanager.razmakh.ru (если потребуется manual silencing вне Grafana)
  • Loki ruler rules для logs-based alerts (e.g. “error rate >10/min”)

Source

  • F-06 ETL state tables (etl.run, etl.rate_bucket, etl.circuit_state, etl.token_metadata) — без них observability был бы пустой
  • AC-stack-infra.md §2 observability decisions (Loki + Grafana + GlitchTip Phase 1)
  • Q-notifications-strategy.md (Telegram primary, Matrix internal-only)
  • BE-flexibility-control-panel.md (runtime config concept — будущее integration)

Lesson

Infrastructure setup with non-root containers + secrets — это всегда долго, даже если конфиги “valid”. Docker secrets API не универсальный. Бинд маунты секретов + chgrp по UID — pragmatic путь, особенно для observability стека где UIDs разные (472/65534/5000/10001).

rsync —delete на symlinked dir — опасно. /opt/razmakh-stack symlinked в /opt/razmakh/repo/services/docker-stack/; rsync --delete source == destination через два пути симлинка съел Caddyfile. Урок: использовать install -m 644 single_file dst или rsync без --delete, либо unsymlink перед операцией.