← Все статьи

Три отказа алертинга из одного корня: имя там, где должен быть ID

Кто-то переименовал канал в Slack, и доставка алертов продакшен-системы молча умерла. Это был третий отказ на одном и том же идентификаторе — все прошлые фиксы меняли транспорт и оставляли имя.

В Gambchamp доставка алертов для продакшен-системы уровня T0 была полностью мертва, и ничто об этом не говорило. Кто-то переименовал канал в Slack. Alertmanager продолжал слать в старое имя, а Slack каждые десять секунд отвечал channel_not_found. Всё это время в очереди висели два живых алерта. Единственным внешним признаком был alertmanager_notifications_failed_total — счётчик, на который никто не смотрел.

Третий раз на том же идентификаторе

Это был третий отказ подряд на одном и том же:

  • сначала общий incoming webhook игнорировал поле channel, и алерты падали в общий канал. Фикс — отдельный Alertmanager;
  • затем bot-токен уважал channel, и доставка заработала — но была привязана к имени канала. Выглядело решённым;
  • наконец, канал переименовали, и доставка умерла.

Каждый фикс менял транспорт и не трогал привязку, поэтому класс отказа пережил все ремонты.

Ярлык — не идентификатор

Имя канала в Slack — человеческий ярлык: владелец может поменять его когда угодно, никого не предупредив. ID канала выдаётся один раз и не меняется никогда — ни при переименовании, ни при смене приватности. Наш конфиг ссылался на ярлык.

Тихим, а не громким отказ сделали две вещи. Slack помечает channel_not_found как неисправимую ошибку, поэтому Alertmanager не ретраит: уведомления выбрасываются, а не откладываются. И мёртвый путь доставки выглядит ровно как спокойная система. Ничего не срабатывает, когда ничто не может сработать.

Фикс

  1. Закрепить канал по ID в receiver’е. Канал приватный, а у бота нет права его перечислять, поэтому API не может проверить ID в режиме только чтения. Я проверил его отложенным сообщением, которое удалил до того, как оно было доставлено, — проба, которая разрешает ID, ничего не публикуя.
  2. Маршрутизировать по явному лейблу, а не по имени. Когда алерты той же системы разделили на прикладной и инфраструктурный каналы, очевидный regex по префиксу имени алерта отправил бы пять guardrail’ов RDS — слоты репликации, хранилище — в прикладной канал. Лейбл signal, выставленный в группе правил, позволяет каждому правилу самому объявить свой слой, а не зависеть от того, как его кто-то назвал.

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

Выводы

  • Всё, что человек может переименовать, — ярлык, а не идентификатор. Привязывайтесь к неизменяемому хэндлу — ID — или к явному объявлению, например к лейблу, который автор ставит намеренно.
  • Тестируйте путь доставки, а не только компонент. Здоровый Alertmanager, который не может доставить, неотличим от отсутствия алертов.
  • Заведите алерт на increase(alertmanager_notifications_failed_total[5m]) > 0. Это самый дешёвый dead-man switch, какой вы когда-либо добавите.
  • Проверьте тот же класс в других местах: consumer group ID, собранные из имён подов, экспортеры с фильтром по соглашению об именах, политики, матчащие объекты по имени.