---
title: "Звонки по оповещениям Prometheus Alertmanager: будить только ради критического"
description: "У Alertmanager нет голосового ресивера. Как превратить оповещения Prometheus в звонок только для severity critical: конфиг вебхука, условия и ловушка Watchdog."
date: 2026-09-04
author: Nooc
authorAvatarLink: /images/avatars/nooc.webp
authorLink: https://nooc.me
tags:
  - Prometheus
  - Alertmanager
  - звонки по оповещениям
  - webhook-уведомления
  - Kubernetes
  - дежурство
---

# Звонки по оповещениям Prometheus Alertmanager: будить только ради критического

У Alertmanager нет голосового ресивера. Чтобы получать звонок при срабатывании оповещения Prometheus, добавьте ресивер `webhook_configs`, указывающий на канал Echobell с типом подписки **Звонок**. В этом руководстве — точный YAML, условие, которое не даёт звонить по уже устранённым оповещениям, маршрутизация по severity и оповещение Watchdog, которое иначе будет звонить вам каждые четыре часа бесконечно.

Prometheus — стек метрик по умолчанию для большей части инфраструктуры, построенной за последнее десятилетие, а [Alertmanager](https://prometheus.io/docs/alerting/latest/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 [закрывается](/ru/blog/opsgenie-end-of-life-alternatives) — именно поэтому столько команд сейчас пересматривают этот слой.)
- **SMS-мосты** вроде [Sachet](https://github.com/messagebird/sachet) — вы поднимаете ещё один сервис, платите шлюзу за сообщение, а SMS всё равно приходит как сообщение. В iOS текст не пробивает режим фокусирования, если отправитель не в списке разрешённых.
- **Самописная обвязка над Twilio** — написать маленький приёмник вебхуков, купить номер, платить за звонок, и вот у вас появился кусок продакшен-инфраструктуры, единственная задача которого — заставить телефон зазвонить.

Универсальный **webhook-ресивер** и есть выход. Он отправляет POST с документированным JSON на любой URL — а больше ничего и не нужно.

## Что понадобится

- Работающие Prometheus + Alertmanager и доступ к редактированию `alertmanager.yml`
- Установленный Echobell ([App Store](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-alertmanager-phone-call-alerts-ru&mt=8) / [Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid))
- Десять минут

Руководство написано для 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`:

```yaml
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 на группу:

```json
{
  "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`:

```yaml
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, где дереву маршрутизации и место:

```yaml
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](https://github.com/prometheus-operator/kube-prometheus), у вас есть оповещение `Watchdog` с выражением `vector(1)`. Оно *спроектировано* так, чтобы срабатывать всегда: оно существует, чтобы внешняя система могла заметить, что сам Prometheus остановился. Конфигурация по умолчанию направляет его в ресивер `null`.

Направьте маршрут-«ловушку всего» на звонящий канал, не исключив его, — и Watchdog будет звонить вам каждый `repeat_interval`, вечно, начиная прямо сейчас. Это причина номер один, по которой люди приходят к выводу, что звонковые оповещения «не работают».

Оставьте маршрут `null` из шага 5. А потом, по желанию, сделайте с ним нечто полезное: превратите Watchdog в настоящий «выключатель мертвеца».

```yaml
    - 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](https://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-условиями](/ru/blog/time-window-notifications-using-utc-conditions).

## Что делать с пустым `commonLabels`

Когда группа содержит оповещения с нескольких инстансов, `commonLabels.instance` исчезает, и заголовок уведомления выглядит как `🔴 HighErrorRate on `.

Три выхода, по убыванию предпочтительности:

1. **Добавьте метку в `group_by`.** Если `group_by` включает `instance`, все оповещения группы её разделяют, и `commonLabels.instance` всегда есть. Цена — больше уведомлений: одно на инстанс вместо одного на имя оповещения.
2. **Читайте первое оповещение.** `{{alerts[0].labels.instance}}` всегда заполнено. Это лишь одно из, возможно, многих, поэтому дополните его счётчиком: `{{alerts[0].labels.instance}} (+{{alerts.length}} оповещ.)`.
3. **Спроектируйте подпись так, чтобы пустое значение читалось.** В Echobell нет оператора значения по умолчанию — `{{a || "unknown"}}` отрисует буквальный текст `true`, а не запасное значение — поэтому пишите `Instance: {{commonLabels.instance}}` отдельной строкой, где пустое значение очевидно пусто, а не ломает фразу.

## Держите полезную нагрузку небольшой

Группа, охватывающая сотню подов, даёт большое тело JSON, а Echobell отклоняет тела триггеров больше 1 МиБ с HTTP 413. Ограничьте это в Alertmanager:

```yaml
      - 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`, дайте ему сработать один раз и удалите. Либо отправьте оповещение вручную:

```bash
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 для критических оповещений](/ru/blog/how-to-bypass-ios-focus-mode-for-critical-alerts).

### Работает ли это с 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](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-alertmanager-phone-call-alerts-ru&mt=8) или [возьмите его в Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid), а затем отправьте оповещение `EchobellTest` из примера выше, прежде чем доверять этому пути что-то по-настоящему важное.

---

## Похожие материалы

- [Документация по интеграции с Prometheus](/ru/docs/developer/prometheus)
- [Справочник по условиям канала](/ru/docs/conditions)
- [Телефонные звонки по оповещениям Grafana через Echobell](/ru/blog/grafana-call-notification)
- [Звонки от Uptime Kuma: как заставить телефон звонить при сбоях](/ru/blog/uptime-kuma-phone-call-alerts)
- [Усталость от оповещений реальна: как её решают грамотные разработчики](/ru/blog/fix-alert-fatigue-developer-guide)
