server-monitoring/README.md
ShiZa039 74b6f92cb8 Стек мониторинга: Prometheus + Alertmanager + Grafana + node_exporter + cAdvisor
Самодостаточный docker compose стек с деплоем через deploy.sh,
bootstrap-установкой с нуля и конфигурацией через .env.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 12:54:40 +02:00

15 KiB
Raw Blame History

Мониторинг серверов

Самодостаточный стек: node_exporter + cAdvisor + Prometheus + Alertmanager + Grafana в одном docker compose. Вся конфигурация в репозитории; всё специфичное для конкретного сервера — только в .env. Один репозиторий раскатывается на любой сервер сменой .env.

Сервис Версия Порт (на BIND_IP) Зачем
node_exporter v1.9.1 9100 CPU, RAM, диски, температуры (hwmon), сеть, systemd-юниты
cAdvisor v0.52.1 — (только внутренняя сеть) метрики docker-контейнеров
Prometheus v3.5.0 LTS 9090 сбор и хранение (30 дней, scrape 30s)
Alertmanager v0.28.1 9093 API активных алертов + Telegram
Grafana 12.0.2 3001 дашборды (Node Exporter Full, Cadvisor exporter)

Наружу на 0.0.0.0 не публикуется ничего: каждый сервис слушает только BIND_IP.

Установка одной командой (bootstrap)

На чистом сервере достаточно указать репозиторий — bootstrap.sh сам поставит зависимости (git, curl, htpasswd, docker + compose через get.docker.com), склонирует репо в /opt/monitoring, при первом запуске интерактивно спросит BIND_IP (сам предложит tailnet-адрес, если есть tailscale), SERVER_NAME и пароли, создаст .env и запустит деплой:

curl -fsSL https://git.sab-core.ru/ShiZa/server-monitoring/raw/branch/main/bootstrap.sh \
  | sudo bash -s -- --repo https://git.sab-core.ru/ShiZa/server-monitoring.git

Флаги: --repo <url> (обязателен на чистом сервере), --dir <path> (по умолчанию /opt/monitoring), --branch <ветка>. Для приватного репо используй URL с токеном (https://user:token@…) или предварительно настрой SSH-ключ и передай ssh-URL.

Повторный запуск идемпотентен: git pull + deploy.sh, .env не перезаписывается — так же можно обновлять уже установленный сервер. Из существующего клона запускается без флагов: sudo bash bootstrap.sh.

Если терминала нет (например, запуск из CI), скрипт создаст .env из шаблона и остановится, попросив заполнить его и запустить bash deploy.sh.

Новый сервер за 5 минут (вручную)

Требования: Debian-подобная ОС, docker + плагин docker compose, curl.

git clone https://git.sab-core.ru/ShiZa/server-monitoring.git && cd server-monitoring
cp .env.example .env && chmod 600 .env
nano .env        # заполнить BIND_IP, SERVER_NAME, GRAFANA_ADMIN_PASSWORD, остальное по необходимости
bash deploy.sh

deploy.sh идемпотентен: проверяет окружение, рендерит конфиги из .env в rendered/ (в git не попадает), валидирует конфиг promtool-ом, поднимает стек и прогоняет smoke-тесты по всем пяти сервисам с выводом OK/FAIL. Запускать повторно можно всегда — после каждого изменения .env или правил.

Подробная установка

Шаг 0. Подготовка (для обоих вариантов)

# docker + compose уже должны стоять; проверка:
docker --version && docker compose version && curl --version | head -1

# клонировать репозиторий (путь любой, например /opt)
sudo git clone https://git.sab-core.ru/ShiZa/server-monitoring.git /opt/monitoring
cd /opt/monitoring
cp .env.example .env
chmod 600 .env      # в .env будут пароли — закрыть от чужих глаз

Дальше — по одному из двух вариантов.

Вариант 1: сервер в tailnet (Headscale/Tailscale)

1. Узнай tailnet-адрес сервера:

tailscale ip -4
# или, если команда недоступна:
ip -o -4 addr show tailscale0 | awk '{print $4}'

2. Заполни .env (минимум):

BIND_IP=100.64.0.5            # tailnet-адрес из шага 1
SERVER_NAME=srv-home          # уникальное имя этого сервера
GRAFANA_ADMIN_PASSWORD=<придумай>
METRICS_PASSWORD=             # можно пусто: снаружи tailnet порты недоступны
CRITICAL_CONTAINERS="authentik|forgejo"
CRITICAL_UNITS="headscale.service"

3. Деплой и проверка:

bash deploy.sh

Все пять проверок должны показать [OK]. Веб-интерфейсы открываются только с устройств твоего tailnet: Grafana http://100.64.0.5:3001, Prometheus http://100.64.0.5:9090, Alertmanager http://100.64.0.5:9093.

Замечание: если tailscaled стартует позже docker, после перезагрузки сервера контейнеры могут пару минут перезапускаться, пока tailnet-адрес не поднимется — это штатно. Хочешь убрать — добавь зависимость docker от tailscaled (systemctl edit docker.serviceAfter=tailscaled.service).

Вариант 2: сервер без tailnet (смотрит в мир напрямую)

1. Узнай публичный IP (из панели VPS-провайдера или ip -o -4 addr show; это должен быть адрес интерфейса сервера, а не NAT).

2. Заполни .env — здесь пароль на метрики обязателен:

BIND_IP=203.0.113.10          # публичный IP сервера
SERVER_NAME=vps-1
GRAFANA_ADMIN_PASSWORD=<придумай>
METRICS_USER=admin
METRICS_PASSWORD=<придумай>   # basic auth на Prometheus/Alertmanager/node_exporter
CRITICAL_CONTAINERS="..."     # что крутится на этом сервере
CRITICAL_UNITS="..."          # какие юниты критичны (headscale тут может не быть)

deploy.sh при пустом METRICS_PASSWORD выдаст предупреждение — на публичном сервере не игнорируй его.

3. Ограничь доступ фаерволом (рекомендуется, вторая линия защиты после basic auth). Пример для ufw — доступ к мониторингу только со своих IP:

MY_IP=<твой домашний/рабочий IP>
for p in 9090 9093 9100 3001; do sudo ufw allow from $MY_IP to any port $p proto tcp; done
for p in 9090 9093 9100 3001; do sudo ufw deny $p/tcp; done
sudo ufw status numbered

4. Деплой и проверка:

bash deploy.sh

Проверь, что без пароля API закрыт, а с паролем работает:

curl -s -o /dev/null -w '%{http_code}\n' http://203.0.113.10:9090/-/healthy          # 401
curl -su admin:ПАРОЛЬ -o /dev/null -w '%{http_code}\n' http://203.0.113.10:9090/-/healthy  # 200

Grafana (http://IP:3001) защищена собственным логином, cAdvisor наружу не публикуется вовсе.

После установки (оба варианта)

  1. Зайди в Grafana (admin / GRAFANA_ADMIN_PASSWORD) → папка Monitoring → дашборды «Node Exporter Full» и «Cadvisor exporter» уже на месте.
  2. Проверь таргеты: http://BIND_IP:9090/targets — все четыре job (prometheus, node, cadvisor + сам alertmanager в разделе alerting) должны быть UP.
  3. Проверь температуры (раздел ниже) и Telegram: временно останови тестовый контейнер — через ~4 минуты придёт алерт ContainerDown, после запуска — resolved.

Переменные .env

Переменная Обязательная Что делает
BIND_IP да IP, на котором слушает стек: tailnet-IP (100.x.y.z) или публичный IP сервера
SERVER_NAME да лейбл server во всех метриках и алертах
GRAFANA_ADMIN_PASSWORD да пароль admin в Grafana
METRICS_USER / METRICS_PASSWORD для публичных серверов basic auth на Prometheus, Alertmanager и node_exporter. Пусто — без аутентификации (допустимо только когда BIND_IP доступен лишь по tailnet)
TG_BOT_TOKEN / TG_CHAT_ID нет Telegram-уведомления; пусто — алерты только в API/веб-интерфейсе
CRITICAL_CONTAINERS нет контейнеры, обязанные работать (ContainerDown), подстроки имени через |, например "authentik|forgejo"
CRITICAL_UNITS нет systemd-юниты, обязанные быть active (SystemdUnitDown), через |
*_PORT нет порты сервисов; Grafana по умолчанию 3001, чтобы не конфликтовать с forgejo

Значения с | — в двойных кавычках. Добавить контейнер или юнит под алерт = дописать его в переменную и снова запустить bash deploy.sh.

Безопасность: tailnet против «смотрит в мир»

  • Сервер в tailnet: BIND_IP = tailnet-адрес, METRICS_PASSWORD можно оставить пустым — доступ и так только из tailnet.
  • Сервер с публичным IP: METRICS_PASSWORD обязателен (deploy.sh сгенерирует bcrypt и включит basic auth на Prometheus/Alertmanager/node_exporter; Grafana защищена своим логином; cAdvisor наружу не торчит вообще). Дополнительно стоит ограничить порты 9090/9093/9100/3001 фаерволом по своим IP.
  • BIND_IP=127.0.0.1 (только SSH-туннель) не поддерживается: Prometheus из контейнера не достучится до node_exporter на loopback хоста.
  • Если BIND_IP — tailnet-адрес и tailscaled стартует позже docker, контейнеры при загрузке могут несколько раз перезапуститься, пока адрес не появится — это нормально (restart: unless-stopped доведёт до рабочего состояния).

Трей-клиент забирает активные алерты с http://BIND_IP:9093/api/v2/alerts (с basic auth, если включён).

Алерты

Статические (prometheus/rules/alerts.yml): InstanceDown (critical, 2м), HighCPU >90% 5м, HighRAM >90% 5м, DiskFull >85% warning / >95% critical, HighTemp >80°C 3м (сенсоры coretemp/k10temp). Генерируемые из .env (rendered/generated-rules.yml): SystemdUnitDown (critical), ContainerDown (critical, ловит и остановленный, и удалённый контейнер). Critical-алерт глушит одноимённый warning (inhibit_rules).

Температура CPU

Метрики берутся из hwmon (node_hwmon_temp_celsius). Проверка на сервере:

curl -su "$METRICS_USER:$METRICS_PASSWORD" http://$BIND_IP:9100/metrics | grep node_hwmon_temp_celsius

Если пусто — на хосте не загружен драйвер сенсоров:

ls /sys/class/hwmon/                 # пусто или только nvme/acpi?
sudo modprobe coretemp               # Intel
sudo modprobe k10temp                # AMD
echo coretemp | sudo tee /etc/modules-load.d/sensors.conf   # автозагрузка

Правило HighTemp фильтрует чипы по regex coretemp|k10temp — если у железа другой чип (смотри лейбл chip в метриках), поправь regex в prometheus/rules/alerts.yml.

Как добавить удалённый сервер (пока только описание)

Вариант A — центральный Prometheus + экспортеры на удалёнке (проще, для серверов в одном tailnet):

  1. На удалённом сервере поднять из этого же репозитория только node_exporter и cadvisor (свой .env с его tailnet-IP).
  2. В prometheus/prometheus.yml.tpl центрального сервера добавить в scrape_configs job с таргетом 100.x.y.z:9100 (и при необходимости пробросить порт cAdvisor на tailnet-IP удалёнки) с лейблом server.
  3. bash deploy.sh на центральном — правила и дашборды подхватят новый сервер автоматически (везде есть лейбл server/instance).

Вариант B — полный стек на каждом сервере (для серверов вне tailnet, «смотрят в мир»): каждый сервер алертит сам за себя в тот же Telegram-чат, а централизованный обзор — либо federation (центральный Prometheus забирает срез метрик с https://ip:9090/federate с basic auth), либо просто второй datasource в центральной Grafana. Не требует связности между серверами, кроме доступа к 9090.

Эксплуатация

docker compose ps                  # статус
docker compose logs -f prometheus  # логи сервиса
bash deploy.sh                     # применить изменения .env/правил (идемпотентно)
docker compose pull && bash deploy.sh   # обновление образов (после смены тегов в compose)
docker compose down                # остановить (данные в volumes сохраняются)

Существующие контейнеры и сети (authentik, forgejo и т.д.) стек не трогает — их метрики читает cAdvisor через ro-маунты docker.