200+ серверов под контролем: внедряем Prometheus и Grafana для хостинг-провайдера

Задача клиента

К нам обратился технический директор хостинг-провайдера из Казани — компания, обслуживающая более 200 серверов в двух дата-центрах. На серверах размещались VPS клиентов, выделенные серверы, managed-хостинг и собственная инфраструктура.

Существующая система мониторинга (самописные Bash-скрипты + Nagios) перестала справляться:

  • Алерты приходили с опозданием 5-10 минут — клиенты узнавали о проблемах раньше техподдержки
  • Нет исторических данных — невозможно анализировать тренды и планировать мощности
  • Ручное добавление каждого сервера — при расширении инфраструктуры мониторинг отставал
  • Отсутствие дашбордов — для оценки состояния инфраструктуры нужно было подключаться к каждому серверу
  • Нет группировки алертов — при сетевом сбое приходили сотни одинаковых уведомлений

Мы предложили внедрение Prometheus + Grafana + Alertmanager — стандарт мониторинга, который используют компании от стартапов до Google и Amazon.

Проектирование архитектуры мониторинга

Мониторинг — это глаза и уши системного администратора. Без мониторинга вы узнаёте о проблемах от пользователей, с мониторингом — до того, как пользователи их заметят.

Мы выбрали Prometheus + Grafana по следующим причинам:

  • Prometheus — система сбора и хранения метрик с мощным языком запросов PromQL
  • Grafana — платформа визуализации с интерактивными дашбордами
  • Alertmanager — система управления алертами с маршрутизацией и группировкой
  • Exporters — агенты сбора метрик для любого софта (node, mysql, postgres, nginx, etc.)

Преимущества перед имеющимся Nagios:

  • Pull-модель — Prometheus сам забирает метрики, не нужен агент с push
  • Service discovery — автоматическое обнаружение новых серверов
  • PromQL — выразительный язык запросов
  • Cloud-native — идеален для Kubernetes и Docker
  • Экосистема — тысячи готовых экспортёров и дашбордов

Схема, которую мы спроектировали

Архитектура мониторинга для 200+ серверов:

Серверы (targets):
  ├── node_exporter (:9100)     → метрики ОС (CPU, RAM, Disk, Network)
  ├── mysqld_exporter (:9104)   → метрики MySQL
  ├── postgres_exporter (:9187) → метрики PostgreSQL
  └── nginx_exporter (:9113)    → метрики Nginx

Prometheus (:9090)
  ├── Scrape targets → Pull метрики каждые 15 секунд
  ├── TSDB → Хранение метрик (по умолчанию 15 дней)
  ├── PromQL → Вычисление и агрегация
  └── Alert Rules → Определение условий алертов
        ↓
Alertmanager (:9093)
  ├── Группировка → Объединение однотипных алертов
  ├── Маршрутизация → Направление по командам/каналам
  ├── Silencing → Подавление при обслуживании
  └── Receivers → Email, Telegram, Slack, PagerDuty

Grafana (:3000)
  ├── Data Source → Prometheus
  ├── Dashboards → Визуализация метрик
  └── Alerts → Дополнительные правила алертов

Расчёт ресурсов для 200+ серверов

Мы рассчитали необходимые ресурсы для Prometheus-сервера:

СерверовRAM PrometheusДиск (15 дней)CPU
1-101-2 ГБ10-20 ГБ1 ядро
10-504-8 ГБ50-100 ГБ2 ядра
50-20016-32 ГБ200-500 ГБ SSD4 ядра

Для нашего клиента с 200+ серверами мы выделили сервер с 32 ГБ RAM, 500 ГБ NVMe SSD и 4 ядрами CPU. Для долгосрочного хранения (6+ месяцев) мы запланировали второй этап с VictoriaMetrics.

Установка Prometheus на выделенный сервер мониторинга

Мы установили Prometheus из официальных бинарных файлов — это самый надёжный способ для production.

Установка и systemd unit

Вот пошаговая установка, которую мы выполнили:

# Создаём пользователя
useradd -r -s /usr/sbin/nologin -M prometheus

# Создаём директории
mkdir -p /etc/prometheus /var/lib/prometheus
chown prometheus:prometheus /var/lib/prometheus

# Скачиваем последнюю версию
PROM_VER="2.52.0"
wget https://github.com/prometheus/prometheus/releases/download/v${PROM_VER}/prometheus-${PROM_VER}.linux-amd64.tar.gz
tar xzf prometheus-${PROM_VER}.linux-amd64.tar.gz
cd prometheus-${PROM_VER}.linux-amd64

# Копируем бинарники и конфиги
cp prometheus promtool /usr/local/bin/
cp -r consoles console_libraries /etc/prometheus/
chown -R prometheus:prometheus /etc/prometheus

# Создаём systemd unit
cat > /etc/systemd/system/prometheus.service << 'EOF'
[Unit]
Description=Prometheus Monitoring
Wants=network-online.target
After=network-online.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecReload=/bin/kill -HUP $MAINPID
ExecStart=/usr/local/bin/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --storage.tsdb.path=/var/lib/prometheus \
  --storage.tsdb.retention.time=30d \
  --storage.tsdb.retention.size=50GB \
  --web.console.templates=/etc/prometheus/consoles \
  --web.console.libraries=/etc/prometheus/console_libraries \
  --web.enable-lifecycle \
  --web.enable-admin-api
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now prometheus
systemctl status prometheus

Конфигурация Prometheus с file-based service discovery

Для 200+ серверов мы использовали file-based service discovery — это позволяет добавлять серверы без перезапуска Prometheus:

# /etc/prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s
  scrape_timeout: 10s

# Подключение файлов с правилами алертов
rule_files:
  - "/etc/prometheus/rules/*.yml"

# Подключение Alertmanager
alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - "localhost:9093"

# Targets для сбора метрик
scrape_configs:
  # Метрики самого Prometheus
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]

  # Метрики серверов через node_exporter
  - job_name: "nodes"
    static_configs:
      - targets:
          - "10.0.0.10:9100"
          - "10.0.0.11:9100"
          - "10.0.0.12:9100"
        labels:
          env: "production"
      - targets:
          - "10.0.0.20:9100"
        labels:
          env: "staging"

  # Метрики Nginx
  - job_name: "nginx"
    static_configs:
      - targets: ["10.0.0.10:9113"]

  # Метрики PostgreSQL
  - job_name: "postgresql"
    static_configs:
      - targets: ["10.0.0.11:9187"]

  # File-based service discovery
  - job_name: "file_sd"
    file_sd_configs:
      - files:
          - "/etc/prometheus/targets/*.json"
        refresh_interval: 30s

Для динамического добавления серверов мы создали JSON-файлы:

# /etc/prometheus/targets/webservers.json
[
  {
    "targets": ["10.0.0.10:9100", "10.0.0.11:9100"],
    "labels": {
      "job": "webservers",
      "env": "production",
      "team": "platform"
    }
  }
]

Теперь для добавления нового сервера в мониторинг достаточно обновить JSON-файл — Prometheus подхватит изменения автоматически через 30 секунд.

Развёртывание Node Exporter на 200+ серверах

Экспортёры — это агенты, которые собирают метрики и отдают их Prometheus. Для развёртывания на 200+ серверах мы написали Ansible-роль.

Установка Node Exporter

Node Exporter устанавливается на каждый сервер. Вот конфигурация, которую мы стандартизировали:

# Создаём пользователя
useradd -r -s /usr/sbin/nologin -M node_exporter

# Скачиваем
NE_VER="1.8.1"
wget https://github.com/prometheus/node_exporter/releases/download/v${NE_VER}/node_exporter-${NE_VER}.linux-amd64.tar.gz
tar xzf node_exporter-${NE_VER}.linux-amd64.tar.gz
cp node_exporter-${NE_VER}.linux-amd64/node_exporter /usr/local/bin/

# Systemd unit
cat > /etc/systemd/system/node_exporter.service << 'EOF'
[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target

[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter \
  --collector.systemd \
  --collector.processes \
  --collector.tcpstat \
  --collector.textfile.directory=/var/lib/node_exporter/textfile_collector \
  --web.listen-address=:9100
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

mkdir -p /var/lib/node_exporter/textfile_collector
chown node_exporter:node_exporter /var/lib/node_exporter/textfile_collector

systemctl daemon-reload
systemctl enable --now node_exporter

# Проверяем
curl -s http://localhost:9100/metrics | head -20

Экспортёры для БД и веб-серверов

Помимо node_exporter, мы настроили специализированные экспортёры для сервисов клиента:

# === PostgreSQL Exporter ===
useradd -r -s /usr/sbin/nologin postgres_exporter

# Создаём пользователя мониторинга в PostgreSQL
sudo -u postgres psql << 'SQL'
CREATE USER monitoring WITH PASSWORD 'MonitorPass123';
GRANT pg_monitor TO monitoring;
SQL

# Устанавливаем
wget https://github.com/prometheus-community/postgres_exporter/releases/download/v0.15.0/postgres_exporter-0.15.0.linux-amd64.tar.gz
tar xzf postgres_exporter-0.15.0.linux-amd64.tar.gz
cp postgres_exporter-0.15.0.linux-amd64/postgres_exporter /usr/local/bin/

cat > /etc/systemd/system/postgres_exporter.service << 'EOF'
[Unit]
Description=PostgreSQL Exporter
After=network.target

[Service]
User=postgres_exporter
Environment=DATA_SOURCE_NAME=postgresql://monitoring:MonitorPass123@localhost:5432/postgres?sslmode=disable
ExecStart=/usr/local/bin/postgres_exporter
Restart=always

[Install]
WantedBy=multi-user.target
EOF

systemctl enable --now postgres_exporter

# === Nginx Exporter (требует stub_status) ===
wget https://github.com/nginxinc/nginx-prometheus-exporter/releases/download/v1.1.0/nginx-prometheus-exporter_1.1.0_linux_amd64.tar.gz
tar xzf nginx-prometheus-exporter_1.1.0_linux_amd64.tar.gz
cp nginx-prometheus-exporter /usr/local/bin/

cat > /etc/systemd/system/nginx_exporter.service << 'EOF'
[Unit]
Description=Nginx Exporter
After=network.target

[Service]
ExecStart=/usr/local/bin/nginx-prometheus-exporter --nginx.scrape-uri=http://127.0.0.1:8080/nginx_status
Restart=always

[Install]
WantedBy=multi-user.target
EOF

systemctl enable --now nginx_exporter

Кастомные метрики: бэкапы, SSL, бизнес-показатели

Мы настроили кастомные метрики через textfile collector — это позволило мониторить не только инфраструктуру, но и бизнес-критичные показатели:

# Метрика о последнем бэкапе (из скрипта бэкапа)
cat > /var/lib/node_exporter/textfile_collector/backup.prom << EOF
# HELP backup_last_success_timestamp Last successful backup timestamp
# TYPE backup_last_success_timestamp gauge
backup_last_success_timestamp{job="borg"} $(date +%s)
# HELP backup_last_size_bytes Size of last backup in bytes
# TYPE backup_last_size_bytes gauge
backup_last_size_bytes{job="borg"} 5368709120
# HELP backup_last_duration_seconds Duration of last backup
# TYPE backup_last_duration_seconds gauge
backup_last_duration_seconds{job="borg"} 342
EOF

# Метрика SSL-сертификата
DOMAIN="example.ru"
EXPIRY=$(echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_TS=$(date -d "$EXPIRY" +%s)
cat > /var/lib/node_exporter/textfile_collector/ssl.prom << EOF
# HELP ssl_certificate_expiry_timestamp SSL certificate expiry timestamp
# TYPE ssl_certificate_expiry_timestamp gauge
ssl_certificate_expiry_timestamp{domain="$DOMAIN"} $EXPIRY_TS
EOF

Теперь в дашборде Grafana видно, когда истекает SSL-сертификат каждого домена и когда был последний бэкап на каждом сервере.

PromQL: запросы, которые мы настроили для дашбордов

PromQL (Prometheus Query Language) — мощный язык для выборки и агрегации метрик. Мы написали десятки запросов для дашбордов клиента.

Основы PromQL, которые мы использовали

Базовые типы данных и селекторы:

# Простой селектор — все значения метрики
node_cpu_seconds_total

# Фильтрация по лейблам
node_cpu_seconds_total{mode="idle"}
node_cpu_seconds_total{mode!="idle", instance="10.0.0.10:9100"}

# Regex-фильтрация
node_cpu_seconds_total{mode=~"user|system"}
node_filesystem_avail_bytes{mountpoint!~"/snap.*|/boot.*"}

# Range vector — диапазон значений за период
node_cpu_seconds_total{mode="idle"}[5m]

# Offset — сдвиг во времени (для сравнения)
node_memory_MemAvailable_bytes offset 1h

Четыре типа метрик, которые мы объяснили дежурным инженерам клиента:

  • Counter — только растёт (запросы, ошибки, байты). Используется с rate() или increase()
  • Gauge — произвольное значение (температура, RAM, загрузка). Используется как есть
  • Histogram — распределение значений (время ответа)
  • Summary — квантили (percentiles)

Готовые запросы для ежедневного мониторинга

Вот библиотека PromQL-запросов, которые мы подготовили:

# === CPU ===
# Загрузка CPU в процентах (все ядра)
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# Загрузка CPU по ядрам
100 - (rate(node_cpu_seconds_total{mode="idle"}[5m]) * 100)

# === RAM ===
# Использование RAM в процентах
(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100

# Свободная RAM в ГБ
node_memory_MemAvailable_bytes / 1024^3

# === Диск ===
# Использование диска в процентах
(1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes) * 100

# Прогноз заполнения диска (через сколько часов закончится)
predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[24h], 3600 * 24 * 7) / 1024^3

# Скорость записи на диск (MB/s)
rate(node_disk_written_bytes_total[5m]) / 1024^2

# === Сеть ===
# Входящий трафик (Мбит/с)
rate(node_network_receive_bytes_total{device!~"lo|veth.*"}[5m]) * 8 / 1024^2

# Исходящий трафик
rate(node_network_transmit_bytes_total{device!~"lo|veth.*"}[5m]) * 8 / 1024^2

# === Системные ===
# Uptime в днях
(time() - node_boot_time_seconds) / 86400

# Количество запущенных процессов
node_procs_running

# Load average (1m) нормализованный по ядрам
node_load1 / count without (cpu) (node_cpu_seconds_total{mode="idle"})

Агрегация для обзорных дашбордов

Для обзорного дашборда всей инфраструктуры мы написали запросы агрегации:

# Средняя загрузка CPU по окружению
avg by (env) (100 - rate(node_cpu_seconds_total{mode="idle"}[5m]) * 100)

# Суммарная RAM всех серверов
sum(node_memory_MemTotal_bytes) / 1024^3

# Топ-5 серверов по загрузке CPU
topk(5, 100 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# Количество серверов по окружению
count by (env) (up{job="nodes"} == 1)

# Quantile — 95-й перцентиль времени ответа
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

# Математические операторы
# Объём свободного места на всех дисках (ГБ)
sum by (instance) (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}) / 1024^3

# Рост диска за неделю
node_filesystem_size_bytes - node_filesystem_avail_bytes
  - (node_filesystem_size_bytes offset 7d - node_filesystem_avail_bytes offset 7d)

Обзорный дашборд показывал состояние всех 200+ серверов на одном экране — дежурный инженер мог мгновенно определить проблемные серверы.

Grafana: дашборды для разных уровней

Grafana превращает сухие метрики в наглядные дашборды. Мы создали несколько уровней визуализации для разных ролей.

Установка Grafana

Установка Grafana OSS из официального репозитория:

# Добавляем репозиторий
apt install -y apt-transport-https software-properties-common
wget -q -O /usr/share/keyrings/grafana.key https://apt.grafana.com/gpg.key
echo "deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main" > /etc/apt/sources.list.d/grafana.list

# Устанавливаем
apt update && apt install grafana -y

# Настраиваем
# /etc/grafana/grafana.ini
[server]
http_port = 3000

[security]
admin_user = admin
admin_password = SecureGrafanaPass

[auth.anonymous]
enabled = false

# Запускаем
systemctl enable --now grafana-server
systemctl status grafana-server

Интерфейс доступен по адресу http://server:3000.

Подключение Prometheus и импорт дашбордов

Мы подключили Prometheus как Data Source и импортировали лучшие готовые дашборды:

# Подключение через API
curl -X POST http://admin:SecureGrafanaPass@localhost:3000/api/datasources \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Prometheus",
    "type": "prometheus",
    "url": "http://localhost:9090",
    "access": "proxy",
    "isDefault": true
  }'

# Или через GUI:
# Configuration -> Data Sources -> Add data source -> Prometheus
# URL: http://localhost:9090
# Save & Test

Импорт готовых дашбордов и создание кастомных

Мы импортировали проверенные дашборды и создали кастомные для специфики хостинг-провайдера:

# Через GUI: Dashboards -> Import -> ID

# Node Exporter Full — ID: 1860 (самый популярный)
# Node Exporter for Prometheus — ID: 11074
# Nginx — ID: 12708
# PostgreSQL — ID: 9628
# MySQL — ID: 7362
# Docker — ID: 893
# Blackbox Exporter — ID: 7587

# Через API (пример импорта Node Exporter Full)
curl -s https://grafana.com/api/dashboards/1860/revisions/latest/download | \
curl -X POST http://admin:SecureGrafanaPass@localhost:3000/api/dashboards/import \
  -H 'Content-Type: application/json' \
  -d "{
    \"dashboard\": $(cat),
    \"overwrite\": true,
    \"inputs\": [{\"name\": \"DS_PROMETHEUS\", \"type\": \"datasource\", \"pluginId\": \"prometheus\", \"value\": \"Prometheus\"}],
    \"folderId\": 0
  }"

Для клиента мы создали три уровня дашбордов: обзорный (все 200+ серверов на одном экране), детальный (конкретный сервер) и бизнес-метрики (SLA, доступность услуг).

Alertmanager: умные оповещения вместо спама

Главная боль клиента — при сетевом сбое Nagios засыпал дежурного сотнями одинаковых алертов. Alertmanager решает эту проблему через группировку, маршрутизацию и подавление.

Установка Alertmanager

Установка аналогична Prometheus:

useradd -r -s /usr/sbin/nologin -M alertmanager
mkdir -p /etc/alertmanager /var/lib/alertmanager

AM_VER="0.27.0"
wget https://github.com/prometheus/alertmanager/releases/download/v${AM_VER}/alertmanager-${AM_VER}.linux-amd64.tar.gz
tar xzf alertmanager-${AM_VER}.linux-amd64.tar.gz
cp alertmanager-${AM_VER}.linux-amd64/alertmanager /usr/local/bin/
cp alertmanager-${AM_VER}.linux-amd64/amtool /usr/local/bin/

chown -R alertmanager:alertmanager /etc/alertmanager /var/lib/alertmanager

cat > /etc/systemd/system/alertmanager.service << 'EOF'
[Unit]
Description=Alertmanager
Wants=network-online.target
After=network-online.target

[Service]
User=alertmanager
Group=alertmanager
Type=simple
ExecStart=/usr/local/bin/alertmanager \
  --config.file=/etc/alertmanager/alertmanager.yml \
  --storage.path=/var/lib/alertmanager \
  --web.listen-address=:9093
Restart=always

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now alertmanager

Конфигурация с Telegram-уведомлениями и маршрутизацией

Мы настроили маршрутизацию алертов по критичности — критические идут в Telegram и на почту, предупреждения только в Telegram:

# /etc/alertmanager/alertmanager.yml
global:
  resolve_timeout: 5m
  smtp_from: 'monitoring@itfresh.ru'
  smtp_smarthost: 'mail.itfresh.ru:587'
  smtp_auth_username: 'monitoring@itfresh.ru'
  smtp_auth_password: 'MailPassword'
  smtp_require_tls: true
  telegram_api_url: 'https://api.telegram.org'

templates:
  - '/etc/alertmanager/templates/*.tmpl'

route:
  receiver: 'telegram-critical'
  group_by: ['alertname', 'instance']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

  routes:
    # Критические алерты — Telegram + Email
    - match:
        severity: critical
      receiver: 'telegram-critical'
      repeat_interval: 1h

    # Предупреждения — только Telegram
    - match:
        severity: warning
      receiver: 'telegram-warning'
      repeat_interval: 4h

    # Информационные — только email
    - match:
        severity: info
      receiver: 'email-info'
      repeat_interval: 24h

receivers:
  - name: 'telegram-critical'
    telegram_configs:
      - bot_token: '1234567890:ABCdefGHIjklMNOpqrsTUVwxyz'
        chat_id: -1001234567890
        parse_mode: 'HTML'
        message: |-
          {{ range .Alerts }}
          {{ if eq .Status "firing" }}🔴{{ else }}🟢{{ end }} {{ .Labels.alertname }}
          Instance: {{ .Labels.instance }}
          Severity: {{ .Labels.severity }}
          Description: {{ .Annotations.description }}
          {{ end }}
    email_configs:
      - to: 'admin@itfresh.ru'
        send_resolved: true

  - name: 'telegram-warning'
    telegram_configs:
      - bot_token: '1234567890:ABCdefGHIjklMNOpqrsTUVwxyz'
        chat_id: -1001234567890
        parse_mode: 'HTML'
        send_resolved: true

  - name: 'email-info'
    email_configs:
      - to: 'team@itfresh.ru'
        send_resolved: true

inhibit_rules:
  - source_match:
      severity: 'critical'
    target_match:
      severity: 'warning'
    equal: ['alertname', 'instance']

Правило inhibit_rules подавляет warning, если уже есть critical — дежурный не будет засыпан дублями.

Правила алертов для хостинг-провайдера

Мы создали полный набор правил алертов, покрывающих все типовые инциденты:

# /etc/prometheus/rules/node_alerts.yml
groups:
  - name: node_alerts
    rules:
      # Сервер недоступен
      - alert: InstanceDown
        expr: up == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Instance {{ $labels.instance }} is down"
          description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 2 minutes."

      # CPU загрузка > 85%
      - alert: HighCpuUsage
        expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "High CPU usage on {{ $labels.instance }}"
          description: "CPU usage is {{ $value | printf \"%.1f\" }}% on {{ $labels.instance }}."

      # CPU загрузка > 95%
      - alert: CriticalCpuUsage
        expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 95
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Critical CPU usage on {{ $labels.instance }}"
          description: "CPU usage is {{ $value | printf \"%.1f\" }}% on {{ $labels.instance }}."

      # RAM использование > 90%
      - alert: HighMemoryUsage
        expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 90
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High memory usage on {{ $labels.instance }}"
          description: "Memory usage is {{ $value | printf \"%.1f\" }}% on {{ $labels.instance }}."

      # Диск заполнен > 85%
      - alert: DiskSpaceLow
        expr: (1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes) * 100 > 85
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Disk space low on {{ $labels.instance }}"
          description: "Disk {{ $labels.mountpoint }} is {{ $value | printf \"%.1f\" }}% full on {{ $labels.instance }}."

      # Диск заполнен > 95%
      - alert: DiskSpaceCritical
        expr: (1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes) * 100 > 95
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Disk space critical on {{ $labels.instance }}"
          description: "Disk {{ $labels.mountpoint }} is {{ $value | printf \"%.1f\" }}% full."

      # Высокий Network receive errors
      - alert: NetworkErrors
        expr: rate(node_network_receive_errs_total[5m]) > 10
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Network errors on {{ $labels.instance }}"
          description: "{{ $labels.device }} has {{ $value | printf \"%.0f\" }} receive errors/sec."

      # Бэкап устарел (> 26 часов)
      - alert: BackupStale
        expr: time() - backup_last_success_timestamp > 93600
        for: 0m
        labels:
          severity: warning
        annotations:
          summary: "Backup is stale on {{ $labels.instance }}"
          description: "Last backup was {{ $value | humanizeDuration }} ago."

      # SSL-сертификат истекает через 14 дней
      - alert: SSLCertExpiring
        expr: (ssl_certificate_expiry_timestamp - time()) / 86400 < 14
        for: 0m
        labels:
          severity: warning
        annotations:
          summary: "SSL certificate expiring for {{ $labels.domain }}"
          description: "Certificate expires in {{ $value | printf \"%.0f\" }} days."

Проверка и применение:

# Проверяем правила
promtool check rules /etc/prometheus/rules/node_alerts.yml

# Перезагружаем Prometheus
curl -X POST http://localhost:9090/-/reload

# Проверяем алерты в UI
# http://prometheus:9090/alerts

Best practices и автоматизация развёртывания

Для 200+ серверов ручное развёртывание невозможно. Мы автоматизировали весь процесс через Ansible и настроили безопасный доступ к мониторингу.

Лейблы, naming conventions и recording rules

Мы внедрили строгие naming conventions для поддержки порядка в инфраструктуре:

# Используйте осмысленные лейблы
- targets: ["10.0.0.10:9100"]
  labels:
    env: "production"      # Окружение
    role: "webserver"       # Роль сервера
    team: "platform"        # Ответственная команда
    dc: "msk-1"             # Дата-центр
    project: "website"      # Проект

# Naming conventions для recording rules (предвычисленные метрики)
# /etc/prometheus/rules/recording_rules.yml
groups:
  - name: node_recording_rules
    interval: 15s
    rules:
      # Предвычисляем частые запросы для ускорения дашбордов
      - record: instance:node_cpu_utilisation:rate5m
        expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

      - record: instance:node_memory_utilisation:ratio
        expr: 1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes

      - record: instance:node_disk_utilisation:ratio
        expr: 1 - node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}

Recording rules предвычисляют часто используемые запросы — дашборды загружаются мгновенно даже при 200+ серверах.

Безопасность и reverse proxy для Grafana

Мы закрыли все компоненты мониторинга за Nginx с аутентификацией:

# Reverse proxy для Prometheus с аутентификацией
# /etc/nginx/conf.d/prometheus.conf
server {
    listen 443 ssl http2;
    server_name monitoring.example.ru;

    ssl_certificate /etc/letsencrypt/live/monitoring.example.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/monitoring.example.ru/privkey.pem;

    # Basic Auth
    auth_basic "Monitoring";
    auth_basic_user_file /etc/nginx/.htpasswd;

    location /prometheus/ {
        proxy_pass http://127.0.0.1:9090/;
    }

    location /grafana/ {
        proxy_pass http://127.0.0.1:3000/;
        proxy_set_header Host $host;
    }

    location /alertmanager/ {
        proxy_pass http://127.0.0.1:9093/;
    }
}

# Создаём файл паролей
htpasswd -c /etc/nginx/.htpasswd admin

Для долгосрочного хранения на втором этапе мы запланировали:

  • VictoriaMetrics — drop-in замена Prometheus с 10x экономией RAM и диска
  • Thanos — бесконечное хранение в S3-совместимом хранилище
  • Grafana Mimir — горизонтально масштабируемое хранилище метрик

Ansible-роль для автоматического развёртывания

Для развёртывания node_exporter на все 200+ серверов мы написали Ansible-роль:

# ansible/roles/node_exporter/tasks/main.yml
---
- name: Create node_exporter user
  user:
    name: node_exporter
    system: yes
    shell: /usr/sbin/nologin
    create_home: no

- name: Download node_exporter
  get_url:
    url: "https://github.com/prometheus/node_exporter/releases/download/v{{ ne_version }}/node_exporter-{{ ne_version }}.linux-amd64.tar.gz"
    dest: "/tmp/node_exporter.tar.gz"

- name: Extract and install
  unarchive:
    src: "/tmp/node_exporter.tar.gz"
    dest: "/tmp/"
    remote_src: yes

- name: Copy binary
  copy:
    src: "/tmp/node_exporter-{{ ne_version }}.linux-amd64/node_exporter"
    dest: "/usr/local/bin/node_exporter"
    mode: '0755'
    remote_src: yes

- name: Create systemd service
  template:
    src: node_exporter.service.j2
    dest: /etc/systemd/system/node_exporter.service
  notify: restart node_exporter

- name: Start node_exporter
  systemd:
    name: node_exporter
    state: started
    enabled: yes
    daemon_reload: yes

Развёртывание на все серверы одной командой:

ansible-playbook -i inventory.ini playbook.yml --tags node_exporter

Весь процесс установки на 200+ серверов занял 15 минут.

Результаты внедрения

Проект был реализован за 8 рабочих дней. Вот измеримые результаты, которые получил клиент:

  • Время обнаружения инцидента — 30 секунд вместо 5-10 минут (алерт в Telegram приходит через 30 сек + 2 мин ожидания for)
  • 200+ серверов на едином дашборде — дежурный видит состояние всей инфраструктуры на одном экране
  • Автоматическое обнаружение новых серверов — добавление в мониторинг за 30 секунд через JSON-файл
  • Группировка алертов — при сетевом сбое приходит 1 уведомление вместо сотни
  • Исторические данные за 30 дней — возможность анализировать тренды и планировать мощности
  • Прогноз заполнения дисков — predict_linear показывает, когда нужно расширять хранилище
  • Мониторинг бэкапов и SSL — алерт, если бэкап не выполнялся >26 часов или сертификат истекает через 14 дней
  • SLA дашборд — руководство видит процент доступности каждого клиента

Через 2 месяца после внедрения количество обращений клиентов в техподдержку по поводу недоступности снизилось на 60% — потому что инженеры начали устранять проблемы до того, как клиенты их замечали. Если вам нужна профессиональная система мониторинга для любого масштаба инфраструктуры — обратитесь в АйТи Фреш, мы настроим мониторинг от одного сервера до сотен нод.

Часто задаваемые вопросы

Prometheus лучше для cloud-native и containerized инфраструктуры (Kubernetes, Docker), микросервисов и динамических окружений. Zabbix лучше для классической инфраструктуры с SNMP-устройствами, сетевым оборудованием и когда нужен встроенный agent-based мониторинг. Для нашего клиента — хостинг-провайдера с Linux-серверами — Prometheus оказался идеальным выбором благодаря PromQL и экосистеме экспортёров.

Prometheus потребляет примерно 1-2 байта RAM на каждый сэмпл в памяти. При scrape interval 15 секунд и 500 метриках с сервера: 10 серверов = ~1 ГБ RAM, 50 серверов = ~4 ГБ RAM, 200 серверов = ~16 ГБ RAM. Диск: примерно 1-2 байта на сэмпл после компрессии. За 30 дней при 10 серверах потребуется около 10-15 ГБ на SSD. Для нашего клиента с 200+ серверами выделили сервер с 32 ГБ RAM.

Три варианта: 1) Pushgateway — экспортёры пушат метрики на промежуточный сервер; 2) VPN — объединить все серверы в одну сеть (WireGuard); 3) SSH-туннельssh -L 9100:localhost:9100 user@remote-server; 4) Prometheus Federation — локальный Prometheus собирает метрики и отдаёт центральному. Для хостинг-провайдера мы использовали WireGuard VPN между двумя дата-центрами.

Добавьте кастомную метрику через textfile collector node_exporter. В конце скрипта бэкапа записывайте timestamp: echo 'backup_last_success_timestamp{job="borg"} '$(date +%s) > /var/lib/node_exporter/textfile_collector/backup.prom. Затем создайте правило алерта: time() - backup_last_success_timestamp > 93600 (26 часов). Мы настроили это на всех 200+ серверах клиента.

Да, используйте windows_exporter (ранее wmi_exporter). Он собирает метрики CPU, RAM, диска, сети, служб Windows и IIS. Установка через MSI-пакет или Chocolatey: choco install prometheus-windows-exporter. Порт по умолчанию — 9182. В Grafana есть готовые дашборды для Windows (ID: 14694).

Нужна помощь с настройкой?

Специалисты АйТи Фреш помогут с внедрением и настройкой — 15+ лет опыта, обслуживание от 15 000 ₽/мес

📞 Связаться с нами
#prometheus мониторинг#grafana установка#node exporter#promql запросы#alertmanager настройка#мониторинг серверов linux#grafana dashboard#prometheus настройка