Современная инфраструктура редко ограничивается одним сервером. Даже относительно небольшой проект может состоять из нескольких виртуальных машин, контейнеров, базы данных, reverse proxy, очереди сообщений, файлового хранилища и нескольких приложений. Система продолжает работать, но её состояние уже нельзя надёжно контролировать вручную.

На одном сервере заканчивается свободное место. На другом растёт потребление памяти. Один из контейнеров начинает постоянно перезапускаться. Приложение продолжает отвечать, но среднее время ответа увеличивается с 100 до 1000 миллисекунд. Все эти проблемы могут появляться постепенно.

Именно поэтому мониторинг становится не дополнительным инструментом, а частью самой инфраструктуры. Если о проблеме становится известно только после сообщения пользователя, значит система уже некоторое время находилась в неисправном состоянии.


Что такое мониторинг

Мониторинг — это постоянный сбор и анализ измеряемых показателей, по которым можно определить состояние системы и заметить отклонения от нормального поведения. Главная идея мониторинга проста:

Нам нужно знать не только то, что система сейчас работает, но и понимать, как она себя ведёт во время работы.

Например, если сервер использует 70% оперативной памяти, само это число ещё мало что говорит. Если вчера использование памяти было 40%, утром стало 50%, днём 60%, а к вечеру достигло 70%, ситуация выглядит уже иначе.

Что можно мониторить

Для Linux-сервера обычно начинают с базовых ресурсов.

CPU

Можно отслеживать:

  • загрузку CPU;

  • использование отдельных ядер;

  • время CPU в разных режимах;

  • steal time на виртуальных машинах;

  • количество контекстных переключений;

  • load average.

Но высокая загрузка CPU не всегда означает проблему. Сервер обработки видео может часами использовать почти 100% процессора — и это будет ожидаемым поведением. Поэтому сама метрика редко рассматривается изолированно. Нас интересует вопрос:

Соответствует ли текущая загрузка ожидаемой нагрузке и поведению приложения?


RAM

Для памяти можно отслеживать:

  • общий объём;

  • доступную память;

  • используемую память;

  • swap;

  • page cache;

  • buffer;

  • memory pressure.

Например:

Total RAM:       16 GB
Available RAM:    3 GB
Swap used:        0 GB

Особенно важна динамика. Если приложение постепенно съедает доступную память. Это может быть первым признаком утечки памяти или другого дефекта.


Disk

Для дисковой подсистемы обычно отслеживают:

  • свободное место;

  • размер файловой системы;

  • read/write throughput;

  • IOPS;

  • latency;

  • ошибки ввода-вывода.

Проблема здесь не обязательно возникает непосредственно при достижении 100%. Приложение может начать испытывать проблемы раньше, если ему нужны временные файлы, логи, файлы блокировок или дополнительное место для базы данных.


Network

Для сети можно собирать:

  • входящий трафик;

  • исходящий трафик;

  • количество пакетов;

  • ошибки интерфейсов;

  • dropped packets;

  • состояние сетевых интерфейсов.

В production это помогает обнаруживать не только перегрузку канала, но и проблемы с сетевыми интерфейсами.


Processes and services

Можно отслеживать:

  • количество процессов;

  • состояние systemd-сервисов;

  • количество открытых файлов;

  • количество соединений;

  • состояние системных компонентов.

Например, CPU может быть нормальным, но количество процессов внезапно выросло в несколько раз. Такие показатели помогают увидеть проблемы, которые не проявляются через базовые CPU/RAM метрики.


Что происходит без мониторинга

Представим обычный сервер с веб-приложением. Всё работает нормально. Приложение записывает логи:

application.log
access.log
error.log

Через несколько дней размер логов увеличивается. Свободное место уменьшается:

30%
▼
20%
▼
10%
▼
5%
▼
1%

В какой-то момент приложение пытается записать новый файл. Операционная система сообщает:

No space left on device

После этого могут начаться вторичные ошибки:

Заканчивается место на диске
↓
Ошибка записи
↓
Ошибка приложения
↓
HTTP 500
↓
Пользователь получает ошибку

Если мониторинга нет, первый сигнал может прийти от пользователя. Если мониторинг настроен, цепочка может быть другой.

Свободное место уменьшается
▼
Метрика фиксирует изменение
▼
Порог превышен
▼
Создаётся alert
▼
Администратор получает уведомление
▼
Проблема устраняется

Именно здесь появляется практическая ценность мониторинга. Он не обязательно предотвращает саму проблему, но позволяет увидеть её раньше.


Мониторинг не равен автоматическому исправлению

Это важный момент. Мониторинг отвечает прежде всего на вопрос:

Что происходит с системой?

Alerting позволяет добавить:

Насколько это отклонение критично?

А автоматизация может ответить на другой вопрос:

Что делать при таком событии?

Например:

Disk usage > 90%

может вызвать alert. А уже другой механизм может:

  • отправить сообщение;

  • создать задачу;

  • удалить старые временные файлы;

  • увеличить размер диска;

  • запустить дополнительный экземпляр приложения.


Monitoring и Health Check

Эти понятия легко перепутать. Health check отвечает на простой вопрос:

Сервис доступен?

Например:

Total RAM:      16 GB
Used:           14 GB
Available:       2 GB

Ответ:

HTTP/1.1 200 OK

Это полезно. Но представим следующую ситуацию:

GET /health
▶ 200 OK

а реальные запросы:

GET /api/users
▶ 5.2 seconds

Приложение формально работает. Health endpoint возвращает 200 OK. Но пользователи уже получают очень медленные ответы. Мониторинг может показать:

Request latency
100 ms
▼
150 ms
▼
300 ms
▼
800 ms
▼
5000 ms

Таким образом Health check сообщает о доступности. Monitoring показывает состояние системы значительно подробнее. В нормальной инфраструктуре они используются вместе.


Monitoring и Observability

Здесь появляется понятие Observability, или наблюдаемость. Observability — это способность системы предоставлять достаточно телеметрии, чтобы по внешним наблюдениям исследовать как известные, так и новые, заранее не предусмотренные проблемы.


Metrics, Logs и Traces

Обычно выделяют три основных типа сигналов.

Тип

Что показывает

Пример

Metrics

Числовые показатели

CPU = 82%

Logs

События и сообщения

Database connection failed

Traces

Путь отдельного запроса

API → Auth → DB


Metrics

Метрики — это числовые значения, которые можно измерять во времени. Метрики особенно хорошо подходят для:

  • графиков;

  • alerting;

  • анализа трендов;

  • агрегирования;

  • статистики.


Logs

Логи описывают события. Например:

2026-08-08 01:20:15 ERROR Database connection failed

Лог может содержать намного больше деталей, чем метрика:

timestamp
level
service
message
exception
request_id

Метрика может сказать:

Errors = 120/min

а лог поможет понять:

Database connection failed

Traces

Трассировка позволяет проследить конкретный запрос через несколько компонентов. Например:

Client
▼ 5 ms
API Gateway
▼ 20 ms
Auth Service
▼ 35 ms
Application
▼ 800 ms
Database

В таком случае сразу видно, что основная задержка возникает при обращении к базе. В этой статье мы сосредоточимся на метриках и Prometheus.


Что такое метрика

Метрику удобно представлять как значение, измеряемое во времени.Например:

Time ▶ 10:00 ▶ 10:01 ▶ 10:02 ▶ 10:03
CPU ▶   42% ▶   51% ▶   67% ▶   81%

Однако в Prometheus метрика — это не просто число. У временного ряда есть:

  • имя;

  • значение;

  • timestamp;

  • labels.

Например:

http_requests_total{
  method="GET",
  status="200",
  service="api"
}

Здесь:

http_requests_total

— имя метрики. А:

method="GET"
status="200"
service="api"

— labels.


Что такое временной ряд

Prometheus хранит данные как time series, или временные ряды. Можно представить такой ряд:

timestamp             value
10:00:00               120
10:00:15               125
10:00:30               131
10:00:45               140

Если к имени метрики добавить labels:

http_requests_total{
  method="GET",
  status="200"
}

получается конкретный временной ряд. Другой набор labels:

http_requests_total{
  method="GET",
  status="500"
}

будет уже другим временным рядом. Одна метрика может описывать огромное количество временных рядов.


Prometheus

Prometheus — система мониторинга и сбора метрик, ориентированная на работу с временными рядами. Его ключевые возможности включают:

  • сбор метрик;

  • хранение временных рядов;

  • labels;

  • PromQL;

  • recording rules;

  • alerting rules;

  • service discovery;

  • интеграцию с exporters и другими компонентами.

Одним из ключевых архитектурных решений Prometheus является pull-модель.


Pull-модель Prometheus

Есть два распространённых подхода к передаче метрик. В push-модели источник сам отправляет данные:

Application
▼
Metrics Server

В pull-модели:

Prometheus
▼
GET /metrics
▼
Application / Exporter

Prometheus сам обращается к target и получает текущие значения. Например:

Prometheus
    |
    | GET /metrics
    ▼
Node Exporter
    ▼
Linux metrics

После этого Prometheus сохраняет полученные значения. Преимущество такого подхода в том, что центральная система сама контролирует процесс сбора:

  • какой target опрашивать;

  • когда опрашивать;

  • с каким интервалом;

  • какие targets сейчас доступны.


Endpoint /metrics

Exporter обычно публикует endpoint:

/metrics

Например:

http://server:9100/metrics

Если открыть его в браузере или через curl, можно увидеть что-то вроде:

# HELP node_memory_MemTotal_bytes Memory information field MemTotal_bytes.
# TYPE node_memory_MemTotal_bytes gauge
node_memory_MemTotal_bytes 1.7179865088e+10

Здесь:

# HELP

содержит описание метрики.

# TYPE

показывает её тип. А следующая строка содержит само значение.


Типы метрик

Prometheus поддерживает четыре основных типа метрик: Counter, Gauge, Histogram и Summary. Для начала достаточно разобраться с двумя наиболее простыми — Counter и Gauge.

Gauge

Gauge может увеличиваться и уменьшаться. Например, temperature может выглядеть так:

20
22
25
21
19

То же самое относится к:

  • доступной памяти;

  • температуре;

  • текущему числу активных соединений.


Counter

Counter предназначен для накопительных значений. Например:

http_requests_total

может изменяться:

1000
1050
1100
1180
1250

Counter обычно увеличивается. После перезапуска приложения он может быть сброшен. Именно поэтому для анализа скорости изменения counter часто используется rate().


Архитектура Prometheus

Упрощённо систему можно представить так:

                     Prometheus
         ┌────────────────┼────────────────┐
         ▼                ▼                ▼
  Service Discovery     Scrape           Storage
         ▼                ▼                ▼     
      Targets          /metrics        Time Series

Prometheus должен решить четыре основные задачи:

  1. определить, откуда получать метрики;

  2. периодически запросить эти метрики;

  3. сохранить полученные значения;

  4. при необходимости обработать их с помощью PromQL и rules.

Prometheus Server

Отвечает за:

  • обнаружение targets;

  • scrape;

  • выполнение PromQL;

  • хранение данных;

  • работу rules.

Target

Target — это конечная точка, с которой Prometheus должен получить метрики. Проще говоря, target — это конкретный адрес, куда Prometheus обращается за метриками.

Например:

node-exporter:9100

Здесь:

node-exporter

— имя хоста или контейнера, а:

9100

— порт, на котором Node Exporter предоставляет HTTP endpoint. Если Prometheus работает в той же Docker-сети, он может обратиться к контейнеру по его имени:

http://node-exporter:9100/metrics

Если exporter находится на отдельном Linux-сервере, target может выглядеть иначе:

192.168.1.20:9100

или:

server-01.example.com:9100

Таким образом, target можно представить как комбинацию:

hostname + port

Но фактически в конфигурации Prometheus target связан не только с адресом. Для него также могут быть определены labels, параметры scrape и другие свойства. Например:

scrape_configs:
  - job_name: node
    static_configs:
      - targets:
          - 192.168.1.20:9100
          - 192.168.1.21:9100

Здесь Prometheus получает два targets:

192.168.1.20:9100
192.168.1.21:9100

Оба относятся к одной job:

node

Это удобно, потому что затем их можно различать по instance, а группу — по job. Очень важно не путать target с самой метрикой. Например:

node-exporter:9100

может предоставлять тысячи различных метрик:

node_cpu_seconds_total
node_memory_MemTotal_bytes
node_memory_MemAvailable_bytes
node_filesystem_avail_bytes
node_network_receive_bytes_total

То есть:

Target
▼
/metrics
▼
множество метрик

Например, один Node Exporter может публиковать:

                          node-exporter:9100
                                 ▼
                             /metrics        
                                 ▼
                  ┌─────────────────────────────┐
                  │ CPU metrics                 │
                  │ Memory metrics              │
                  │ Filesystem metrics          │
                  │ Network metrics             │
                  │ Disk metrics                │
                  │ System metrics              │
                  └─────────────────────────────┘

Поэтому Prometheus обычно не создаёт отдельный target для каждой метрики. Он опрашивает один endpoint и получает сразу набор временных рядов.


Scrape

Scrape — это одна процедура получения метрик с target. Когда Prometheus выполняет scrape, он обращается к HTTP endpoint источника метрик. Упрощённо процесс выглядит так:

Prometheus
    │
    │ HTTP GET /metrics
    ▼
Node Exporter
    ▼
Набор метрик

Например, Prometheus обращается:

GET http://node-exporter:9100/metrics

Node Exporter отвечает:

# HELP node_memory_MemTotal_bytes ...
# TYPE node_memory_MemTotal_bytes gauge
node_memory_MemTotal_bytes 17179865088

# HELP node_memory_MemAvailable_bytes ...
# TYPE node_memory_MemAvailable_bytes gauge
node_memory_MemAvailable_bytes 8456230912

Prometheus получает этот ответ, разбирает его и сохраняет соответствующие временные ряды.


Как часто выполняется Scrape

Периодичность задаётся параметром:

global:
  scrape_interval: 15s

В этом случае Prometheus будет по умолчанию пытаться получать метрики каждые 15 секунд. Условно:

12:00:00 ▶ scrape
12:00:15 ▶ scrape
12:00:30 ▶ scrape
12:00:45 ▶ scrape
12:01:00 ▶ scrape

Следовательно, одна и та же метрика постепенно превращается в последовательность значений во времени:

12:00:00 ▶ CPU = 42
12:00:15 ▶ CPU = 47
12:00:30 ▶ CPU = 51
12:00:45 ▶ CPU = 63
12:01:00 ▶ CPU = 59

Это и позволяет Prometheus работать именно как система мониторинга временных рядов, а не просто как набор текущих значений. Важно понимать, что scrape_interval — это не «частота обновления графика». Это интервал, с которым Prometheus получает данные от target. График уже строится из сохранённых временных рядов.


Что происходит, если Scrape не удался

Target может быть недоступен. В таком случае Prometheus не получает новые значения. Для каждого target существует специальная метрика:

up

Если последний scrape завершился успешно:

up = 1

Если возникла ошибка:

up = 0

Например:

up{job="node", instance="server-01:9100"} = 1

означает, что последний scrape этого target завершился успешно. А:

up{job="node", instance="server-02:9100"} = 0

указывает на проблему с получением метрик. При этом up = 1 не означает, что само приложение полностью исправно. Это означает только, что Prometheus смог успешно получить метрики с данного endpoint.


Target и Endpoint — не совсем одно и то же

Эти понятия также полезно различать. Target — объект, который Prometheus должен опрашивать. Endpoint — конкретная HTTP-точка, через которую target предоставляет метрики. В типичном случае:

Target:
node-exporter:9100
Endpoint:
http://node-exporter:9100/metrics

То есть target задаёт, куда обращаться, а endpoint — какой ресурс получать. В стандартной конфигурации Prometheus обычно используется endpoint:

/metrics

но его можно изменить, если exporter публикует метрики по другому пути.


Job

Targets в Prometheus объединяются в jobs. Например:

scrape_configs:
  - job_name: node
    static_configs:
      - targets:
          - server-01:9100
          - server-02:9100

Здесь:

job_name: node

описывает логическую группу targets.

Получается:

Job: node
├── server-01:9100
└── server-02:9100

Это полезно, например, когда у нас есть разные группы источников:

job="node"
job="cadvisor"
job="database"
job="application"

Тогда можно выполнять запросы для конкретной группы. Например:

up{job="node"}

или:

up{job="cadvisor"}

TSDB

После получения метрик возникает следующий вопрос:

Где Prometheus их хранит?

Для этого используется встроенное временное хранилище — TSDB (Time Series Database). Prometheus не просто сохраняет последний результат scrape. Он сохраняет последовательность значений во времени.

Например, в базе появляется история:

10:00 ▶ 42%
10:01 ▶ 48%
10:02 ▶ 51%
10:03 ▶ 63%
10:04 ▶ 72%

Именно эта история позволяет отвечать на вопросы:

  • когда начался рост нагрузки;

  • насколько быстро увеличивается использование памяти;

  • было ли значение выше порога;

  • как показатель изменялся за последний час;

  • отличается ли сегодняшняя нагрузка от вчерашней.


Как выглядит временной ряд в TSDB

Допустим, существует метрика:

node_memory_MemAvailable_bytes

Для конкретного сервера это может быть:

node_memory_MemAvailable_bytes{
    instance="server-01:9100",
    job="node"
}

У неё будет множество точек:

timestamp      value
10:00:00       8.4 GB
10:00:15       8.1 GB
10:00:30       7.9 GB
10:00:45       7.5 GB
10:01:00       7.3 GB

Все эти точки относятся к одному временному ряду. Если появляется второй сервер:

node_memory_MemAvailable_bytes{
  instance="server-02:9100",
  job="node"
}

это уже другой временной ряд. Таким образом, labels определяют идентичность series, а TSDB хранит значения этих series во времени.


Как PromQL работает с TSDB

Когда пользователь вводит запрос:

node_memory_MemAvailable_bytes

Prometheus не обращается заново к Node Exporter за историческими данными. Он использует уже сохранённые временные ряды из TSDB. Если запрос содержит диапазон времени:

rate(node_cpu_seconds_total[5m])

Prometheus использует данные соответствующего временного интервала из хранилища и вычисляет производное значение.


Rules

Prometheus также позволяет заранее выполнять вычисления с помощью rules. Rules нужны в первую очередь для двух задач:

  1. recording rules — заранее вычислять и сохранять часто используемые показатели;

  2. alerting rules — проверять условия и создавать alerts.


Recording Rules

Представим, что у нас есть сложный запрос:

100 * (
  1 -
  avg by(instance) (
    rate(node_cpu_seconds_total{mode="idle"}[5m])
  )
)

Он вычисляет приблизительную загрузку CPU. Если такой запрос используется в десяти dashboard и нескольких других запросах, каждый раз вычислять его заново может быть неудобно. Для этого можно создать recording rule. Например:

groups:
  - name: node
    rules:
      - record: instance:node_cpu_utilisation:rate5m
        expr: |
          100 * (
            1 -
            avg by(instance) (
              rate(node_cpu_seconds_total{mode="idle"}[5m])
            )
          )

Prometheus будет периодически вычислять это выражение и записывать результат как новую временную series:

instance:node_cpu_utilisation:rate5m

После этого вместо длинной формулы можно обращаться к уже вычисленной метрике:

instance:node_cpu_utilisation:rate5m

Это особенно полезно для больших Prometheus-инсталляций, где одни и те же сложные вычисления используются регулярно.


Alerting Rules

Вторая задача rules — обнаружение проблем. Например, мы хотим обнаруживать использование RAM выше 90%. Можно задать условие:

groups:
  - name: node
    rules:
      - alert: HighMemoryUsage
        expr: |
          100 * (
            1 -
            node_memory_MemAvailable_bytes
            /
            node_memory_MemTotal_bytes
          ) > 90
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Высокое использование памяти"
          description: "Использование RAM на {{ $labels.instance }} превышает 90% более 5 минут"

Теперь логика выглядит так:

Metrics
▼
PromQL expression
▼
Условие ▶ 90%
▼
Условие выполняется 5 минут
▼
Alert

Параметр:

for: 5m

нужен для того, чтобы не создавать alert из-за кратковременного всплеска. Например:

RAM = 91%

на несколько секунд может быть совершенно нормальным. Но продолжительная нагрузка, уже выглядит гораздо подозрительнее. Важно понимать, что Prometheus сам определяет условие alerting rule, но отправка уведомлений пользователю обычно выполняется через отдельный компонент — Alertmanager.

Упрощённая схема:

Metrics
▼
Prometheus
▼
Alerting Rule
▼
Alert
▼
Alertmanager
▼
Email / Telegram / Slack

Таким образом, Prometheus отвечает за обнаружение условия, а Alertmanager — за дальнейшую обработку и маршрутизацию уведомлений.


Почему это важно для дальнейшей практики

Когда мы в дальнейшем будем запускать Node Exporter и cAdvisor, эти понятия будут постоянно встречаться. Например:

node-exporter:9100

— это target.

GET /metrics

— запрос во время scrape. Полученные:

node_cpu_seconds_total
node_memory_MemAvailable_bytes

— временные ряды, которые сохраняются в TSDB. Запрос:

rate(node_cpu_seconds_total[5m])

— пример использования PromQL. А условие:

... ▶ 90

в alerting rule может использоваться для обнаружения проблемы. Таким образом, вся цепочка:

Target
▼
Scrape
▼
Metrics
▼
TSDB
▼
PromQL
▼
Rules
▼
Alert

является базовой моделью работы Prometheus.


Exporters

Prometheus не обязан самостоятельно уметь читать метрики каждого приложения, ОС или базы данных. Для этого используются exporters. Exporter преобразует информацию из некоторого источника в формат, который понимает Prometheus. Общая схема:

System
   ▼
Exporter
   ▼
/metrics
   ▼
Prometheus

В этой статье нас интересуют два exporter-подобных компонента:

Node Exporter
▼
Linux host

и:

cAdvisor
▼
Containers

Node Exporter

Node Exporter предназначен для сбора метрик Linux-хоста. Например:

CPU
Memory
Filesystem
Network
Disk

Схема:

Linux host
▼
Node Exporter
▼
:9100/metrics
▼
Prometheus

Примеры метрик:

node_cpu_seconds_total
node_memory_MemAvailable_bytes
node_memory_MemTotal_bytes
node_filesystem_avail_bytes
node_network_receive_bytes_total

Как выглядит запуск Node Exporter в Docker

На первый взгляд запуск Node Exporter в Docker выглядит очень просто:

docker run -d \
  --name node-exporter \
  -p 9100:9100 \
  prom/node-exporter

Контейнер запустится, Node Exporter начнёт слушать порт 9100, а endpoint метрик станет доступен по адресу:

http://localhost:9100/metrics

Однако для нашей задачи этого недостаточно. Мы хотим получить метрики Linux-хоста, на котором работает Docker, а не ограниченное представление о том окружении, которое доступно самому контейнеру.

Почему обычного запуска недостаточно

Контейнер — это изолированное окружение. У него собственные:

  • network namespace;

  • PID namespace;

  • корень файловой системы;

  • представление о процессах;

  • доступ к системным ресурсам.

Поэтому команда:

docker run -d prom/node-exporter

не означает:

«Node Exporter теперь полностью видит весь Linux-сервер».

Она означает только, что Node Exporter запущен внутри контейнерного окружения. Для некоторых метрик этого может быть достаточно, но для полноценного мониторинга host нужно предоставить exporter доступ к соответствующим ресурсам самого хоста. Условно различие можно представить так:

Обычный контейнер:
Linux host
  └── Docker    
    └── Node Exporter
     └── видит своё окружение

Нам же нужно:

Linux host
├── CPU
├── RAM
├── Disk
├── Network
├── Processes
└── Filesystems
      ▲
  Node Exporter

Именно поэтому официальный пример контейнерного запуска Node Exporter для мониторинга host использует дополнительные параметры. В частности:

  • --network host;

  • --pid host;

  • bind mount корневой файловой системы;

  • --path.rootfs=/host.

--network host

Первый параметр:

--network host

помещает контейнер в сетевой namespace хоста. Вместо отдельного контейнерного сетевого пространства Node Exporter получает возможность использовать сетевое пространство Linux-хоста. Для мониторинга это важно, когда мы хотим видеть сетевую конфигурацию и интерфейсы именно сервера. В результате порт

9100

становится доступен через сетевой стек host. То есть Node Exporter можно проверить непосредственно с сервера:

curl http://localhost:9100/metrics

--pid host

Следующий параметр:

--pid host

даёт контейнеру доступ к PID namespace хоста. Без этого контейнер работает в собственном пространстве процессов. Можно представить это так. Без --pid host:

Host
├── process 101
├── process 102
└── process 103
Container
├── process 1
├── process 2
└── process 3

С --pid host Node Exporter может работать в контексте процессов самого Linux-хоста. Это важно для метрик, связанных с процессами и некоторыми системными характеристиками.


Монтирование корневой файловой системы

Следующая часть:

-v "/:/host:ro,rslave"

выглядит немного необычно. Она означает:

Смонтировать корневую файловую систему хоста / внутрь контейнера по пути /host.

Схематично:

Linux host
/
├── /etc
├── /proc
├── /sys
├── /var
├── /home
└── ...        
      ▼ mount
Container

/host
├── etc
├── proc
├── sys
├── var
├── home
└── ...

Параметр:

ro

означает read-only. То есть контейнер получает доступ к данным файловой системы для чтения, но не должен изменять их через этот mount. Это важный принцип безопасности: если компоненту мониторинга не нужен доступ на запись, разумно предоставлять ему только чтение.


Зачем нужен --path.rootfs=/host

После монтирования файловой системы возникает следующий вопрос:

Откуда Node Exporter должен считать, что начинается корень хоста?

Внутри контейнера корень файловой системы по-прежнему выглядит как:

/

но настоящий root filesystem сервера теперь находится:

/host

Поэтому Node Exporter запускается с параметром:

--path.rootfs=/host

Этот параметр сообщает exporter:

Когда нужно обращаться к файловой системе Linux-хоста, используй /host как её корень.

Именно поэтому две части конфигурации работают вместе:

-v "/:/host:ro,rslave"

и:

--path.rootfs=/host

Первая делает файловую систему хоста доступной внутри контейнера, а вторая говорит Node Exporter, где её искать.


Полный запуск

В результате команда становится значительно длиннее:

docker run -d \
  --name node-exporter \
  --network host \
  --pid host \
  -v "/:/host:ro,rslave" \
  quay.io/prometheus/node-exporter:latest \
  --path.rootfs=/host

Теперь схема выглядит так:

                         Linux host
                             │
            ┌────────────────┼────────────────┐
            │                │                │
            ▼                ▼                ▼
           CPU              RAM             Disk
            │                │                │
            └────────────────┼────────────────┘
                             │
                             ▼
                      Node Exporter
                             │
                             │ :9100/metrics
                             ▼
                        Prometheus

То есть Node Exporter находится внутри контейнера, но получает необходимый доступ к ресурсам host для сбора системных метрик.


Проверяем, что Node Exporter работает

Сначала посмотрим, запущен ли контейнер:

docker ps

В списке должен появиться:

node-exporter

Затем проверяем endpoint:

curl http://localhost:9100/metrics

Если всё работает, получим большой набор метрик. Например:

node_cpu_seconds_total
node_memory_MemTotal_bytes
node_memory_MemAvailable_bytes
node_filesystem_avail_bytes
node_network_receive_bytes_total

Можно дополнительно проверить, что Node Exporter действительно видит информацию о хосте. Например:

curl -s http://localhost:9100/metrics | grep node_memory_MemTotal_bytes

Или:

curl -s http://localhost:9100/metrics | grep node_filesystem_avail_bytes

Что важно понимать

Запуск Node Exporter в Docker не превращает его в обычный контейнер приложения. Для типичного приложения мы стремимся к максимальной изоляции. У мониторинга ситуация другая. Node Exporter по смыслу должен наблюдать за host, поэтому ему приходится предоставить дополнительный доступ к системным ресурсам. Поэтому его запуск сложнее, чем запуск обычного HTTP-сервиса. При этом стоит соблюдать принцип минимально необходимых прав: предоставлять только тот доступ, который действительно нужен для выбранного набора метрик.


Почему мы не используем здесь обычный -p 9100:9100

В простом контейнере часто встречается:

-p 9100:9100

Эта конструкция публикует контейнерный порт через Docker port forwarding. Но в нашем варианте используется:

--network host

Поэтому Node Exporter работает непосредственно в сетевом пространстве host, и дополнительная публикация порта через -p не нужна. Получается:

Обычный контейнер:
Container :9100
▼
Docker port mapping
▼
Host :9100

А при host network:

Node Exporter :9100
▼
Host network
▼
Host :9100

Это одна из причин, почему команда запуска выглядит иначе.


cAdvisor

cAdvisor предназначен для сбора информации о контейнерах и используемых ими ресурсах. Например:

container CPU
container memory
container network
container filesystem

Упрощённая схема:

Linux host
    |
    ├── nginx
    ├── application
    ├── redis
    └── postgres
             ▲
          cAdvisor
             ▼
          /metrics
             ▼
        Prometheus

Для запуска cAdvisor в Docker ему необходимо предоставить доступ к данным хоста и Docker. Официальный пример монтирует /, /var/run, /sys и /var/lib/docker.


Service Discovery

Статически прописывать targets удобно в небольшой лабораторной системе. Например:

targets:  - server-01:9100  - server-02:9100  - server-03:9100

Но представим, что у нас больше 100 серверов. Поддерживать такой файл вручную становится неудобно. Поэтому Prometheus поддерживает Service Discovery. Принцип:

Cloud / Kubernetes / Discovery source
              ▼
       Service Discovery
              ▼
          Prometheus
              ▼
           Targets

Это позволяет автоматически находить targets вместо ручного перечисления каждого сервера.


Labels

Labels — фундаментальный механизм Prometheus. Рассмотрим:

http_requests_total{
  job="api",
  instance="server-01",
  method="GET",
  status="200"
}

Теперь можно выбирать отдельные части данных. Например:

http_requests_total{status="500"}

получит HTTP 500. А:

http_requests_total{method="POST"}

получит только POST. Можно объединить фильтры:

http_requests_total{ method="POST", status="500" }

Это означает:

Найти POST-запросы, которые завершились кодом 500.


Недостаток Labels

Labels — очень мощный механизм. Но он же является одной из потенциальных проблем Prometheus. Представим:

Linux host
▼
Node Exporter
▼
:9100/metrics
▼
Prometheus

Количество комбинаций ограничено. Теперь добавим:

user_id
request_id
session_id

Если user_id принимает сотни тысяч значений, а request_id вообще уникален для каждого запроса, число временных рядов может вырасти очень быстро. Например:

http_request_duration_seconds{
    user_id="123456"
}

Тогда практически каждый пользователь создаёт собственный набор time series. Поэтому labels должны использоваться для измерений, а не для произвольных уникальных идентификаторов.


PromQL

Для анализа данных используется PromQL — Prometheus Query Language. Самый простой запрос:

up

Он возвращает состояние targets. Обычно:

1

означает успешный последний scrape.

0

означает ошибку последнего scrape.


Работа с метриками

Можно просто запросить:

node_memory_MemAvailable_bytes

или:

node_filesystem_avail_bytes

или:

node_network_receive_bytes_total

Но реальные системы почти никогда не ограничиваются простым чтением текущего значения. Нас интересуют производные показатели.


Функция rate()

Для counter используется:

rate()

Официальная документация определяет rate() как среднюю скорость увеличения временного ряда в секунду за выбранный диапазон. Функция предназначена для counter-метрик. Например:

rate(node_network_receive_bytes_total[5m])

означает:

Рассчитать среднюю скорость изменения количества полученных байтов за последние пять минут.


Использование памяти

Для оценки использования RAM можно использовать:

100 * (
  1 -
  node_memory_MemAvailable_bytes
  /
  node_memory_MemTotal_bytes
)

Предположим, Prometheus вернул:

73.4

Можно интерпретировать это как приблизительно 73,4% занятой оперативной памяти согласно этой формуле.


Загрузка CPU

Для приблизительной оценки загрузки CPU:

100 * (
  1 -
  avg by(instance) (
    rate(node_cpu_seconds_total{mode="idle"}[5m])
  )
)

Что здесь происходит? Сначала:

rate(node_cpu_seconds_total{mode="idle"}[5m])

получает скорость изменения CPU time в режиме idle. Затем:

avg by(instance)(...)

усредняет значения по CPU. После этого:

1 - idle

даёт долю занятого времени. И:

× 100

переводит результат в проценты.


Ресурсы

1. Prometheus

Официальный Getting Started

Getting started with Prometheus

Лучший первый материал для знакомства с Prometheus. Показывает установку, базовую конфигурацию, targets, scrape, Prometheus UI, Node Exporter и первые запросы. Подходит для первого практического знакомства.

Официальные Tutorials

Prometheus Tutorials

Набор небольших официальных практических руководств. После Getting Started здесь можно выбрать отдельные темы: метрики, Grafana, alerting и другие возможности Prometheus.

Официальная документация

Prometheus Documentation

Основной справочник по Prometheus. Не стоит читать её целиком в начале — лучше возвращаться сюда по мере необходимости, когда начинаете работать с конкретной функцией или настройкой.

LabEx — Prometheus Monitoring

Prometheus Monitoring — LabEx

Практический курс, который объединяет установку Prometheus, Node Exporter, базовый PromQL, alerts и Alertmanager. Хорошо подходит для закрепления материала после официального Getting Started. Курс содержит несколько hands-on лабораторий и итоговую практическую задачу.


2. PromQL — самый важный раздел после основ

PromQL Querying basics

Базовый материал по языку запросов Prometheus. Здесь стоит разобраться с time series, instant и range queries, label selectors, фильтрацией и базовыми операциями.

PromQL functions

Справочник по функциям PromQL. Особенно полезны rate(), increase(), avg(), sum(), max(), min(), count() и функции для работы с временными рядами.

PromQL operators

Следующий шаг после базовых запросов. Здесь изучаются арифметические операции, сравнения, логические операции и операции между временными рядами.


3. Node Exporter

Официальный Node Exporter

Node Exporter — GitHub

Основной источник информации о Node Exporter. Изучите его назначение, установку, доступные collectors и принцип получения метрик Linux-хоста. Особенно важно научиться понимать метрики CPU, RAM, дисков, файловых систем и сети, а не просто устанавливать exporter.


4. cAdvisor

Официальный cAdvisor

cAdvisor — GitHub

Инструмент для сбора метрик контейнеров. В отличие от Node Exporter, который в первую очередь показывает состояние хоста, cAdvisor позволяет смотреть на использование CPU, памяти, сети и других ресурсов контейнерами. После Node Exporter это логичный следующий шаг для мониторинга Docker.


5. Alerting и Alertmanager

Prometheus Alerting

Alerting in Prometheus

Официальный материал по системе оповещений Prometheus.

Alerting rules

Alerting rules

Подробная документация по созданию правил. Полезна после того, как вы уже уверенно пишете PromQL.

Alertmanager

Alertmanager Documentation

Изучает следующий уровень: что происходит после срабатывания alert.


Заключение

Мониторинг — это не просто график загрузки CPU. Его задача — дать возможность увидеть состояние системы, понять динамику её изменения и обнаружить проблемы до того, как они превратятся в серьёзный инцидент. Следующий этап — практика.

Комментарии (2)


  1. habrasilence
    24.08.2026 05:33

    А при --network host порт 9100 случайно наружу не торчит? Я бы рядом дописал про firewall или --web.listen-address=127.0.0.1:9100, а то такую команду легко скопировать как есть