Содержание
- Почему Alertmanager сам не может позвонить
- Что понадобится
- Шаг 1 — Создайте канал, который вам позвонит
- Шаг 2 — Добавьте webhook-ресивер
- Шаг 3 — Разберитесь, что именно приходит
- Шаг 4 — Отправляйте восстановления тихим пушем
- Шаг 5 — Маршрутизируйте по severity, а не всё подряд
- Шаг 6 — Ловушка Watchdog
- Настройка, чтобы не кричать «Волки!»
- Звонить только вне рабочего времени
- Что делать с пустым commonLabels
- Держите полезную нагрузку небольшой
- Поделиться оповещением с командой
- Чего эта схема не даёт
- Диагностика
- Частые вопросы
- Может ли Prometheus Alertmanager позвонить сам по себе?
- Пробьёт ли звонок режим «Не беспокоить»?
- Работает ли это с Alertmanager за файрволом или внутри Kubernetes?
- Как перестать получать звонки, когда оповещение устраняется?
- Почему телефон звонит каждые четыре часа из-за одного и того же оповещения?
- Можно ли позвонить нескольким людям по одному оповещению?
- Где фильтровать severity — в Alertmanager или в условиях Echobell?
- Итог
- Похожие материалы
У Alertmanager нет голосового ресивера. Чтобы получать звонок при срабатывании оповещения Prometheus, добавьте ресивер webhook_configs, указывающий на канал Echobell с типом подписки Звонок. В этом руководстве — точный YAML, условие, которое не даёт звонить по уже устранённым оповещениям, маршрутизация по severity и оповещение Watchdog, которое иначе будет звонить вам каждые четыре часа бесконечно.
Prometheus — стек метрик по умолчанию для большей части инфраструктуры, построенной за последнее десятилетие, а Alertmanager действительно хорош в сложных частях: дедупликация оповещений, группировка, глушение на время обслуживания и подавление нижестоящего шума при отказе вышестоящей зависимости.
Единственное, чего он не сделает, — не разбудит человека.
Почему Alertmanager сам не может позвонить
В Alertmanager есть ресиверы для e-mail, Slack, PagerDuty, OpsGenie, Discord, Telegram, Pushover, Webex, MS Teams и ещё десятка сервисов. Но все они доставляют сообщение, а сообщения подчиняются переключателю звонка, режиму «Не беспокоить» и режимам фокусирования в iOS. В 03:00 это означает: оповещение пришло, и ничего не произошло.
Никакого voice_configs не существует. Обычно люди приходят к таким вариантам:
- PagerDuty / OpsGenie / Splunk On-Call — они действительно звонят, но это полноценные платформы управления инцидентами с соответствующей ценой за место. Правильный ответ, если вам нужны ротации и деревья эскалации; избыточный, если нужно просто, чтобы телефон зазвонил. (К тому же OpsGenie закрывается — именно поэтому столько команд сейчас пересматривают этот слой.)
- SMS-мосты вроде Sachet — вы поднимаете ещё один сервис, платите шлюзу за сообщение, а SMS всё равно приходит как сообщение. В iOS текст не пробивает режим фокусирования, если отправитель не в списке разрешённых.
- Самописная обвязка над Twilio — написать маленький приёмник вебхуков, купить номер, платить за звонок, и вот у вас появился кусок продакшен-инфраструктуры, единственная задача которого — заставить телефон зазвонить.
Универсальный webhook-ресивер и есть выход. Он отправляет POST с документированным JSON на любой URL — а больше ничего и не нужно.
Что понадобится
- Работающие Prometheus + Alertmanager и доступ к редактированию
alertmanager.yml - Установленный Echobell (App Store / Google Play)
- Десять минут
Руководство написано для Alertmanager 0.31. Полезная нагрузка вебхука уже много лет имеет version: "4", поэтому версии 0.2x ведут себя точно так же.
Вашему Alertmanager нужен исходящий HTTPS до hook.echobell.one. Он не обязан быть доступен из интернета — Alertmanager внутри кластера, VPC или домашней лаборатории работает прекрасно.
Шаг 1 — Создайте канал, который вам позвонит
В Echobell создайте канал с названием вроде Prometheus Critical. Установите тип уведомления подписки на Звонок. Именно эта настройка важна: оповещения типа «Звонок» приходят как экран входящего вызова и звонят сквозь режим фокусирования и «Не беспокоить» в iOS, чего push-уведомление не делает.
Настройте шаблоны так, чтобы они читали полезную нагрузку Alertmanager напрямую:
Title: 🔴 {{commonLabels.alertname}} on {{commonLabels.instance}}
Body: {{commonAnnotations.summary}}
{{commonAnnotations.description}}
И в дополнительных настройках — шаблон ссылки, чтобы из записи об уведомлении сразу открывался график:
{{alerts[0].generatorURL}}
Затем скопируйте Webhook URL канала:
https://hook.echobell.one/t/<channel-token>
Относитесь к этому URL как к секрету: любой, у кого он есть, может заставить ваш телефон звонить.
Шаг 2 — Добавьте webhook-ресивер
В alertmanager.yml:
route:
group_by: ["alertname", "cluster", "service"]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: echobell-critical
receivers:
- name: echobell-critical
webhook_configs:
- url: "https://hook.echobell.one/t/<channel-token>"
send_resolved: false
Перезагрузите конфигурацию через curl -X POST http://localhost:9093/-/reload или SIGHUP.
Обратите внимание на send_resolved: false. Значение по умолчанию для webhook-ресивера — true, в отличие от большинства других ресиверов Alertmanager. Пропустив эту строку, вы получите звонок и когда сервис сломался, и когда он починился сам. Именно второй звонок учит людей игнорировать первый. В шаге 4 показано, как вернуть уведомление о восстановлении без звонка.
Шаг 3 — Разберитесь, что именно приходит
Alertmanager группирует оповещения и отправляет по одному POST на группу:
{
"version": "4",
"groupKey": "{}:{alertname=\"HighErrorRate\"}",
"truncatedAlerts": 0,
"status": "firing",
"receiver": "echobell-critical",
"groupLabels": { "alertname": "HighErrorRate" },
"commonLabels": { "alertname": "HighErrorRate", "severity": "critical" },
"commonAnnotations": { "summary": "Error rate above 5% for 10m" },
"externalURL": "http://alertmanager.internal:9093",
"alerts": [
{
"status": "firing",
"labels": { "alertname": "HighErrorRate", "instance": "api-7d9f:8080" },
"annotations": { "summary": "Error rate above 5% for 10m" },
"startsAt": "2026-09-04T02:41:07.351Z",
"endsAt": "0001-01-01T00:00:00Z",
"generatorURL": "http://prometheus:9090/graph?g0.expr=...",
"fingerprint": "a1b2c3d4e5f60718"
}
]
}
Echobell читает тело JSON как есть, поэтому все эти поля доступны в шаблонах и условиях. Вложенный доступ работает в обоих вариантах синтаксиса — {{commonLabels.severity}} или {{alerts[0].labels["instance"]}}.
Два свойства этой нагрузки определяют всё дальнейшее:
Поле status верхнего уровня равно firing, если активно хотя бы одно оповещение в группе. Оно становится resolved только когда все оповещения группы устранены. Это делает его отличным критерием фильтрации.
commonLabels содержит только метки, общие для всех оповещений группы. Это самый частый сюрприз. Если group_by достаточно широкий и один вебхук несёт HighErrorRate с трёх разных инстансов, то commonLabels.instance отсутствует, и {{commonLabels.instance}} отрисуется пустой строкой. Ниже есть отдельный раздел о том, как с этим быть.
Шаг 4 — Отправляйте восстановления тихим пушем
Вы всё же хотите знать, когда что-то восстановилось, — просто не хотите, чтобы вам за это звонили. Создайте второй канал Echobell Prometheus Recovered, задайте тип Обычный и такие шаблоны:
Title: ✅ {{commonLabels.alertname}} resolved
Body: {{commonAnnotations.summary}}
а в дополнительных настройках — такое условие:
status == "resolved"
Условия — это выражения, которые вычисляются до какой-либо доставки. Если выражение ложно, Echobell примет запрос и ничего не отправит.
Затем направьте один и тот же ресивер на оба канала — ресивер может содержать несколько webhook_configs:
receivers:
- name: echobell-critical
webhook_configs:
# Звонит на телефон. Только при firing.
- url: "https://hook.echobell.one/t/<calling-channel-token>"
send_resolved: false
# Тихий пуш. Условие канала отбрасывает половину firing.
- url: "https://hook.echobell.one/t/<recovery-channel-token>"
send_resolved: true
Канал восстановления получает и firing-, и resolved-нагрузки и отбрасывает первые. Итог: сбой звонит, восстановление приходит пушем, который вы прочитаете утром.
Шаг 5 — Маршрутизируйте по severity, а не всё подряд
Маршрут-«ловушка всего», отправляющий каждое оповещение в звонящий канал, — это машина по производству проигнорированных звонков. Разделяйте по severity в Alertmanager, где дереву маршрутизации и место:
route:
group_by: ["alertname", "cluster", "service"]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: echobell-warning
routes:
# Watchdog никогда не доходит до человека. См. шаг 6.
- matchers:
- alertname = "Watchdog"
receiver: "null"
- matchers:
- severity = "critical"
receiver: echobell-critical
group_wait: 10s
repeat_interval: 1h
receivers:
- name: "null"
- name: echobell-critical
webhook_configs:
- url: "https://hook.echobell.one/t/<calling-channel-token>"
send_resolved: false
- name: echobell-warning
webhook_configs:
- url: "https://hook.echobell.one/t/<normal-channel-token>"
send_resolved: true
Маршруты вычисляются сверху вниз, и побеждает первое совпадение — continue по умолчанию false. Поэтому порядок важен: маршрут Watchdog должен стоять выше всего, что могло бы его поглотить.
Если вы предпочитаете оставить один канал и фильтровать на стороне Echobell, эквивалентное условие такое:
status == "firing" && commonLabels.severity == "critical"
Обычно лучше делать это в Alertmanager, потому что там severity заодно управляет group_wait и repeat_interval. Вариант с Echobell лучше, когда изменение конфигурации сегодня не пройдёт ревью.
Шаг 6 — Ловушка Watchdog
Если вы используете kube-prometheus-stack, у вас есть оповещение Watchdog с выражением vector(1). Оно спроектировано так, чтобы срабатывать всегда: оно существует, чтобы внешняя система могла заметить, что сам Prometheus остановился. Конфигурация по умолчанию направляет его в ресивер null.
Направьте маршрут-«ловушку всего» на звонящий канал, не исключив его, — и Watchdog будет звонить вам каждый repeat_interval, вечно, начиная прямо сейчас. Это причина номер один, по которой люди приходят к выводу, что звонковые оповещения «не работают».
Оставьте маршрут null из шага 5. А потом, по желанию, сделайте с ним нечто полезное: превратите Watchdog в настоящий «выключатель мертвеца».
- matchers:
- alertname = "Watchdog"
receiver: deadmansswitch
group_wait: 0s
group_interval: 1m
repeat_interval: 50s
receivers:
- name: deadmansswitch
webhook_configs:
- url: "https://hc-ping.com/<your-check-uuid>"
send_resolved: false
Echobell не может быть таким выключателем сам: он оповещает, когда запрос приходит, а не когда запросы прекращаются. Поэтому отправляйте пинг Watchdog в сервис, созданный для обнаружения тишины (Healthchecks.io, Cronitor, Dead Man's Snitch), а затем направьте вебхук «проверка упала» этого сервиса на ваш звонящий канал Echobell. Теперь звонок означает «умер сам мониторинг» — то самое оповещение, ради которого вы больше всего хотите быть разбужены, и то, которое никто не настраивает.
Настройка, чтобы не кричать «Волки!»
Три настройки Alertmanager делают большую часть работы, плюс одна в Prometheus:
| Настройка | Где | Что делает |
|---|---|---|
for: | Правило оповещения | Как долго условие должно держаться, прежде чем оповещение вообще сработает. Первая линия защиты от двухсекундного сбоя. |
group_wait | Маршрут | Сколько ждать другие оповещения перед первым уведомлением. По умолчанию 30s; для critical снизьте до 10s. |
group_interval | Маршрут | Минимальный интервал перед уведомлением о новых оповещениях в существующей группе. По умолчанию 5m. |
repeat_interval | Маршрут | Как часто повторно уведомлять о неустранённом оповещении. По умолчанию 4h — значит ночной сбой позвонит вам в 03:00 и ещё раз в 07:00. |
repeat_interval — та настройка, над которой стоит подумать. Четыре часа — долго для чего-то сломанного; двадцать минут — машина, которая заставит вас отключить канал. Час для critical — разумная отправная точка.
Если вы хотите, чтобы неотвеченный звонок повторялся сразу, а не ждал следующего repeat_interval, включите Повторять неудавшийся звонок в настройках приложения Echobell.
Звонить только вне рабочего времени
В рабочее время вы, скорее всего, и так смотрите в дашборд. Системные переменные времени Echobell (все в UTC) позволяют одному каналу вести себя по-разному в зависимости от часа, без второго маршрута в Alertmanager:
status == "firing" && (hour >= 17 || hour < 9)
Это звонит вам только вне 09:00–17:00 UTC. Направьте второй канал типа «Обычный» на обратный интервал для дневных пушей:
status == "firing" && hour >= 9 && hour < 17
Добавьте dayOfWeek >= 1 && dayOfWeek <= 5, чтобы выходные тоже считались нерабочим временем. Помните, что всё считается в UTC — сместите под свой часовой пояс. Подробнее — в статье Уведомления по временным окнам с UTC-условиями.
Что делать с пустым commonLabels
Когда группа содержит оповещения с нескольких инстансов, commonLabels.instance исчезает, и заголовок уведомления выглядит как 🔴 HighErrorRate on .
Три выхода, по убыванию предпочтительности:
- Добавьте метку в
group_by. Еслиgroup_byвключаетinstance, все оповещения группы её разделяют, иcommonLabels.instanceвсегда есть. Цена — больше уведомлений: одно на инстанс вместо одного на имя оповещения. - Читайте первое оповещение.
{{alerts[0].labels.instance}}всегда заполнено. Это лишь одно из, возможно, многих, поэтому дополните его счётчиком:{{alerts[0].labels.instance}} (+{{alerts.length}} оповещ.). - Спроектируйте подпись так, чтобы пустое значение читалось. В Echobell нет оператора значения по умолчанию —
{{a || "unknown"}}отрисует буквальный текстtrue, а не запасное значение — поэтому пишитеInstance: {{commonLabels.instance}}отдельной строкой, где пустое значение очевидно пусто, а не ломает фразу.
Держите полезную нагрузку небольшой
Группа, охватывающая сотню подов, даёт большое тело JSON, а Echobell отклоняет тела триггеров больше 1 МиБ с HTTP 413. Ограничьте это в Alertmanager:
- url: "https://hook.echobell.one/t/<channel-token>"
send_resolved: false
max_alerts: 20
Тогда Alertmanager отправит не более двадцати оповещений и запишет в truncatedAlerts количество отброшенных, которое можно вывести в теле:
Body: {{commonAnnotations.summary}}
Alerts: {{alerts.length}} (+{{truncatedAlerts}} отброшено)
Поделиться оповещением с командой
Канал Echobell можно разделить по ссылке подписки, и каждый подписчик выбирает свой тип уведомления. Поэтому один и тот же маршрут может звонить дежурному инженеру и приходить обычным пушем всем остальным — без оплаты за место и без дополнительных правил маршрутизации в Alertmanager.
Это же соответствует причине, по которой многие команды вообще держат Prometheus у себя: метрики и правила оповещений остаются на вашей инфраструктуре, а Echobell хранит содержимое и историю уведомлений на устройстве, а не на своих серверах.
Чего эта схема не даёт
Честность о границах избавит вас от плохой миграции в будущем. Echobell — слой доставки, а не платформа управления инцидентами. В нём нет:
- Графиков дежурств и передачи смен follow-the-sun
- Деревьев эскалации, вызывающих второго человека, если первый не ответил
- Хронологии инцидентов, отслеживания подтверждений и инструментов постмортема
Если вашей команде это нужно, вам нужен PagerDuty, Grafana Cloud IRM или аналог. Здесь закрыт конкретный пробел, который оставляет Alertmanager: превратить сработавшее оповещение в телефон, который действительно звонит. Для одиночных админов, небольших команд и домашних лабораторий этого обычно достаточно.
Диагностика
Вообще ничего не приходит. Сначала посмотрите логи самого Alertmanager (level=error component=dispatcher), затем убедитесь, что маршрут действительно разрешается в ваш ресивер: amtool config routes test severity=critical alertname=HighErrorRate покажет, в какой ресивер попадёт оповещение, не дожидаясь реального срабатывания.
Echobell возвращает HTTP 404. Токен канала неверен или канал удалён. Неизвестный токен даёт 404, а не тихий успех.
Echobell возвращает 200 с "notificationTriggered": false. Ваше условие оказалось ложным. В теле ответа также есть "conditionsMet": false — самый быстрый способ отличить «условие написано неверно» от «вебхук вообще не дошёл». Сверьте status == "firing" с тем, что реально прислал Alertmanager, — это статус верхнего уровня, а не alerts[0].status.
HTTP 413. Нагрузка превысила 1 МиБ. Задайте max_alerts, как показано выше.
HTTP 405. У канала включён POST Only, а что-то отправило GET. Alertmanager использует POST, так что обычно это значит, что вы открыли URL в браузере.
В заголовке пустой пробел. В commonLabels для этой группы не было такой метки. См. раздел выше.
Не звонит, но уведомление приходит. Тип уведомления подписки — «Обычный» или «Срочный», а не «Звонок». Тип выбирается каждым подписчиком, поэтому проверьте на том устройстве, которое не звонит.
Тестирование без вреда для продакшена. Добавьте правило с expr: vector(1), отдельным alertname и severity: critical, дайте ему сработать один раз и удалите. Либо отправьте оповещение вручную:
curl -X POST http://localhost:9093/api/v2/alerts -H 'Content-Type: application/json' -d '[
{"labels":{"alertname":"EchobellTest","severity":"critical"},
"annotations":{"summary":"Testing the phone call path"}}
]'
Частые вопросы
Может ли Prometheus Alertmanager позвонить сам по себе?
Нет. У Alertmanager есть ресиверы для e-mail, Slack, PagerDuty, OpsGenie и многих других сервисов, но нет ни голосового, ни SMS-ресивера. Для звонков нужно направить универсальный webhook-ресивер в сервис, умеющий звонить, например Echobell, либо оплачивать платформу управления инцидентами.
Пробьёт ли звонок режим «Не беспокоить»?
Да. Тип уведомления «Звонок» в Echobell отображается как входящий вызов и звонит сквозь режим фокусирования и «Не беспокоить» в iOS. Подробности и нужные настройки — в статье Как обойти «Фокусирование» в iOS для критических оповещений.
Работает ли это с Alertmanager за файрволом или внутри Kubernetes?
Да. Вебхук — это исходящий HTTPS-запрос от Alertmanager, ему достаточно доступа до hook.echobell.one. Вашему Alertmanager не нужен ни публичный адрес, ни ingress.
Как перестать получать звонки, когда оповещение устраняется?
Установите send_resolved: false в конфигурации вебхука, указывающего на звонящий канал. По умолчанию webhook-ресивер использует true, в отличие от большинства других ресиверов Alertmanager, так что это отказ, а не подписка. Чтобы всё же тихо получать восстановления, добавьте второй канал с условием status == "resolved".
Почему телефон звонит каждые четыре часа из-за одного и того же оповещения?
Это repeat_interval со значением по умолчанию 4h. Alertmanager повторно уведомляет о всё ещё активном оповещении с этой периодичностью. Задавайте его на уровне маршрута — 1h для critical частый выбор. Если звонки начались сразу после добавления маршрута-«ловушки всего», виновником, скорее всего, является всегда активное оповещение Watchdog; см. шаг 6.
Можно ли позвонить нескольким людям по одному оповещению?
Да. Поделитесь каналом с коллегами, и каждый подписчик выберет свой тип уведомления. Все подписанные на звонящий канал получат вызов, без оплаты за место.
Где фильтровать severity — в Alertmanager или в условиях Echobell?
Лучше в Alertmanager: маршрутизация там же позволяет задавать group_wait и repeat_interval для каждой severity, а дерево маршрутов остаётся в системе контроля версий вместе с остальной конфигурацией. Условия Echobell пригодятся, когда изменить конфиг Alertmanager нельзя, или для фильтров, которых в Alertmanager попросту нет, — например по времени суток.
Итог
Вся настройка — это один ресивер, одна строка send_resolved: false и дерево маршрутов, которое держит всё, кроме severity: critical, подальше от звонящего канала. Ваши правила оповещений, группировки, глушения и подавления остаются ровно такими, какими были, а разрыв между «Prometheus заметил» и «человек заметил» закрывается.
Скачайте Echobell для iPhone или возьмите его в Google Play, а затем отправьте оповещение EchobellTest из примера выше, прежде чем доверять этому пути что-то по-настоящему важное.