Files

15 KiB
Raw Permalink Blame History

Monitoring stack — Garage cluster (vps01 + bigbox + vps02) + Vinograd WAN

Стек мониторинга для S3-кластера Garage (репликация RF=3, WireGuard 10.8.0.0/24) и внешнего канала связи объекта «Винный город» (провайдер Ростелеком). Расположен на bigbox в /opt/monitoring.

Архитектура

                        bigbox (10.8.0.2)
   ┌─────────────────────────────────────────────────┐
   │  prometheus :9090 (host net)                    │
   │      │  scrape ──► garage x3 :3903 (WG admin)   │
   │      ├──► node-exporter :9100 (bigbox)          │
   │      ├──► 10.8.0.1:3903 (vps01)                 │
   │      ├──► 10.8.0.4:3903 (vps02)                 │
   │      └──► blackbox :9115 (host net) → /health   │
   │                                                  │
   │  grafana :3001  (dashboards, alerts)             │
   │  loki    :3100  (logs)                           │
   │  promtail ── docker.sock ──► docker logs garage  │
   └─────────────────────────────────────────────────┘
        │                        │
        ▼ WireGuard 10.8.0.0/24  ▼ caddy (vps02)
   vps01 (10.8.0.1)         grafana.nixg.ru
   vps02 (10.8.0.4)         → 87.242.100.206 → reverse_proxy 10.8.0.2:3001

Прометеус и blackbox-exporter работают в host network (иначе не видят WireGuard-подсеть 10.8.0.0/24 хостa). Остальные — в bridge-сети monitoring.

Сервисы и порты

Сервис Порт Доступ Примечание
Prometheus 9090 локально/WG host network, retention 30d (TSDB)
Grafana 3001 браузер + публично 3000 занят gitea → 3001
Loki 3100 локально сбор логов, retention 7d
Promtail — docker.sock docker logs garage и др.
blackbox-exporter 9115 host network HTTP-пробы /health нод
node-exporter 9100 WG на каждой ноде (apt)

Публичный доступ

grafana.nixg.ru → 87.242.100.206 (vps02) → caddy → reverse_proxy 10.8.0.2:3001
  • Запись добавлена в /opt/caddy/Caddyfile на vps02: grafana.nixg.ru { reverse_proxy 10.8.0.2:3001 }
  • Caddy — docker-контейнер (network_mode: host), перезагрузка: docker exec caddy caddy reload --config /etc/caddy/Caddyfile
  • Сертификат Let's Encrypt выпускается автоматически.
  • Логин: estorozhenko (сменён с admin через UI), пароль — задан пользователем.
  • Read-only доступ: it@vinogorod.ru (роль Viewer, создан вручную в UI, пароль 1qazXSW2). Только просмотр дашбордов, без правки. Учётка в grafana-data (переживает пересоздание контейнера).

Garage admin API (метрики)

В Garage v2.1 admin API (включая /metrics в Prometheus-формате) слушает отдельный порт [admin] api_bind_addr. На всех трёх нодах прописано:

[admin]
api_bind_addr = "<WG-IP>:3903"   # bigbox 10.8.0.2, vps01 10.8.0.1, vps02 10.8.0.4
admin_token   = "..."
metrics_token = "..."

Проверка метрик:

curl -H "Authorization: Bearer TOKEN" http://10.8.0.2:3903/metrics

/health возвращает 200 при кворуме (≥2 нод), 503 иначе.

Порты 3903 и 9100 открыты только для WG-подсети:

  • vps01: ufw allow from 10.8.0.0/24 to any port 3903 (и 9100 аналогично)
  • vps02/bigbox: файрвол не блокирует (policy ACCEPT)

Запуск

cd /opt/monitoring
docker compose up -d

Пароль Grafana — переменная GRAFANA_ADMIN_PASSWORD в .env (по умолчанию admin). Пользователь Grafana: estorozhenko (сменён пользователем вручную).

Полезные команды

# статус кластера
docker run --rm -v /opt/garage/garage.toml:/etc/garage.toml:ro \
  -v /opt/garage/meta:/var/lib/garage/meta \
  --entrypoint /garage dxflrs/garage:v2.1.0 status

# метрики одной ноды (Prometheus-формат)
curl -s -H "Authorization: Bearer TOKEN" http://10.8.0.2:3903/metrics | head

# health
curl -s -o /dev/null -w "%{http_code}" http://10.8.0.2:3903/health

# таргеты прометея (все должны быть UP)
curl http://127.0.0.1:9090/api/v1/targets

# принудительная синхронизация mirror'а в gitea
curl -X POST -H "Authorization: token GITEA_TOK" \
  http://127.0.0.1:3000/api/v1/repos/estorozhenko/monitoring/mirror-sync

Алерты (prometheus/alerts.yml)

Алерт Условие Severity
GarageNodeDown up{job="garage"} == 0 (2м) critical
GarageNoQuorum <2 нод up (2м) critical
GarageResyncErrors block_resync_errored_blocks > 0 (10м) warning
GarageNodeUnstable cluster_layout_node_connected == 0 (5м) warning
TProxyDown up{job="tproxy"} == 0 (2м) critical
TProxyBackendErrors increase(tproxy_backend_dial_failures_total[5m]) > 0 (10м) warning
VinogradRostelecomDown probe_success{job="vinograd_wan"} == 0 (2м) critical

Примечание: метрики Garage из admin API (:3903) НЕ имеют префикса garage_ — это api_s3_request_counter, block_resync_*, cluster_*. Префикс garage_ только у garage_build_info, garage_local_disk_*, garage_replication_factor.

Vinograd WAN — внешний канал «Винный город» (Ростелеком)

Объект «Винный город» (г. Геленджик, ул. Туристическая, 25), канал Ростелеком (договор Бастион, Static IP). Адреса из «Реестра внешних каналов связи.ods» (закладка «Винный город»):

Адрес Роль
83.239.50.145 Шлюз (gateway) — поднимается от РТК
83.239.50.146 Наше оборудование (CPE, Static IP, /30)

Мониторинг через blackbox-exporter (ICMP-проба) → Prometheus job vinograd_wan:

  • Интервал scrape: 30s (графики скорости ответа каждые 30 секунд)
  • Метрики:
    • probe_success{job="vinograd_wan"} — доступность (1/0)
    • probe_icmp_duration_seconds{job="vinograd_wan",phase="rtt"} — RTT, сек
  • Лейблы instance: vinograd-gw-83.239.50.145, vinograd-cpe-83.239.50.146
  • Алерт: VinogradRostelecomDown (critical, 2м подряд недоступен)
  • Grafana: дашборд Vinograd WAN (RTT ms + availability), панели в фолдере Garage

Retention: глобальный 30d (прометеевский TSDB) — данные хранятся минимум неделю, что покрывает требование «хранить неделю» с запасом (жёсткий 7d для одного job требовал бы отдельного инстанса Prometheus).

Проверка вручную:

# ICMP-проба через blackbox (debug)
curl -s "http://127.0.0.1:9115/probe?target=83.239.50.146&module=icmp&debug=true"
# данные в Prometheus
curl -sG 'http://127.0.0.1:9090/api/v1/query' \
  --data-urlencode 'query=probe_success{job="vinograd_wan"}'

Статус 2026-09-08: шлюз 83.239.50.145 НЕ отвечает на ICMP (probe_success=0, совпадает с алертом UptimeKuma 08:07 MSK). Оборудование 83.239.50.146 отвечает ~13ms. Алерт VinogradRostelecomDown в состоянии FIRE до восстановления канала — это корректное отражение реальной аварии.

tproxy-server (vps03) — метрики WEB Proxy (этап 6, РЕШЕНО ✅)

tproxy-server (Telegram Desktop WEB Proxy) развёрнут на vps03 (77.67.89.154), admin-эндпоинты на loopback :8081: /healthz, /readyz, /metrics (Prometheus-формат, 13 счётчиков tproxy_*).

2026-09-08: vps03 подключён к WireGuard (10.8.0.3) — вместо SSH-туннеля. Админ-эндпоинты tproxy остались на loopback, но туннель tproxy-tunnel.service удалён не был (безвреден, оставлен на всякий случай). Prometheus берёт node-метрики vps03 напрямую по WG: 10.8.0.3:9100.

  • Локальный порт 18081 (не 8081 — тот на bigbox занят ICQ-веб-чатом!)
  • Prometheus скрейпит 127.0.0.1:18081 (job tproxy, host=vps03), но через relabel_configs в prometheus.yml лейбл instance заменён на vps03:8081, чтобы в Grafana легенды показывали hostname, а не IP туннеля. Аналогично relabel сделан для garage (10.8.0.x:3903 → vps01|bigbox|vps02:3903) и node (10.8.0.x:9100 → hostname).
  • Дашборд: 3 панели tproxy (sessions/streams, traffic /sec, backend errors)
  • Алерты: TProxyDown (critical), TProxyBackendErrors (warning)

Подробности — в PLAN.md (этап 6) и EXPERIENCE.md.

Node-дашборды (диски/память/сеть/доступность) — этап 7, РЕШЕНО ✅

На каждую ноду (vps01, vps02, vps03, bigbox) — отдельный дашборд, grafana/dashboards/nodes/node-<host>.json (uid node-<host>, title "Node "). Генерируются скриптом /tmp/gen_nodes_dash.py (в git не хранится; при желании перенести — в EXPERIENCE.md). Файлы дашбордов разложены по подпапкам (папки в UI Grafana совпадают):

  • grafana/dashboards/ — garage-cluster.json → папка Garage
  • grafana/dashboards/nodes/ — node-*.json → папка nodes
  • grafana/dashboards/vinogorod/ — vinograd-wan.json → папка vinogorod

Панели (все по {host="<host>"}, job=node):

  1. Availability — stat-панель up{job="node",host=...} (UP/DOWN)
  2. Disk free (GB) — node_filesystem_avail_bytes (без tmpfs/overlay/squashfs)
  3. Memory available (GB) — node_memory_MemAvailable_bytes
  4. Load — node_load1/5/15
  5. Net RX/TX (bytes/s) — rate(node_network_{receive,transmit}_bytes_total[5m])
  6. Net utilization (%) — (rx+tx)*8/speed*100

Физические интерфейсы: vps01=eth0, vps02=enp3s0, vps03=ens1, bigbox=eno1. Контейнерные (docker0/veth*/br-*) не включены.

Тонкость node-exporter 1.9: на виртуалках vps01/vps02 node_network_speed_bytes = -125000 (sysfs speed=-1 → умножается на 125000), поэтому для них скорость линка захардкожена 125000000 Б/с (1 Gbit/s) в формуле утилизации. На физических (vps03/bigbox) используется сама метрика node_network_speed_bytes (=1.25e+08).

Катастрофоустойчивость

Проект хранится в двух git-репозиториях — на случай поломки bigbox:

Репозиторий Роль Адрес
gitverse.ru (облако) источник истины git@gitverse.ru:kpa39l/monitoring.git
gitea на bigbox pull-mirror (8h) https://gitea.nixg.ru/estorozhenko/monitoring

Порядок работы: правки коммитятся и пушутся в gitverse (источник), gitea подтягивает их mirror'ом (каждые 8ч или вручную mirror-sync). Если bigbox сломается — репозиторий остаётся в облаке gitverse.

VESTI (2026-09-13)

Проект /opt/vesti: web-интерфейс (:8400, systemd), publisher-контейнер (docker vesti-publisher, :8410/healthz), SOCKS5-туннель Telegram (:1080), SQLite БД (vesti.db).

Метрики (textfile-коллектор node-exporter, job node, label component="vesti")

Скрипт /opt/vesti/scripts/vesti-metrics.sh (запуск: cron.d vesti-metrics, root, каждую минуту; альтернативно systemd-таймер vesti-metrics.timer) пишет /var/lib/node_exporter/textfile_collector/vesti.prom. node-exporter на bigbox: ARGS="--web.listen-address=10.8.0.2:9100 --collector.textfile.directory=/var/lib/node_exporter/textfile_collector".

Метрика Смысл
vesti_web_up web :8400 (systemd)
vesti_publisher_health publisher :8410 /healthz
vesti_publisher_docker контейнер publisher (Up)
vesti_telegram_tunnel SOCKS5-туннель :1080
vesti_web_systemd systemd-юнит web
vesti_publisher_bot / _proxy / _channels детали publisher
vesti_db_ok SQLite vesti.db доступна
vesti_db_posts_total / _new_total / _published_total счётчики БД
vesti_db_last_run_ts Unix-ts последнего рan краулера

Дашборд

grafana/dashboards/vesti/vesti.json (uid vesti, папка vesti), 18 панелей. Провайдер vesti-dashboards в grafana/provisioning/dashboards/dashboards.yml. Проверка импорта: sudo docker cp grafana:/var/lib/grafana/grafana.db /tmp/gf.db

  • SELECT uid,title FROM dashboard WHERE uid='vesti'.

Алерты (группа vesti в alerts.yml)

Алерт Выражение Удержание Severity
VestiWebDown vesti_web_up{component="vesti"} == 0 2m critical
VestiPublisherDown vesti_publisher_health{component="vesti"} == 0 2m critical
VestiPublisherContainerDown vesti_publisher_docker{component="vesti"} == 0 2m critical
VestiTunnelDown vesti_telegram_tunnel{component="vesti"} == 0 2m critical
VestiDbDown vesti_db_ok{component="vesti"} == 0 2m critical
VestiDbStale time() - vesti_db_last_run_ts{component="vesti"} > 1800 5m warning

Реализовано через встроенный Prometheus Alerting (rule_files в prometheus.yml → alerts.yml; правила видны в /api/v1/rules). Перечитывается рестартом прометеуса (docker restart prometheus).