1. Архитектура: как работает стек
Три компонента решают три разные задачи:
Читает метрики из /proc и /sys (CPU, RAM, диск, сеть) и отдаёт их по HTTP на порту 9100. Это просто бинарник — он ничего не хранит и не отправляет.
Каждые 15 секунд сам ходит к Node Exporter и забирает (scrape) метрики. Хранит их до 30 дней. Принимает PromQL-запросы от Grafana.
Подключается к Prometheus как к источнику данных, строит дашборды и отправляет уведомления в Telegram, Slack или email при срабатывании алертов.
Поток данных
Требования к VPS
Для мониторинга 1–3 серверов: 1 ГБ RAM минимум, 2 ГБ комфортно. Диск: ~5 ГБ на 30 дней метрик одного сервера. Виртуализация — только KVM (Docker не работает на OpenVZ).
2. Установка через Docker Compose
Если Docker ещё не установлен — сначала установите его. Подробнее в статье Docker Compose на VPS. Иначе сразу создаём структуру проекта.
mkdir -p ~/monitoring && cd ~/monitoringФайл .env — пароль Grafana
# Пароль администратора Grafana
GRAFANA_ADMIN_PASSWORD=ЗАМЕНИТЕ_НА_СЛОЖНЫЙ_ПАРОЛЬ
# Замените на ваш домен (или оставьте для доступа по IP)
GRAFANA_ROOT_URL=https://grafana.example.comdocker-compose.yml
version: "3.9"
services:
# ── Prometheus: сбор и хранение метрик ───────────────────
prometheus:
image: prom/prometheus:v2.54.1
container_name: prometheus
restart: unless-stopped
ports:
- "127.0.0.1:9090:9090" # только localhost
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d' # хранить 30 дней
- '--storage.tsdb.path=/prometheus'
extra_hosts:
- "host.docker.internal:host-gateway" # доступ к хосту
# ── Grafana: дашборды и алерты ────────────────────────────
grafana:
image: grafana/grafana:11.4.0
container_name: grafana
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000" # только localhost
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}
- GF_USERS_ALLOW_SIGN_UP=false
- GF_SERVER_ROOT_URL=${GRAFANA_ROOT_URL}
depends_on:
- prometheus
# ── Node Exporter: метрики хоста ─────────────────────────
node-exporter:
image: prom/node-exporter:v1.8.2
container_name: node-exporter
restart: unless-stopped
network_mode: host # нужен для доступа к /proc и /sys хоста
pid: host
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
volumes:
prometheus_data:
grafana_data:Почему network_mode: host у Node Exporter?
Node Exporter должен читать метрики реального хоста, а не контейнера. При network_mode: host он слушает напрямую на порту 9100 хоста. Prometheus обращается к нему через псевдоним host.docker.internal, который Docker резолвит в IP шлюза (172.17.0.1 или аналог).
3. Настройка Prometheus
Создайте файл конфигурации в той же директории:
global:
scrape_interval: 15s # как часто собирать метрики
evaluation_interval: 15s # как часто проверять правила алертов
scrape_configs:
# Prometheus мониторит сам себя
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# Метрики этого сервера
- job_name: 'node'
static_configs:
- targets: ['host.docker.internal:9100']
labels:
instance: 'main-server' # замените на имя вашего сервераПосле изменения prometheus.yml перезагрузите конфиг без перезапуска контейнера:
curl -X POST http://localhost:9090/-/reload4. Запуск и вход в Grafana
cd ~/monitoring
# Запустить все сервисы в фоне
docker compose up -d
# Проверить статус
docker compose ps
# Убедиться, что Node Exporter отдаёт метрики
curl http://localhost:9100/metrics | head -20Через 30–60 секунд Grafana будет доступна. Пока Nginx ещё не настроен, проверьте через SSH-туннель с локального компьютера:
# Туннель: localhost:3000 → сервер:3000
ssh -L 3000:127.0.0.1:3000 user@YOUR_SERVER_IP
# Теперь откройте в браузере: http://localhost:3000Первый вход в Grafana
- 1Откройте
http://localhost:3000 - 2Логин: admin, пароль: тот, что задали в .env
- 3Connections → Data sources → Add data source → Prometheus
- 4URL:
http://prometheus:9090→ Save & test
5. Импорт готового дашборда
Не нужно строить графики вручную — на grafana.com/grafana/dashboards тысячи готовых дашбордов. Для Node Exporter лучший — Node Exporter Full (ID: 1860). Он показывает всё: CPU по ядрам, RAM, диски, сеть, температуру.
Как импортировать дашборд
- 1В Grafana: Dashboards → Import
- 2В поле "Import via grafana.com" введите 1860 → Load
- 3Выберите Prometheus как Data Source → Import
- 4Готово — сразу увидите все метрики сервера
Полный мониторинг Linux-сервера: CPU, RAM, диск, сеть. 20+ панелей.
Мониторинг самого Prometheus: scrape-лаги, размер хранилища, производительность.
Упрощённый вариант для тех, кому нужен минимум без лишнего.
Классический дашборд с акцентом на дисковые метрики.
6. Nginx и SSL для Grafana
Grafana слушает только на 127.0.0.1:3000 — наружу не доступна. Настроим Nginx как прокси с SSL.
apt install nginx certbot python3-certbot-nginx -yserver {
listen 80;
server_name grafana.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket — нужен для live-обновления дашбордов
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}ln -s /etc/nginx/sites-available/grafana /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
# SSL через Let's Encrypt
certbot --nginx -d grafana.example.comЗащита Prometheus
У Prometheus нет встроенной аутентификации. Если хотите веб-интерфейс Prometheus снаружи — добавьте HTTP Basic Auth в Nginx. Проще всего держать Prometheus только на localhost и работать с ним через SSH-туннель или Grafana.
7. Алерты в Telegram
Grafana Alerting (начиная с версии 8) умеет отправлять уведомления без сторонних инструментов. Настройка займёт 5 минут.
Шаг 1: создать Telegram-бота
- 1. Откройте @BotFather в Telegram →
/newbot - 2. Введите имя бота и username (должен заканчиваться на bot)
- 3. Сохраните токен вида
1234567890:AAF... - 4. Напишите боту любое сообщение (иначе бот не сможет отправлять вам)
Шаг 2: получить Chat ID
curl -s "https://api.telegram.org/botTOKEN/getUpdates" | python3 -c "
import sys, json
data = json.load(sys.stdin)
print(data['result'][0]['message']['chat']['id'])
"Если в группе — добавьте бота в группу, напишите там что-нибудь, затем выполните команду выше. Chat ID группы будет отрицательным числом.
Шаг 3: настроить Contact Point в Grafana
- 1. Alerting → Contact points → Add contact point
- 2. Name: Telegram, Integration: Telegram
- 3. Bot API Token: вставьте токен
- 4. Chat ID: вставьте числовой ID
- 5. Test → Save contact point
Шаг 4: создать правило алерта
Пример: алерт при загрузке CPU выше 85% на протяжении 5 минут.
- 1. Alerting → Alert rules → New alert rule
- 2. Data source: Prometheus, Query A:
100 - (
avg by(instance) (
rate(node_cpu_seconds_total{mode="idle"}[5m])
) * 100
)- 3. Reduce: Last, Threshold: IS ABOVE 85
- 4. Evaluation: каждые 1m, Pending period: 5m
- 5. Contact point: Telegram
- 6. Summary: "Высокая загрузка CPU на {{ $labels.instance }}"
- 7. Save rule
RAM > 90%
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
Threshold: > 90
Диск > 85%
100 - ((node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100)Threshold: > 85
Сервер недоступен
up{job="node"}Threshold: == 0
8. Мониторинг нескольких серверов
Prometheus и Grafana живут на одном сервере. На каждом дополнительном сервере нужно установить только Node Exporter — лёгкий бинарник без зависимостей.
Установка Node Exporter без Docker (на втором сервере)
# Скачать актуальный бинарник
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz
# Распаковать и установить
tar xzf node_exporter-*.tar.gz
cp node_exporter-*/node_exporter /usr/local/bin/
useradd -rs /bin/false node_exporter
# Создать systemd-юнит
cat > /etc/systemd/system/node_exporter.service << 'EOF'
[Unit]
Description=Node Exporter
After=network.target
[Service]
User=node_exporter
ExecStart=/usr/local/bin/node_exporter
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now node_exporter
# Проверить
curl http://localhost:9100/metrics | grep node_unameЗакрыть порт 9100 — только для вашего Prometheus
# Замените IP на адрес вашего Prometheus-сервера
ufw allow from PROMETHEUS_SERVER_IP to any port 9100
ufw deny 9100Добавить второй сервер в prometheus.yml
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
static_configs:
- targets:
- 'host.docker.internal:9100' # этот сервер
- '1.2.3.4:9100' # второй сервер
- '5.6.7.8:9100' # третий сервер
labels:
env: 'production'Применить изменения без перезапуска:
curl -X POST http://localhost:9090/-/reloadПосле этого в Grafana переключайте серверы через переменную instance — дашборд Node Exporter Full поддерживает это из коробки.
Метки для удобной фильтрации
Добавляйте метки к каждому серверу, чтобы различать их в Grafana:
- targets: ['1.2.3.4:9100']
labels:
instance: 'server-2-frankfurt'
role: 'web'9. Шпаргалка по PromQL
Эти запросы удобно вставлять в Grafana (Add panel → Query) или проверять прямо в интерфейсе Prometheus (/graph).
| Метрика | PromQL-запрос |
|---|---|
| Загрузка CPU, % | 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) |
| Использование RAM, % | (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 |
| Свободный диск, GB | node_filesystem_avail_bytes{mountpoint="/"} / 1024^3 |
| Диск занят, % | 100 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} * 100) |
| Входящий трафик, MB/s | rate(node_network_receive_bytes_total{device!="lo"}[5m]) / 1024^2 |
| Исходящий трафик, MB/s | rate(node_network_transmit_bytes_total{device!="lo"}[5m]) / 1024^2 |
| Uptime, дни | node_time_seconds - node_boot_time_seconds) / 86400 |
| Нагрузка (Load Average 1m) | node_load1 |
| Сервер online | up{job="node"} |
Полезные команды управления стеком
# Статус всех сервисов
docker compose -f ~/monitoring/docker-compose.yml ps
# Логи Prometheus
docker compose -f ~/monitoring/docker-compose.yml logs -f prometheus
# Горячая перезагрузка конфига Prometheus
curl -X POST http://localhost:9090/-/reload
# Проверить, что Prometheus видит все targets
curl -s http://localhost:9090/api/v1/targets | python3 -m json.tool | grep health
# Место на диске под метрики Prometheus
du -sh /var/lib/docker/volumes/monitoring_prometheus_data
# Бэкап volumes
docker run --rm \
-v monitoring_prometheus_data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/prometheus-$(date +%Y%m%d).tar.gz /data