PROM 10: Настройка нотификаций (alertmanager)

Описание:

Всем привет!

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

Best Practice: С реальным кейсом здесь будет немного посложнее. На самом деле мало просто настроить правила алертинга и отправлять их на телефон. Важно еще уметь на них грамотно реагировать и писать по ним постмортемы. В двух словах стоит сказать, что вам точно придется настроить какие-то правила эскалации - например первой линией будете вы, второй разработчики, а третьей ваш руководитель - тогда вы точно не пропустите важное сообщение если будете за городом или недоступны. К слову, alertmanager не будет заниматься эскалациями - его задача только отправить нужный алерт в нужный канал. Для настройки эскалация я рекомендую посмотреть на сервисы pagerduty, opsgenie или opensource вариант от linkedin - oncall. Вторым пунктом, о котором стоит упомянуть - что после решения инцидента было бы неплохо написать пост мортем, который будет описывать полностью весь инцидент и поможет понять причины произошедшего, а так же что пошло плохо при решении инцидента и что было сделано верно - это необходимо для корректировки ваших действий в будущем и снижении общего даунтайма. За шаблон можно взять рекомендации от atlassian - postmortem best practices.

Итак, поехали настраивать alertmanager!

Установка и базовые функции alertmanager

Alertmanager является отдельной утилитой, которая устанавливается практически идентично prometheus. Вам достаточно скачать готовый бинарник, составить конфигурационный файл и запустить приложение. Вторым вариантом является запуск уже готового докер контейнера. Давайте пойдем по первому пути и установим alertmanager в систему:

root@prom-test:~# cd /opt/
root@prom-test:/opt# wget https://github.com/prometheus/alertmanager/releases/download/v0.22.2/alertmanager-0.22.2.linux-amd64.tar.gz

... skipped ...

2021-06-08 22:20:46 (9.02 MB/s) - ‘alertmanager-0.22.2.linux-amd64.tar.gz’ saved [23629386/23629386]

root@prom-test:/opt# tar zxf alertmanager-0.22.2.linux-amd64.tar.gz
root@prom-test:/opt# mv alertmanager-0.22.2.linux-amd64 alertmanager
root@prom-test:/opt# cd alertmanager/
root@prom-test:/opt/alertmanager#

Вот собственно и вся установка. Давайте теперь подробнее обсудим основные фичи alertmanager'а:

Группировка событий (Grouping). Это очень важный поинт - представьте что у вас отвалилась одна нода по сети. И на нее сработало сразу куча алертов - недоступность node_exporter, недоступность mysql и так далее. С помощью группировки вы сможете сгруппировать алерты в одну группу и отправить только одно оповещение, а не десятки или сотни.

Замедление (Inhibition). Еще один важный кейс в работе alertmanager - он позволяет не отправлять новую группу алертов, если какие-то указанные алерты уже были отправлены. Из примера выше - вы можете не отправлять новые сообщения о том что недоступен сайт, если у вас уже есть алерт о том что недоступна база - ведь скорее всего именно она и является корнем всех проблем.

Безмолвие (Silences). Возможность остановить отправку алертов на какое-то время - например на выходные для дев сервера или же все алерты на период обслуживания системы.

Высокая доступность (High Availability) - alertmanager может быть запущен в кластерном режиме, чтобы обеспечить высокую доступность. Prometheus будет отправлять информацию об алертах всем доступным alertmanager'ам, а они в свою очередь выберут лидера, который будет непосредственно их обрабатывать. В случае выхода лидера из строя - он будет заменен другим доступным alertmanager'ом. Таким образом вы не пропустите ни один алерт.

Базовая конфигурация alertmanager

Итак, давайте создадим простой alertmanager.yml файл конфигурации и разберем его на примере:

route:
group_by: ['alertname']
group_wait: 30s
group_interval: 5m
repeat_interval: 1h
receiver: 'web.hook'
routes:
- receiver: 'pagerduty-critical'
group_wait: 10s
matchers:
- severity="critical"
- receiver: 'slack-warning'
group_wait: 60s
matchers:
- severity="warning"

receivers:
- name: 'web.hook'
webhook_configs:
- url: 'http://127.0.0.1:5001/'
- name: 'pagerduty-critical'
pagerduty_configs:
- routing_key: 'test'
service_key: 'md5-hash'
- name: 'slack-warning'
slack_configs:
- api_url: 'https://hooks.slack.com/services/TOKEN'
channel: '#warnings'

inhibit_rules:
- source_matchers:
- severity="critical"
target_matchers:
- severity="warning"
equal: ['instance']

Итак, давайте разберемся с конфигурацией. Она разбита на три части - route, receivers и inhibit_rules. Это базовые секции. На самом деле существуют еще global - там указываются базовые параметры, templates - в которых указываются шаблоны отправляемых сообщений и mute_time_intervals - интервалы, когда не следует отправлять алерты. Про эти секции вы сможете посмотреть в официальной документации - вот здесь, а мы пока перейдем к основным секциям.

Когда алерт приходит в alertmanager он изначально попадает в маршруты (route), где alertmanager разбирается что с ним надо сделать. Маршруты просматриваются сверху вниз, начиная с корневого маршрута, у которого нет никаких условий (matchers). Он сработает в случае, если ни один из других маршрутов не подошел по условиям. В нашем случае корневой маршрут будет использовать получателя (receiver) с названием web.hook.

Если алерт совпал с условием первого маршрута где тег severity=critical, то в качестве метода нотификации будет использован получатель с названием pagerduty-critical.

Если алерт совпал с условием второго маршрута где тег severity=warning, то в качестве метода нотификации будет использован получатель с названием slack-warning.

Кстати, в качестве условий совпадения вы можете использовать и regexp, например вот так (просто меняете = на =~):

matchers:
- severity=~"warning|info"

Так же доступны операторы != - не равно и !~ - не равно по regexp.

Возвращаемся к основной теме. Таким образом alertmanager раскидывает алерты - ищет маршрут которому подходит указанное правило и дальше отправляет этот алерт нужному получателю описанному в receivers. А для случаев когда алерт с одним и тем же условием нужно отправить нескольким получателям существует флаг continue: true, о котором можно прочитать в документации.

Помимо настроек правила маршрутизации мы так же указываем правила по которым стоит группировать алерты - group_by - в данном случае мы используем просто имя алерта. Хотя на самом деле можем применять любой лейбл - например clustername или service - в общем действительно полезные ключи для группировки. Если же вы не хотите вообще группировать алерты, то можете указать group_by: [...], тогда alertmanager будет группировать оповещения по всем доступным лейблам - то есть по сути никакой группировки производиться не будет.

Так же у нас используются параметры группировки - group_wait - это время, которое alertmanager будет ждать первый раз при получении алерта до момента его отправки. Оно необходимо для того, чтобы prometheus успел отправить все новые алерты и они успешно сгруппировались. Так же мы указываем group_interval - это интервал через который alertmanager отправит нотификации о новых алертах, которые были добавлены в группу после ее отправки. Ну и repeat_interval - интервал времени, через который alertmanager напомнит об алерте если он еще не разрешился.

Как вы видите из примера мы можем менять настройки группировки и временные интервалы для разных маршрутов.

Давайте теперь переместимся к receivers - тут мы описываем методы доставки сообщений. У нас приведено три примера - использование web hook, pagerduty, а так же slack. Для каждого из методов нотификаций нам необходимо указать специфичные параметры. Для webhook'а - это url, который нужно запросить в случае отправки нотификации. Для pagerduty это routing_key и service_key, которые выдаются pagerduty при создании сервиса в веб интерфейсе. Для slack - это webhook url и имя канала, которые вы сможете получить в админке слака.

Давайте резюмировать. Когда приходит алерт, alertmanager пытается найти для него нужный маршрут по условиям (matchers) в маршрутах (route). Когда маршрут найден, alertmanager группирует все пришедшие алерты по правилу group_by за интервал времени group_wait. После истечения этого интервала, alertmanager передает алерты на отправку нужному получателю (receiver), в котором указывается как именно следует отправить алерт - в slack, pagerduty или куда-то еще.

И последний блок, который хотелось бы осветить - это inhibit_rules. Этот блок позволяет не отправлять какие-то алерты, если другие алерты уже существуют. В нашем примере если уже существует алерт (source_matchers) с тегом severity=critical и тут приходит новый алерт (target_matchers) с тегом severity=warning и при этом у этих алертов совпадают теги instance, то тогда новый алерт не будет никуда отправлен. Ну тут вроде все логично - если у нас уже есть по этому инстансу критикал алерт, то нам наверное не стоит сейчас получать еще и ворнинг по нему же.

Давайте попробуем запустить наш alertmanager:

root@prom-test:/opt/alertmanager# ./alertmanager
level=info ts=2021-06-08T22:58:35.867Z caller=main.go:221 msg="Starting Alertmanager" version="(version=0.22.2, branch=HEAD, revision=44f8adc06af5101ad64bd8b9c8b18273f2922051)"
level=info ts=2021-06-08T22:58:35.868Z caller=main.go:222 build_context="(go=go1.16.4, user=root@b595c7f32520, date=20210602-07:50:37)"
level=info ts=2021-06-08T22:58:35.877Z caller=cluster.go:184 component=cluster msg="setting advertise address explicitly" addr=10.19.0.5 port=9094
level=info ts=2021-06-08T22:58:35.891Z caller=cluster.go:671 component=cluster msg="Waiting for gossip to settle..." interval=2s
level=info ts=2021-06-08T22:58:35.974Z caller=coordinator.go:113 component=configuration msg="Loading configuration file" file=alertmanager.yml
level=info ts=2021-06-08T22:58:35.975Z caller=coordinator.go:126 component=configuration msg="Completed loading of configuration file" file=alertmanager.yml
level=info ts=2021-06-08T22:58:35.988Z caller=main.go:514 msg=Listening address=:9093
level=info ts=2021-06-08T22:58:35.988Z caller=tls_config.go:191 msg="TLS is disabled." http2=false
level=info ts=2021-06-08T22:58:37.892Z caller=cluster.go:696 component=cluster msg="gossip not settled" polls=0 before=0 now=1 elapsed=2.001000948s

Как видно по логам - все успешно запустилось. В данном примере мы запустили исполняемый файл из консоли, в реальной ситуации вам конечно же нужно будет описать запуск сервиса через systemd (или запустить его в docker).

Тут стоит упомянуть про интересную утилиту amtool - она позволяет, к примеру, проверять конфигурацию alertmanager на корректность. По сути она аналогична утилите promtool для prometheus. Давайте посмотрим на парочку примеров:

root@prom-test:/opt/alertmanager# ./amtool check-config alertmanager.yml
Checking 'alertmanager.yml' SUCCESS
Found:
- global config
- route
- 1 inhibit rules
- 3 receivers
- 0 templates

root@prom-test:/opt/alertmanager#

В данном случае мы просто проверили что наша конфигурация валидна. Помимо этого вы можете проверить роутинг для своих алертов:

root@prom-test:/opt/alertmanager# ./amtool config routes --config.file alertmanager.yml
Routing tree:
.
└── default-route receiver: web.hook
├── {severity="critical"} receiver: pagerduty-critical
└── {severity="warning"} receiver: slack-warning

root@prom-test:/opt/alertmanager# ./amtool config routes test severity="warning" --config.file alertmanager.yml
slack-warning
root@prom-test:/opt/alertmanager# ./amtool config routes test severity="info" --config.file alertmanager.yml
web.hook
root@prom-test:/opt/alertmanager#

В первом случае мы просто вывели на экран все наши роуты для алертов. Во второй раз мы протестировали что будет с алертом, у которого выставлен тег severity=warning - он попадет на receiver slack-warning. И в третий раз мы протестировали алерт с тегом severity=info - в этот раз алерт уйдет на дефолтный ресивер - web.hook.

Помимо указанных функций, amtool позволяет создавать и искать существующие алерты, замораживать их, а так же показывать информацию по кластеру alertmanager'ов. Очень советую поиграться с данной утилитой на досуге.

Итак, осталось только подправить настройки prometheus, чтобы он мог начать отправлять алерты в alertmanager:

# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
- localhost:9093

Собственно здесь мы по сути отредактировали только одну секцию alerting, которая касается alertmanager'а - alertmanagers: static_configs: и добавили адрес алертменеджера в targets (туда можно добавить несколько алертменеджеров). Как понятно из названия - мы вручную указываем адреса наших алертменеджеров, хотя promethues позволяет автоматически находить доступные alertmanager с помощью средств обнаружения.

После перезапуска prometheus он начнет отправлять алерты в наш alertmanager.

Подключение нотификаций в telegram

Итак, пришла пора самого интересного - настройка телеграм для нотификаций. На самом деле с точки зрения alertmanager все просто - нам достаточно отправить webhook к api телеграмма для отправки нотификации. Но вот со стороны телеграмма нам предстоит сделать несколько шагов. Давайте пройдемся по ним всем:

Во-первых необходимо создать нового бота. Для этого стоит обратиться к боту @BotFather. Прямо набираем в поиске телеграмма такой ник и далее набираем команду /newbot. Указываем его имя - например rebrainme_notify, его ник (он должен заканчиваться на bot) - rebrainme_notify_bot. В ответ botFather выдаст вам API ключ для доступа к боту с помощью API. Сохраните данный токен - мы будем использовать его дальше как $TOKEN.

Найдите вашего бота в поиске по нику - rebrainme_notify_bot и отправьте ему сообщение - для этого сначала нажмите команду start, а после этого напишите что угодно. Да, в ответ он ничего не ответит, но нам это и не требуется - нам всего лишь нужно узнать наш chat_id, который появился когда мы отправили ему сообщение.

Для выяснения chat_id можно отправить http запрос с помощью curl:

$ curl -s https://api.telegram.org/bot$TOKEN/getUpdates | jq .
{
"ok": true,
"result": [
{
"update_id": 736752606,
"message": {
"message_id": 2,
"from": {
"id": 113317645,
"is_bot": false,
"first_name": "Vasiliy",
"last_name": "Ozerov",
"username": "vasiliyozerov",
"language_code": "en"
},
"chat": {
"id": 113317645,
"first_name": "Vasiliy",
"last_name": "Ozerov",
"username": "vasiliyozerov",
"type": "private"
},
"date": 1623194263,
"text": "gfhjkm"
}
}
]
}

Вместо $TOKEN вам необходимо подставить свой token, который вам выдал botFather на первом шаге. И вы можете получить свой chat_id - в нашем случае он равен 113317645.

Теперь вы можете отправлять сообщения через бота с помощью api запросов, используя $TOKEN бота и chat_id, полученный на предыдущем шаге. Давайте попробуем отправить тестовое сообщение сами себе:

$ curl -s 'https://api.telegram.org/bot$TOKEN/sendMessage?chat_id=113317645&text=Heya'

{"ok":true,"result":{"message_id":3,"from":{"id":1765727458,"is_bot":true,"first_name":"rebrainme_notify","username":"rebrainme_notify_bot"},"chat":{"id":113317645,"first_name":"Vasiliy","last_name":"Ozerov","username":"vasiliyozerov","type":"private"},"date":1623194449,"text":"Heya"}}

В этот момент вам должно прийти сообщение от telegram!

Отлично, отправлять сообщения в телеграм мы научились - осталось только подключить к нему alertmanager. К сожалению эта операция не так проста как может показаться. Проблема заключается в том, что alertmanager отправляет по вебхуку данные в своем формате. Точнее используется json, но все поля кастомные и определяются alertmanager'ом. Поэтому напрямую указать webhook телеграм мы не сможем. Ну точнее сможем - но вот текст сообщения изменить не получится - alertmanager этого не сделает.

Чтобы исправить ситуацию, мы можем воспользоваться вспомогательной утилитой, написанной именно для этих целей - она будет принимать алерт от alertmanager и отправлять его в телеграм. Давайте установим ее:

# apt install golang-go
# root@prom-test:/opt/alertmanager# mkdir /opt/prometheus_bot
# export GOPATH=/opt/prometheus_bot
# go get github.com/inCaller/prometheus_bot

Итак, тут мы установили golang компилятор, а после чего установили наше приложение в /opt/prometheus_bot. Теперь достаточно составить для него конфигурацию и подключить к alertmanager'у:

root@prom-test:/opt/prometheus_bot# cd /opt/prometheus_bot/
root@prom-test:/opt/prometheus_bot# cat config.yaml
telegram_token: "$TOKEN"
split_msg_byte: 4000
send_only: true

Собственно осталось только запустить его:

root@prom-test:/opt/prometheus_bot# ./bin/prometheus_bot
2021/06/08 23:39:59 Authorised on account rebrainme_notify_bot
2021/06/08 23:39:59 Works in send_only mode

Теперь настраиваем alertmanager:

route:
group_by: ['alertname']
group_wait: 0s
group_interval: 5m
repeat_interval: 1h
receiver: 'web.hook'

receivers:
- name: 'web.hook'
webhook_configs:
- url: 'http://127.0.0.1:9087/alert/113317645'

В данном случае мы все входящие алерты будем отправлять на нашего prometheus_bot, а тот в свою очередь - в наш чат telegram. Запускаем алертменеджер и можно попробовать затестировать отправку алертов:

root@prom-test:/opt/alertmanager# ./alertmanager
level=info ts=2021-06-08T23:41:33.722Z caller=main.go:221 msg="Starting Alertmanager" version="(version=0.22.2, branch=HEAD, revision=44f8adc06af5101ad64bd8b9c8b18273f2922051)"
level=info ts=2021-06-08T23:41:33.724Z caller=main.go:222 build_context="(go=go1.16.4, user=root@b595c7f32520, date=20210602-07:50:37)"
level=info ts=2021-06-08T23:41:33.746Z caller=cluster.go:184 component=cluster msg="setting advertise address explicitly" addr=10.19.0.5 port=9094
level=info ts=2021-06-08T23:41:33.761Z caller=cluster.go:671 component=cluster msg="Waiting for gossip to settle..." interval=2s
level=info ts=2021-06-08T23:41:33.835Z caller=coordinator.go:113 component=configuration msg="Loading configuration file" file=alertmanager.yml
level=info ts=2021-06-08T23:41:33.835Z caller=coordinator.go:126 component=configuration msg="Completed loading of configuration file" file=alertmanager.yml
level=info ts=2021-06-08T23:41:33.839Z caller=main.go:514 msg=Listening address=:9093
level=info ts=2021-06-08T23:41:33.840Z caller=tls_config.go:191 msg="TLS is disabled." http2=false

Для тестирования отправки достаточно добавить простой алерт с up == 0 в конфигурацию prometheus и после этого добавить статичесую конфигурацию с левым адресом:

alerts.yml:

groups:
- name: instances
interval: 10s
rules:
- alert: InstanceDown
expr: up == 0
labels:
severity: critical
annotations:
summary: Instance is down!

prometheus.yml:

global:
scrape_interval: 15s
evaluation_interval: 15s

alerting:
alertmanagers:
- static_configs:
- targets:
- localhost:9093

rule_files:
- "alerts.yml"

scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']

- job_name: 'down'
static_configs:
- targets: ['localhost:8181']

Собственно все - осталось только ждать, когда вы получите сообщение в telegram!

Итоги

Итак, сегодня мы познакомились с вами с alertmanager'ом - утилитой, которая принимает алерты от prometheus, группирует их и отправляет в нужные каналы нотификаций. Мы разобрались с тем как марщрутизируются оповещения, а так же как настроить оповещения в телеграм. В следующий раз мы поговорим более детально про настройку федерации в Prometheus!

Полезные ссылки:

Prometheus Bot

Alertmanager webhook config

PostMortem best practice

Alertmanager

Notification Templates Examples

Задание:

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

Критерии оценки выполнения задания:

Настройте Prometheus:

1.1. В Prometheus подключены правила алертинга из файла /opt/prometheus/alerts.yml.

1.2. В файле /opt/prometheus/alerts.yml описан простой алерт на доступность сервера (up == 0) с именем InstanceDown

Установите Alertmanager:

2.1. Alertmanager должен быть скачан с официального сайта и установлен в директорию /opt/alertmanager.

2.2. Alertmanager должен быть запущен как сервис c помощью systemd и быть описан в файле /etc/systemd/system/alertmanager.service.

2.3. Alertmanager должен быть запущен на стандартном порту 9093.

2.4. Конфигурационный файл Alertmanager должен находиться по пути /opt/alertmanager/alertmanager.yml.

2.5. Все алерты, которые получены Alertmanager'ом должны незамедлительно отправляться на ваш почтовый ящик с помощью email_config. Вам необходимо задать следующие параметры:

to - кому отправлять сообщения

smarthost - адрес smtp сервера. Вы можете использовать smtp вашего почтового сервера - yandex, mail, google или любого другого. Адреса серверов обычно приводятся в документации соответствующего сервера.

auth_username - имя пользователя который будет аутентифицироваться на почтовом сервере

auth_password - пароль пользователя который будет аутентифицироваться на почтовом сервере

Важно: Скрипт автоматической проверки не проверяет валидность вашего аккаунта smtp. Мы проверяем только настройку алертов и основные параметры alertmanager'а. Если вы боитесь указывать свой пароль - просто протестируйте отправку уведомлений, а перед запуском скрипта автопроверки удалите свой пароль или замените его на password. Или просто создайте новый почтовый ящик.