# Мониторинг серверов Самодостаточный стек: **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](bootstrap.sh) сам поставит зависимости (git, curl, htpasswd, docker + compose через get.docker.com), склонирует репо в `/opt/monitoring`, при первом запуске интерактивно спросит `BIND_IP` (сам предложит tailnet-адрес, если есть tailscale), `SERVER_NAME` и пароли, создаст `.env` и запустит деплой: ```bash 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 ` (обязателен на чистом сервере), `--dir ` (по умолчанию `/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`. ```bash 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. Подготовка (для обоих вариантов) ```bash # 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-адрес сервера:** ```bash tailscale ip -4 # или, если команда недоступна: ip -o -4 addr show tailscale0 | awk '{print $4}' ``` **2. Заполни `.env`** (минимум): ```ini 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 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.service` → `After=tailscaled.service`). ### Вариант 2: сервер без tailnet (смотрит в мир напрямую) **1. Узнай публичный IP** (из панели VPS-провайдера или `ip -o -4 addr show`; это должен быть адрес интерфейса сервера, а не NAT). **2. Заполни `.env`** — здесь пароль на метрики **обязателен**: ```ini 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: ```bash 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 bash deploy.sh ``` Проверь, что без пароля API закрыт, а с паролем работает: ```bash 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`). Проверка на сервере: ```bash curl -su "$METRICS_USER:$METRICS_PASSWORD" http://$BIND_IP:9100/metrics | grep node_hwmon_temp_celsius ``` Если пусто — на хосте не загружен драйвер сенсоров: ```bash 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. ## Эксплуатация ```bash 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.