Эта статья — результат реальной настройки мониторинга для нескольких серверов и нескольких сайтов на одной машине. Здесь собрано то, что работает на практике: не только как поднять экспортеры, но и почему они не поднимаются с первого раза — и как это починить.
Что мы будем делать
Берём несколько Ubuntu-серверов и выстраиваем централизованную систему мониторинга:
- Prometheus и Grafana — в Docker Compose на одном центральном сервере
- Системные метрики каждого сервера через Node Exporter
- Метрики PostgreSQL через postgres_exporter
- Метрики Nginx с разделением по каждому сайту отдельно
- Prometheus сам ходит за метриками ко всем экспортерам по сети
Все команды проверены на практике. Типичные ошибки — в том числе неочевидный нюанс с Docker bridge и проблема с файрволом — разобраны отдельно.
Содержание
- Архитектура решения
- Prometheus и Grafana в Docker Compose
- Системные метрики: Node Exporter
- Метрики PostgreSQL: postgres_exporter
- Метрики Nginx: разделение по сайтам
- Настройка prometheus.yml
- Подключение Grafana к Prometheus
- Готовые дашборды
- Типичные ошибки и их решение
1. Архитектура решения
Прежде чем что-то устанавливать, важно понять общую схему. Prometheus работает по pull-модели: он сам периодически обходит экспортеры и забирает метрики. Grafana сама по себе ничего не собирает — она только отображает то, что хранит Prometheus.
В нашем случае схема выглядит так: на центральном сервере в Docker Compose запускаются Prometheus и Grafana. На всех остальных серверах — только экспортеры, никакого Prometheus там нет. Prometheus с центрального сервера сам ходит по сети к экспортерам и забирает метрики. Grafana читает данные из локального Prometheus внутри Docker-сети.
Важный нюанс про Docker bridge: Prometheus работает внутри контейнера. Если на том же физическом сервере установлен Node Exporter (вне Docker), то обратиться к нему через
localhost:9100из контейнера не получится — внутри контейнераlocalhostозначает сам контейнер. Нужно использовать адрес Docker bridge-интерфейса: обычно это172.17.0.1. Проверить можно командойip addr show docker0на хосте.
Важно про файрвол: Между серверами должны быть открыты нужные порты. Prometheus обращается к экспортерам по сети, и если где-то стоит UFW или iptables — это первое место, где всё может сломаться. Подробнее в разделе про типичные ошибки.
2. Prometheus и Grafana в Docker Compose
Prometheus и Grafana поднимаются на центральном сервере через Docker Compose. Для проксирования и SSL удобно использовать Traefik — он автоматически получает сертификаты Let's Encrypt и маршрутизирует запросы к нужным контейнерам по имени домена.
2.1. Структура файлов
Создаём директорию для проекта и файл конфигурации Prometheus:
mkdir -p /etc/docker/monitoring
cd /etc/docker/monitoring
nano prometheus.yml # Конфигурация разбирается в разделе 6
nano docker-compose.yml
2.2. docker-compose.yml
Минимальная конфигурация с Prometheus и Grafana (без Traefik — если хотите сразу открывать по IP и порту):
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
ports:
- '9090:9090' # Убрать если используете Traefik
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
environment:
- GF_SECURITY_ADMIN_PASSWORD=ЗАМЕНИТЕ_НА_НАДЁЖНЫЙ_ПАРОЛЬ
volumes:
- grafana-data:/var/lib/grafana
ports:
- '3000:3000' # Убрать если используете Traefik
depends_on:
- prometheus
volumes:
prometheus-data:
grafana-data:
Если используете Traefik — убираете блоки ports и добавляете нужные labels и networks. Тогда Grafana будет доступна по домену через HTTPS, а не по голому порту 3000.
2.3. Запуск
docker compose up -d
docker compose ps # Убеждаемся, что все контейнеры запустились
Grafana будет доступна по адресу http://<IP-сервера>:3000 (или по домену, если настроен Traefik). Логин и пароль по умолчанию: admin / admin, если не задан GF_SECURITY_ADMIN_PASSWORD. При первом входе система попросит сменить пароль — обязательно сделайте это.
2.4. Применение изменений в конфиге Prometheus
После правок в prometheus.yml не нужно пересоздавать контейнер — достаточно послать сигнал SIGHUP:
docker kill --signal=SIGHUP prometheus
Prometheus перечитает конфигурацию без потери собранных данных.
3. Системные метрики: Node Exporter
Node Exporter читает данные из /proc и /sys и отдаёт их Prometheus в нужном формате. Устанавливается на каждый сервер, который хотите мониторить — как на удалённые, так и на тот, где запущен Docker с Prometheus.
3.1. Устанавливаем Node Exporter
Создаём отдельного пользователя без прав на вход в систему — запускать экспортер от root не нужно:
sudo useradd --no-create-home --shell /bin/false node_exporter
Скачиваем и устанавливаем бинарный файл (проверьте актуальную версию на github.com/prometheus/node_exporter/releases):
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz
tar xvf node_exporter-1.8.2.linux-amd64.tar.gz
sudo cp node_exporter-1.8.2.linux-amd64/node_exporter /usr/local/bin/
sudo chown node_exporter:node_exporter /usr/local/bin/node_exporter
rm -rf node_exporter-1.8.2.linux-amd64*
3.2. Создаём systemd-сервис
sudo nano /etc/systemd/system/node_exporter.service
Содержимое:
[Unit]
Description=Node Exporter
After=network.target
[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter
[Install]
WantedBy=multi-user.target
3.3. Запускаем и проверяем
sudo systemctl daemon-reload
sudo systemctl start node_exporter
sudo systemctl enable node_exporter
sudo systemctl status node_exporter
Должно быть active (running). Убеждаемся, что экспортер слушает на всех интерфейсах, а не только на localhost:
sudo ss -tulpn | grep 9100
В выводе должно быть *:9100 или 0.0.0.0:9100. Если 127.0.0.1:9100 — значит в параметрах сервиса явно задан флаг --web.listen-address=127.0.0.1:9100, который нужно убрать. Метрики сервера теперь доступны по адресу http://<IP-сервера>:9100/metrics.
Node Exporter на сервере с Docker: если Node Exporter установлен на том же хосте, где запущен Prometheus в контейнере, в
prometheus.ymlнужно указывать неlocalhost:9100, а172.17.0.1:9100— адрес Docker bridge-интерфейса хоста. Иначе Prometheus просто не найдёт экспортер.
Что именно мониторит Node Exporter: загрузка CPU по ядрам, использование оперативной памяти и swap, дисковое пространство и I/O операции, сетевой трафик, среднее время нагрузки (load average), количество запущенных процессов и системное время работы сервера.
4. Метрики PostgreSQL: postgres_exporter
postgres_exporter устанавливается на тот же сервер, где работает база данных, и подключается к ней от имени специального пользователя с правами только на чтение статистики.
4.1. Создаём пользователя в PostgreSQL
Подключаемся к базе и выполняем:
CREATE USER postgres_exporter WITH PASSWORD 'ЗАМЕНИТЕ_НА_НАДЁЖНЫЙ_ПАРОЛЬ' CONNECTION LIMIT 5;
ALTER USER postgres_exporter SET search_path TO 'postgres_exporter', 'public';
GRANT CONNECT ON DATABASE your_database_name TO postgres_exporter; -- ЗАМЕНИТЕ ИМЯ БД
GRANT pg_monitor TO postgres_exporter; -- Для PostgreSQL 10+
Роль pg_monitor даёт доступ к статистическим представлениям без привилегий суперпользователя — именно то, что нужно.
4.2. Устанавливаем postgres_exporter
sudo useradd --no-create-home --shell /bin/false postgres_exporter
wget https://github.com/prometheus-community/postgres_exporter/releases/download/v0.15.0/postgres_exporter-0.15.0.linux-amd64.tar.gz
tar xvf postgres_exporter-0.15.0.linux-amd64.tar.gz
sudo cp postgres_exporter-0.15.0.linux-amd64/postgres_exporter /usr/local/bin/
sudo chown postgres_exporter:postgres_exporter /usr/local/bin/postgres_exporter
rm -rf postgres_exporter-0.15.0.linux-amd64*
4.3. Настраиваем строку подключения
Создаём файл с переменными окружения:
sudo nano /etc/default/postgres_exporter
Содержимое:
DATA_SOURCE_NAME="postgresql://postgres_exporter:ВАШИ_ДАННЫЕ@localhost:5432/your_database?sslmode=disable" # ЗАМЕНИТЕ
Ограничиваем права на файл — там хранится пароль:
sudo chmod 600 /etc/default/postgres_exporter
sudo chown postgres_exporter:postgres_exporter /etc/default/postgres_exporter
4.4. Создаём systemd-сервис и запускаем
sudo nano /etc/systemd/system/postgres_exporter.service
[Unit]
Description=Postgres Exporter
After=network.target
[Service]
User=postgres_exporter
Group=postgres_exporter
EnvironmentFile=/etc/default/postgres_exporter
ExecStart=/usr/local/bin/postgres_exporter
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl start postgres_exporter
sudo systemctl enable postgres_exporter
Метрики PostgreSQL доступны по адресу http://<IP-сервера>:9187/metrics.
5. Метрики Nginx: разделение по сайтам
Здесь есть важный нюанс, который легко пропустить: стандартный модуль stub_status в Nginx не умеет разделять статистику по виртуальным хостам. Он отдаёт суммарные глобальные метрики по всему Nginx — сколько бы сайтов на нём ни работало.
Поэтому если запустить один nginx_exporter на всё — в Grafana будет видна общая картина, но будет невозможно понять, какой из сайтов создаёт нагрузку или перестал отвечать.
Решение: для каждого сайта создать отдельный location для stub_status, запустить отдельный экземпляр nginx_exporter на своём порту с константной меткой site=имя-сайта. Тогда в Prometheus и Grafana каждая метрика будет знать, к какому сайту относится.
5.1. Добавляем stub_status в конфиги Nginx
В каждый HTTPS-блок (там, где listen 443 ssl) нужно добавить отдельный location. Используем уникальные пути для каждого сайта. Открываем конфиг первого сайта:
sudo nano /etc/nginx/sites-available/your-site # ЗАМЕНИТЕ ИМЯ ФАЙЛА
Внутри блока server { ... } для порта 443 добавляем (например, после location = /favicon.ico):
location /nginx_status_site1 { # ЗАМЕНИТЕ site1 НА УНИКАЛЬНОЕ ИМЯ
stub_status;
access_log off;
allow 127.0.0.1;
allow ::1;
deny all;
}
Для второго сайта — тот же блок, но путь другой: /nginx_status_site2. И так для каждого. Главное — уникальность пути и ограничение allow 127.0.0.1: экспортер будет обращаться только с localhost, посторонние статус не увидят.
После добавления во все конфиги проверяем синтаксис и перезагружаем Nginx:
sudo nginx -t
sudo systemctl reload nginx
Проверяем, что каждый endpoint отвечает:
curl http://localhost/nginx_status_site1
curl http://localhost/nginx_status_site2
Ответ должен выглядеть примерно так:
Active connections: 2
server accepts handled requests
145 145 312
Reading: 0 Writing: 1 Waiting: 1
Если 403 Forbidden — значит в allow что-то не так. Если 404 — location добавлен не в тот блок сервера.
5.2. Устанавливаем nginx_exporter
sudo useradd --no-create-home --shell /bin/false nginx_exporter
wget https://github.com/nginxinc/nginx-prometheus-exporter/releases/download/v1.3.0/nginx-prometheus-exporter_1.3.0_linux_amd64.tar.gz
tar xvf nginx-prometheus-exporter_1.3.0_linux_amd64.tar.gz
sudo cp nginx-prometheus-exporter /usr/local/bin/
sudo chown nginx_exporter:nginx_exporter /usr/local/bin/nginx-prometheus-exporter
rm nginx-prometheus-exporter*
5.3. Создаём отдельный сервис для каждого сайта
Для первого сайта:
sudo nano /etc/systemd/system/nginx_exporter_site1.service # ЗАМЕНИТЕ site1
[Unit]
Description=Nginx Exporter for site1.example.com # ЗАМЕНИТЕ НА ВАШ ДОМЕН
After=network.target
[Service]
User=nginx_exporter
Group=nginx_exporter
Type=simple
ExecStart=/usr/local/bin/nginx-prometheus-exporter \
--nginx.scrape-uri=http://localhost/nginx_status_site1 \ # ЗАМЕНИТЕ ПУТЬ
--web.listen-address=:9114 \ # УНИКАЛЬНЫЙ ПОРТ ДЛЯ КАЖДОГО САЙТА
--prometheus.const-label=site=site1.example.com # ЗАМЕНИТЕ НА ВАШ ДОМЕН
[Install]
WantedBy=multi-user.target
Для второго сайта создаём аналогичный файл nginx_exporter_site2.service, меняя три параметра: путь к nginx_status_site2, порт :9115 и метку site=site2.example.com. Каждый следующий сайт — следующий порт по порядку: 9116, 9117 и так далее.
Почему константная метка важна: флаг --prometheus.const-label=site=site1.example.com добавляет к каждой метрике этого экспортера дополнительное поле site. В Grafana потом можно написать запрос nginx_http_requests_total{site="site1.example.com"} и увидеть данные только для этого сайта.
Запускаем все созданные сервисы:
sudo systemctl daemon-reload
sudo systemctl start nginx_exporter_site1
sudo systemctl start nginx_exporter_site2
sudo systemctl enable nginx_exporter_site1
sudo systemctl enable nginx_exporter_site2
Проверяем, что метрики отдаются с нужной меткой:
curl http://localhost:9114/metrics | grep site
Должны увидеть строки вида nginx_connections_active{site="site1.example.com"} 2.
Что именно мониторит nginx_exporter: активные соединения, соединения в состоянии reading/writing/waiting, общее количество принятых и обработанных соединений, суммарное количество HTTP-запросов. На основе последней метрики строится RPS — количество запросов в секунду.
6. Настройка prometheus.yml
Вся конфигурация сбора метрик находится в одном файле prometheus.yml, который монтируется в контейнер. Prometheus сам ходит к каждому экспортеру по указанному адресу и забирает метрики с заданным интервалом.
Полная конфигурация для нашего случая — системные метрики нескольких серверов, PostgreSQL и Nginx с разделением по сайтам:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
# Мониторинг самого Prometheus
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# Node Exporter на сервере с Docker (не localhost, а bridge-адрес хоста)
- job_name: 'node_central'
static_configs:
- targets: ['172.17.0.1:9100'] # Docker bridge — адрес хоста внутри контейнера
labels:
server: 'central'
# Node Exporter на удалённом сервере
- job_name: 'node_remote'
static_configs:
- targets: ['192.168.1.10:9100'] # ЗАМЕНИТЕ: IP удалённого сервера
labels:
server: 'remote'
# Метрики PostgreSQL
- job_name: 'postgresql'
static_configs:
- targets: ['192.168.1.10:9187'] # ЗАМЕНИТЕ: IP сервера с postgres_exporter
labels:
server: 'remote'
# Метрики Nginx: каждый сайт — отдельная цель
- job_name: 'nginx_site1'
static_configs:
- targets: ['192.168.1.10:9114'] # ЗАМЕНИТЕ: IP сервера с Nginx
labels:
server: 'remote'
- job_name: 'nginx_site2'
static_configs:
- targets: ['192.168.1.10:9115'] # ЗАМЕНИТЕ
labels:
server: 'remote'
# Добавьте по аналогии для каждого nginx_exporter
После изменений применяем конфигурацию без перезапуска контейнера:
docker kill --signal=SIGHUP prometheus
Проверяем, что все цели подхватились: открываем веб-интерфейс Prometheus (Status → Targets) — все настроенные экспортеры должны отображаться со статусом UP.
Про метки (labels): метки
server,roleи другие вprometheus.ymlдобавляются ко всем метрикам конкретного job на уровне Prometheus. Это удобно для фильтрации в Grafana: можно сразу отобразить всё по конкретному серверу запросом видаnode_cpu_seconds_total{server="remote"}.
7. Подключение Grafana к Prometheus
Grafana и Prometheus работают в одной Docker-сети, поэтому Grafana обращается к Prometheus по имени контейнера — не по IP и не по внешнему адресу.
Заходим в Grafana, переходим в Connections → Data sources → Add data source, выбираем Prometheus. В поле Prometheus server URL указываем:
http://prometheus:9090
Называем источник понятно, например Prometheus. Нажимаем Save & test — должна появиться зелёная надпись об успешном подключении.
Почему по имени контейнера: Docker Compose автоматически создаёт внутреннюю DNS-запись для каждого сервиса. Контейнеры в одной сети резолвят друг друга по именам сервисов из
docker-compose.yml. Использоватьlocalhostнельзя по той же причине, что и с Node Exporter — внутри контейнераlocalhostэто сам контейнер.
8. Готовые дашборды
Создавать дашборды с нуля не нужно — на сайте Grafana есть тысячи готовых вариантов. Импорт занимает несколько секунд: в меню Dashboards → Import вводим ID и выбираем источник данных.
Рекомендуемые дашборды для нашей конфигурации: для системных метрик Node Exporter подходит ID 1860 («Node Exporter Full» — самый популярный и информативный). Для PostgreSQL хорошо работает ID 9628. Для Nginx — ID 12708. Для мониторинга самого Prometheus есть ID 3662 («Prometheus 2.0 Stats»).
После импорта дашборда для Nginx в настройках переменных можно задать фильтр по метке site — тогда в верхней части дашборда появится выпадающий список для переключения между сайтами.
9. Типичные ошибки и их решение
9.1. Prometheus не достучивается до экспортера: i/o timeout
Самая распространённая проблема при работе с несколькими серверами. В интерфейсе Prometheus (Status → Targets) видно что-то вроде:
Post "http://192.168.1.10:9100/metrics": dial tcp 192.168.1.10:9100: i/o timeout
Это означает, что сетевое соединение не установилось вообще. Причин может быть несколько, проверяем по порядку.
Сначала убеждаемся, что экспортер действительно запущен на целевом сервере:
sudo systemctl status node_exporter
sudo ss -tulpn | grep 9100
Если экспортер работает, проверяем файрвол на сервере с экспортером. Именно здесь чаще всего и зарыта проблема:
sudo ufw status
Если UFW активен и порта 9100 нет в списке разрешённых, добавляем правило — лучше ограниченное конкретным IP центрального сервера, а не открытое всему миру:
sudo ufw allow from 192.168.1.20 to any port 9100 proto tcp # ЗАМЕНИТЕ: IP центрального сервера
Для nginx_exporter аналогично открываем нужные порты. Если экспортеров несколько — можно разрешить диапазон:
sudo ufw allow from 192.168.1.20 to any port 9114:9121 proto tcp # ЗАМЕНИТЕ
После изменений проверяем доступность порта с центрального сервера:
nc -zv 192.168.1.10 9100 # ЗАМЕНИТЕ IP
Если подключение прошло — Connection to 192.168.1.10 9100 port [tcp/*] succeeded!.
9.2. Node Exporter на хосте недоступен из контейнера
Если Node Exporter установлен на том же сервере, где запущен Docker с Prometheus, и в prometheus.yml указан localhost:9100 или 127.0.0.1:9100 — Prometheus его не найдёт. Внутри контейнера localhost это сам контейнер, а не хост.
Решение: используем адрес Docker bridge-интерфейса. Проверяем его на хосте:
ip addr show docker0
Обычно это 172.17.0.1. Именно этот адрес и указываем в prometheus.yml:
- targets: ['172.17.0.1:9100']
После правки применяем изменения:
docker kill --signal=SIGHUP prometheus
9.3. Экспортер слушает только на localhost
Иногда экспортер запускается, но принимает соединения только с локальной машины. Симптом: curl http://localhost:9100/metrics работает, но с другого сервера — нет.
Проверяем вывод ss -tulpn: если там 127.0.0.1:9100, значит в ExecStart сервиса явно задан флаг --web.listen-address=127.0.0.1:9100. Убираем его или заменяем на --web.listen-address=:9100 (двоеточие без IP означает «все интерфейсы»):
sudo nano /etc/systemd/system/node_exporter.service
# Убираем или меняем флаг --web.listen-address
sudo systemctl daemon-reload
sudo systemctl restart node_exporter
9.4. Метрики Nginx не различаются по сайтам
Если все сайты в Grafana показывают одинаковые значения — скорее всего, запущен один общий nginx_exporter без метки site. Проверяем:
curl http://localhost:9113/metrics | grep site
Если метки нет — нужно создать отдельные сервисы для каждого сайта по инструкции из раздела 5, а старый общий экспортер остановить и отключить:
sudo systemctl stop nginx_exporter
sudo systemctl disable nginx_exporter
sudo rm /etc/systemd/system/nginx_exporter.service
sudo systemctl daemon-reload
9.5. Grafana не видит данные
Если источник данных добавлен, но графики пустые — проверяем URL источника данных. Grafana должна обращаться к Prometheus по имени контейнера (http://prometheus:9090), а не по localhost и не по внешнему IP. Также убеждаемся, что временной диапазон в правом верхнем углу Grafana не слишком узкий — метрики могут просто не успеть накопиться.
Заключение
Теперь у вас работает полноценная система мониторинга: Prometheus и Grafana подняты в Docker Compose на одном сервере, каждый удалённый сервер отдаёт метрики через свои экспортеры, а по каждому сайту на Nginx видно свои показатели независимо от остальных.
Главные выводы, которые стоит запомнить: если экспортер запущен и работает, но Prometheus его не видит — в первую очередь смотрим на файрвол. Если Prometheus в Docker не видит Node Exporter на том же хосте — используем 172.17.0.1 вместо localhost. Если Grafana не подключается к Prometheus — используем имя контейнера prometheus, а не localhost.
При расширении инфраструктуры — новый сайт или новый сервер — достаточно повторить соответствующие шаги: добавить stub_status в Nginx, создать новый сервис экспортера с уникальным портом и меткой, добавить цель в prometheus.yml, открыть порт в файрволе и применить конфиг через docker kill --signal=SIGHUP prometheus.
Комментарии (0)
Чтобы оставить комментарий, войдите или зарегистрируйтесь.
Комментариев пока нет. Будьте первым!