Вас много, а я одна: обзорная система мониторинга на Prometheus и Grafana

от автора

Привет, Хабр! Меня зовут Сева, я работаю backend-разработчиком в Doubletapp, а также занимаюсь некоторыми devops-задачами. В этой статье я расскажу о мониторинге наших backend-приложений: сборе метрик, их визуализации и отправке уведомлений. Покажу примеры конфигов с подробными комментами и дам ссылки на гитхаб.

В Doubletapp мы занимаемся аутсорс-разработкой, к нам часто приходят новые проекты, а часть старых уходит в режим поддержки. Из-за этого у нас много сервисов, где активная разработка уже не ведется и которые развернуты на серверах клиентов. Мы не хотим узнавать о поломках от пользователей спустя несколько дней, поэтому нам нужна обзорная система, которая будет собирать метрики со всех проектов и отвечать на вопросы:

  • жив ли сервер/сервис; 

  • как меняется RPS;

  • какое время ответа;

  • хватает ли памяти и CPU;

  • сколько места осталось на диске.

Для этого мы выбрали популярное решение: собираем метрики с помощью Prometheus, отображаем их на дашбордах в Grafana и отправляем алерты в Телеграм через Prometheus Alert Manager.

Prometheus

Приложения обычно развернуты в облачных сервисах, у каждого клиента своя инфраструктура, поэтому мы не можем воспользоваться service discovery, который позволяет Prometheus автоматически обнаруживать новые объекты: Docker-контейнеры на хосте или поды Kubernetes-кластера.

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

Конфигурация Prometheus

prometheus/prometheus.yml:

# секция global указывает параметры для всех конфигураций сбора метрик global:   # как часто запрашивать метрики по умолчанию   scrape_interval: 10s   # как часто пересчитывать правила для алертов (подробнее — в alerts.yml)   evaluation_interval: 10s   # список конфигураций сбора метрик scrape_configs:   # название процесса, который собирает метрики   - job_name: example-service     static_configs:       # список доменов/ip, с которых будут собираться метрики       - targets:             - example.url.com         # дополнительные данные, которые добавятся к записи         labels:           # название проекта           instance_group: example_project           # окружение приложения           instance_env: test           # тип источника метрик           instance_type: service           # команда, ответственная за проект           team: backend . . .

Чтобы добавить новый сервис, нужно изменить конфиг и перезапустить Prometheus. Можно включить перезагрузку конфигурации в рантайме, но мы просто перезапускаем Prometheus во время выкатки нового конфига через CI/CD.

Сбор метрик

В отличие от Graphite или InfluxDB, Prometheus сам ходит в сервисы и забирает из них телеметрию. Для того, чтобы это работало, сервис должен предоставить эндпоинт с информацией о своем состоянии в формате, понятном Prometheus, — это или их собственный формат, или OpenMetrics.

Обычно для этого используется exporter — демон, который смотрит в логи системы, готовит телеметрию и предоставляет ее по HTTP/S. Для большинства существующих сервисов уже написаны готовые экспортеры. В простом случае можно реализовать /metrics в самом приложении, например, если мы хотим проверить только то, что приложение запущено и готово обрабатывать запросы.

По умолчанию экспортеры работают по HTTP без авторизации, что позволяет любому желающему получить к ним доступ. Стоит озаботиться настройкой TLS и авторизации. Это можно сделать как с помощью настроек самого экспортера, так и прокси вроде Nginx.

Метрики приложений

На всех наших проектах мы используем Nginx для проксирования запросов к API, поэтому из его логов мы можем строить основные метрики. Есть официальный экспортер от самой Nginx Inc. Для стандартной версии Nginx можно получить информацию об активных соединениях и количестве обработанных запросов, но нам нужно больше информации, как минимум HTTP-коды ответов и время обработки запросов. Официальный экспортер может собирать эти данные из API Nginx, но она доступна только в версии Nginx Plus.

Для бесплатного Nginx также существует альтернативный экспортер — martin-helmich/prometheus-nginxlog-exporter. Чтобы его настроить, нужно указать путь и формат лога, который он будет парсить. Также нужно указать границы бакетов для гистограмм, чтобы можно было посмотреть, сколько времени заняла обработка определенной доли запросов. В дефолтном формате лога Nginx нет информации о времени ответа, поэтому его тоже поменяем.

Конфигурация Nginx

nginx/default.conf:

# формат лога log_format timed_combined    '$remote_addr - $remote_user [$time_local] '    '"$request" $status $body_bytes_sent '    '"$http_referer" "$http_user_agent" '    'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"';   server {    # порт, который будет слушать Nginx    listen 80;     # переотправка запросов к API    location / {    # файл логов    access_log /var/log/nginx/app.log timed_combined;    proxy_pass http://app:80/;        . . .    }     # переотправка запросов на /metrics к экспортеру    location /metrics {  # логирование запросов отключено, чтобы не портить метрики    access_log off;       proxy_pass http://nginx-exporter:4040/metrics;        . . .    } } 

Конфигурация Nginx-exporter

nginx-exporter/config.hcl:

listen {   # порт, который будет слушать экспортер   port = 4040   # эндпоинт для получения метрик   metrics_endpoint = "/metrics" }  namespace "nginx" {   # формат лога Nginx для парсинга   format = "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" rt=$request_time uct=\"$upstream_connect_time\" uht=\"$upstream_header_time\" urt=\"$upstream_response_time\""   # границы бакетов для гистограмм   histogram_buckets = [.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10]   # источник логов   source {     files = ["/var/log/nginx/app.log"]   } } 

По ссылке — пример приложения с Nginx и экспортером. 

Метрики серверов

Также важно собирать метрики с серверов, на которых приложения запущены, —  следить за нагрузкой на ЦПУ, расходованием оперативной памяти и диска. Для сбора этих метрик есть официальный экспортер от Prometheus — prometheus/node_exporter. Он запускается как демон и периодически собирает метрики из системных файлов и интерфейсов.

Ссылка на проект с экспортером.

Дашборды

Дашборд в Grafana — это инструмент для визуализации метрик в реальном времени. Дашборд состоит из панелей, каждая из которых отображает определенный набор данных, например в виде графика или таблицы. Можно использовать готовые дашборды или собрать их с нуля в веб-интерфейсе. Для создания панели нужно написать запрос данных на PromQL и настроить их представление. 

В Grafana мы сделали 3 дашборда: главный, на котором собраны все приложения и серверы, дашборд конкретного приложения и дашборд конкретного сервера.

Главный дашборд

На главном находятся таблицы серверов и приложений с основными метриками: состояние (up или down), использование процессора, памяти и диска для сервера и RPS, статус, коды и время ответа для приложения. Он позволяет быстро оценить состояние всех наших проектов. Таблица состоит из отдельных панелей для серверов и приложений, они разделены на продовые и тестовые. Можно перейти на дашборд конкретного сервера/приложения, если кликнуть на его название или одну из метрик.

Таблица серверов

PromQL запросы, использующиеся в таблице серверов

State

up{instance_type="node", instance_env="prod"}

CPU Used

100 * (1 - avg by(instance)(irate(node_cpu_seconds_total{mode='idle', instance_env="prod"}[1m])))

Memory Available

avg by(instance)(node_memory_MemAvailable_bytes{instance_env="prod"})

Memory Total

avg by(instance)(node_memory_MemTotal_bytes{instance_env="prod"})

Disk Available

avg by(instance)(node_filesystem_avail_bytes{mountpoint='/', instance_env="prod"})

Disk Total

avg by(instance)(node_filesystem_size_bytes{mountpoint='/', instance_env="prod"})

Таблица приложений

PromQL запросы, использующиеся в таблице приложений

State

up{instance_type="service"}

RPS

sum by(instance)(rate(nginx_http_response_count_total{status=~'...'}[24h]))

RP24H

floor(sum by(instance)(increase(nginx_http_response_count_total{status=~'...'}[24h])))

2хх

floor(sum by(instance)(increase(nginx_http_response_count_total{status=~'2..'}[24h])))

95th

histogram_quantile(0.95, sum by (instance,le) (rate(nginx_http_upstream_time_seconds_hist_bucket{status='200'}[10m]))) * 1000

Дашборд приложения

На странице приложений — состояние, RPS, количество и коды ответов, время ответа.

PromQL запросы, использующиеся для построения панелей

Панель состояния

up{instance=~'$instance:.+'}

Панель RPS

sum(rate(nginx_http_response_count_total{instance=~'$instance:.+'}[1m]))

  • Метрика nginx_http_response_count_total{instance=~'$instance:.+',status='200'} содержит информацию о количестве обработанных HTTP-запросов

  • Запрашиваются средняя скорость роста на отрезках по 1 минуте для этой метрики (rate(...)[1m])

  • Считается сумма по всем HTTP-методам и статусам ответов sum(...)

Панель запросов за 24 часа

floor(sum(increase(nginx_http_response_count_total{instance=~'$instance:.+'}[24h])))

  • Метрика nginx_http_response_count_total{instance=~'$instance:.+',status='200'}содержит информацию о количестве обработанных HTTP-запросов

  • Для количества обработанных HTTP-запросов считается их количественное увеличение за 24 часа (increase(...)[24h])

  • Считается сумма по всем HTTP-методам и статусам ответов (sum(...)) и округляется до целого floor(...)

Панель запросов по статусам ответа за 24 часа

floor(sum(increase(nginx_http_response_count_total{instance=~'$instance:.+',status=~'2..'}[24h])))

Запрос аналогичен предыдущему, но данные запрашиваются по каждому типу ответов.

Панель времени ответов на разных перцентилях

histogram_quantile(0.95, sum by (le) (rate(nginx_http_upstream_time_seconds_hist_bucket{instance=~'$instance:.+',status='200'}[10m]))) * 1000

Панель графика запросов в секунду

sum(rate(nginx_http_response_count_total{instance=~'$instance:.+',status=~'...'}[1m]))

Панель графика запросов по статусам ответа

floor(sum(increase(nginx_http_response_count_total{instance=~'$instance:.+',status=~'2..'}[1m])))

Панель графика 4хх запросов

floor(sum by (status) (increase(nginx_http_response_count_total{instance=~'$instance:.+',status=~'4..'}[1m])))

Панель графика 5хх запросов

floor(sum by (status) (increase(nginx_http_response_count_total{instance=~'$instance:.+',status=~'5..'}[1m])))

Панель графика времени ответов на разных перцентилях

histogram_quantile(0.99, sum by (le) (rate(nginx_http_upstream_time_seconds_hist_bucket{instance=~'$instance:.+',status='200'}[10m]))) * 1000

  • Метрика nginx_http_upstream_time_seconds_hist_bucket{instance=~'$instance:.+',status='200'} содержит информацию о распределении времени, необходимом приложению для обработки запросов с успешными ответами, по бакетам гистограммы.

  • Запрашиваются средняя скорость роста на отрезках по 10 минут для этой метрики (rate(...)[10m])

  • Строится несколько графиков на разных перцентилях (0.5, 0.75, 0.9, 0.95, 0.99) histogram_quantile(n, sum by (le)(...)

Дашборд сервера

На странице сервера — графики нагрузки на ЦПУ, расходования памяти и диска сервера.

PromQL запросы, использующиеся для построения панелей

Обзорная панель

CPU Used

100 * (1 - avg by(instance)(irate(node_cpu_seconds_total{mode='idle',instance=~'$instance.*'}[1m])))

  • Метрика node_cpu_seconds_total{mode='idle',instance=~'$instance.'} содержит информацию о распределении времени, которое процессор проводит состоянии бездействия

  • Вычисляется посекундная мгновенная скорость увеличения временного ряда на отрезках по 10 минут для этой метрики (irate(...)[10m])

  • Из этого значения вычисляется процентное соотношение времени, которое процессор проводит в любом состоянии, кроме бездействия (100(1 - …))

Memory Available

(node_memory_MemAvailable_bytes{instance=~'$instance.*'}) / (node_memory_MemTotal_bytes{instance=~'$instance.*'}) * 100

Процентное соотношение свободной памяти к общей.

Disk Available

(node_filesystem_avail_bytes{instance=~'$instance.*', mountpoint='/'}) / (node_filesystem_size_bytes{instance=~'$instance.*', mountpoint='/'}) * 100

Процентное соотношение свободного дискового пространства к общему.

Панель памяти

График свободной памяти

node_memory_MemAvailable_bytes{instance=~'$instance.*'}

График использованной памяти

node_memory_MemTotal_bytes{instance=~'$instance.*'} - node_memory_MemAvailable_bytes{instance=~'$instance.*'}

Панель настроена так, что графики отображены друг над другом.

Панель дискового пространства

График свободного дискового пространства

node_filesystem_avail_bytes{instance=~'$instance.*', mountpoint='/'}

График использованного дискового пространства

node_filesystem_size_bytes{instance=~'$instance.*', mountpoint='/'} - node_filesystem_avail_bytes{instance=~'$instance.*', mountpoint='/'}

Панель настроена аналогично панели памяти.

Панель CPU

100 * (1 - avg by(instance)(irate(node_cpu_seconds_total{mode='idle',instance=~'$instance.*'}[1m])))

Запрос PromQL аналогичен запросу CPU Used в обзорной панели, только данные визуализируются как график.

Панель CPU Cores

100 * (1 - (irate(node_cpu_seconds_total{mode='idle',instance=~'$instance.*'}[1m])))

Запрос PromQL похож на запрос CPU Used в обзорной панели, но отсутствует агрегация по instance. Это дает данные о нагрузке на каждое ядро ЦПУ.

Панель CPU Cores Usage

100 * (1 - (irate(node_cpu_seconds_total{mode='idle',instance=~'$instance.*'}[1m])))

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

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

В качестве локальной базы данных для хранения конфигурации (дашборды, пользователи и т. д.) в Grafana поднят sqlite3. Но дашборды удобно хранить в формате JSON в репозитории проекта для развертывания Grafana на новом сервере. Экспортировать их можно на странице самого дашборда, а импортировать — в меню Grafana.

Ссылка на проект с Prometheus+Grafana — тут.

Уведомления

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

Правила уведомлений настраиваются в Prometheus. Правило — это PromQL выражение, которое должно стать истинным, чтобы активировать алерт. Prometheus вычисляет эти выражения в цикле с настроенной периодичностью.

У нас уведомления настроены на основные критические ситуации:

  • Приложение/сервер не работает. В течение 5 минут метрика up находится в значении 0.

  • На сервере мало свободной памяти/диска. Отношение значений занятого объема к общему больше 95 процентов.

  • На сервере высокое потребление ЦПУ. Время, которое процессор проводит в любом состоянии, кроме бездействия, составляет больше 80 процентов.

  • Приложение недоступно. Число ответов 502 Bad Gateway прокси-сервера возрастает в течение 3 минут.

  • Приложение возвращает 5хх-е коды ответов. За последнюю минуту вернулся 5хх-й ответ.

Активные алерты (то есть те, для которых условие стало истинным) шлются в Alertmanager, где они перенаправляются в каналы связи.  Он отправляет уведомления в различные каналы связи, такие как электронная почта или мессенджеры, есть много интеграций, которые поддерживаются из коробки. Также можно сделать переотправку на вебхуки. Они могут помочь в интегрировании каналов связи, для которых нет готовых интеграций. Например, если вы хотите узнавать о высокой нагрузке на ваш сервер в своем паблике во «ВКонтакте».

Конфигурация алертов Prometheus

prometheus/alerts.yml:

groups:   - name: default     rules:           # название алерта       - alert: NodeLowMemory         # выражение PromQL для вычисления       expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes <= 0.05         # алерт считается активным, если выражение expr верно в течение этого времени       for: 5m         # дополнительные данные для алерта       labels:           severity: critical         annotations:             # текстовое описание             summary: "Node {{ $labels.instance }} is low on RAM for more than 5 minutes" 

Настройка алертов и их отправки в основном конфиге Prometheus

prometheus/prometheus.yml:

. . .  # список файлов с описанием алертов rule_files:  - alerts.yml  # опции, связанные с Alertmanger. Протокол, префикс URL, адрес. alerting:   alertmanagers:    - scheme: http      path_prefix: /alertmanager      static_configs:        - targets: [ "alertmanager:9093" ] 

Для оповещения мы используем Телеграм. Хотя у Alertmanager есть свой конфиг специально для Телеграма, для более гибкого форматирования сообщений у нас запущено приложение-вебхук. Alertmanager отправляет POST-запросы с данными об активных алертах в формате JSON по URL типа /alert/{id} (id — идентификатор телеграм-канала) в webhook-receiver, тот их форматирует и отправляет сообщения в Телеграм.  

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

Конфигурация Alertmanager

alertmanager/alertmanager.yml:

# параметры, которые будут унаследованы всеми определенными в routes нодами маршрутизации route:   group_wait: 0s   group_interval: 1s   repeat_interval: 4h   group_by: [...]    # ноды маршрутизации алертов   routes:      # параметры, по которым выбирается уведомление    - match:        instance_group: example_group       # название обработчика, куда будет отправлено уведомление      receiver: example-receiver  # список обработчиков уведомлений receivers:     # название обработчика   - name: example-receiver     webhook_configs:         # уведомлять ли о разрешенных уведомлениях       - send_resolved: True         # куда будет отправлен POST-запрос с уведомлением         url: http://webhook-receiver:3000/alert/<tg-channel-id> 

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

Буду рад ответить на ваши вопросы и подискутировать в комментариях.

Ссылки на репозитории с проектами — Prometheus+Grafana, экспортер для серверов, шаблон приложения с Nginx-экспортером.

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

А как вы мониторите ваши проекты?

7.89% У меня один проект и мониторинги настроены только для него3
23.68% У меня несколько проектов и для каждого отдельный мониторинг9
21.05% У нас только обзорная система для всех проектов8
15.79% Есть и отдельные мониторинги, и обзорная система6
28.95% Полежит, ничего страшного11
2.63% Другое, напишу в комментарии1

Проголосовали 38 пользователей. Воздержались 11 пользователей.

ссылка на оригинал статьи https://habr.com/ru/articles/736602/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *