# Тривога

## Що це таке

Тривога — це автоматичний сигнал про те, що умова, за якою стежить система, трималася достатньо
довго, щоб заради неї варто було відірвати людину від справ. В українських командах часто кажуть
просто «алерт», а тривогу, через яку будять чергового серед ночі, англійською називають page.

За цим словом стоять два різні об'єкти, і Prometheus розводить їх настільки чітко, що різницю легко
помітити. Правило тривоги в Prometheus породжує тривоги, по одній на кожен окремий набір міток, для
якого вираз повертає результат. Далі вони надходять до окремої програми, Alertmanager, а та
перетворює їх на сповіщення: лист, повідомлення в чаті, дзвінок через сервіс чергувань. Сам проєкт
прямо пише, що правила тривог самі по собі ще не є повноцінною системою сповіщень. Розрізняти ці дві
речі доводиться тому, що їхня кількість розходиться. П'ятдесят тривог можуть прийти одним листом,
одна тривога може нагадувати про себе кожні кілька годин, а тривога, якій вимкнули звук, до людей не
доходить, хоча й далі спрацьовує.

Життєвий цикл у тривоги короткий. Поки вираз нічого не повертає, тривога неактивна. Щойно він уперше
дає результат, тривога переходить у стан очікування (pending) і лишається в ньому стільки, скільки
вимагає параметр `for` у правилі. Якщо наприкінці цього проміжку умова все ще виконується, тривога
спрацьовує (firing) і йде далі на доставку. Коли умова перестає виконуватися, тривога завершується
(resolved). Новіший параметр `keep_firing_for` утримує тривогу в стані спрацювання ще деякий час
після того, як умова зникла; документація пояснює його як захист від мерехтіння та хибних
повідомлень про те, що все вже гаразд. Крім того, Prometheus записує стани очікування й спрацювання
як часовий ряд `ALERTS`, тож до самих тривог можна робити запити й показувати їх на графіках.

Правило, яке чекає двадцять хвилин, перш ніж спрацювати, і ще десять, перш ніж відпустити, має такий
вигляд:

```yaml
groups:
    - name: exports
      rules:
          - alert: ExportQueueStalled
            expr: exports_waiting > 0 and rate(exports_done_total[15m]) == 0
            for: 20m
            keep_firing_for: 10m
            labels:
                severity: page
                team: billing
            annotations:
                summary: "{{ $value }} exports queued and none finishing"
```

Мітки й анотації схожі на вигляд, але виконують різну роботу. Мітки входять до того, що робить
тривогу саме цією тривогою, і Alertmanager читає їх, щоб вирішити, куди її надіслати: `severity:
page` може потрапити на телефон, а `severity: ticket` — у чергу заявок. Анотації несуть слова,
потрібні людині, коли повідомлення вже надійшло: короткий підсумок, довший опис, посилання на
інструкцію з першими кроками. Анотації можна складати з шаблонів, що підставляють мітки тривоги та
значення, яке її викликало, і саме тому підсумок у прикладі вище здатен назвати число.

Тривога й інцидент — різні поняття. Тривога повідомляє, що умова справдилася, а чи є те, що
відбувається, інцидентом, вирішує людина.

## Чому це важливо

Тривога — єдина частина моніторингу, яка не чекає, поки на неї подивляться. Метрика лежить у
сховищі, дашборд висить на екрані, журнал чекає на пошуковий запит, а тривога вривається в чийсь
вечір. Тож кожне правило тривоги — це невелика постійна заявка на чиюсь увагу, і вона справедлива
лише тоді, коли людина може щось із цим зробити. Настанови Prometheus щодо тривог радять не будити
людей, коли робити нічого, і тримати систему тривог нескладною.

Друге — звичайна арифметика. Одна несправність зазвичай зачіпає багато правил водночас. Якщо через
розділення мережі половина екземплярів сервісу втрачає зв'язок із базою даних, кожен екземпляр може
надіслати власну тривогу, і документація Alertmanager саме на цьому прикладі показує, як сотні
тривог стають одним сповіщенням. Alertmanager збирає разом тривоги зі спільними мітками,
переліченими в `group_by`, чекає `group_wait` (типово 30 секунд), щоб перше повідомлення вмістило
всю групу, надсилає оновлення для групи не частіше ніж раз на `group_interval` (5 хвилин) і повторює
повідомлення про тривогу, що досі спрацьовує, через `repeat_interval` (4 години). Пригнічення йде ще
далі й приглушує одні тривоги, поки спрацьовує інша, тож тривога про недоступність усього кластера
може заглушити тривоги окремих сервісів у ньому. Silence, як це називає Alertmanager, вимикає звук
для тривог із певними мітками на визначений час, зазвичай на період планових робіт.

Третє — кінець історії. Більшість отримувачів надсилає повідомлення, коли тривога завершується, але
для поштового отримувача параметр `send_resolved` типово має значення false. Команда, яка отримує
тривоги поштою, може побачити, як проблема почалася, і так і не дізнатися, коли вона скінчилася.

## Як це виглядає на практиці

Тривоги на власному хості компанії влаштовано навпаки. Машина має 464 МБ пам'яті й тримає сервер
git-репозиторіїв, базу даних і вебсервер, а найімовірніше її погубить вичерпана пам'ять або
переповнений диск, і обидва збої настають мовчки. Тривога, яка розраховує, що хост сам повідомить
про свою відмову, тут марна, бо говорити довелося б саме тому хостові, який відмовляє. Тому
[таймер на хості пінгує зовнішній сервіс лише тоді, коли і пам'ять, і диск у нормі](/uk/portfolio/built-dead-mans-switch-monitoring-115/),
раз на чверть години з невеликим випадковим зсувом, а зовнішній сервіс надсилає тривогу, коли пінги
перестають надходити. Хоч би як зламався хост, зокрема через утрачену мережу чи завантаження, що
зависло, усе закінчується тим самим пропущеним пінгом, і саме на це розрахована схема. Сторінка
називає й межу такого рішення: перевірка доводить, що машина здорова, але про те, чи відкривається
сайт, вона нічого не каже.

Та сама інверсія захищає завдання за розкладом. Зашифроване резервне копіювання на інший хост
запускається таймером і
[пінгує свій перемикач лише тоді, коли запуск вдався](/uk/portfolio/built-encrypted-backups-with-a-monthly-restore-drill-114/),
тож невдалий запуск лишає по собі прогалину в низці пінгів, і цю прогалину помічає щось поза
машиною, а не рядок у журналі на ній самій.

Інвертовані тривоги мають власний спосіб зламатися, і він проявився, коли платформа мала вирости з
одного хоста до двох. Якби обидва хости ділили один перемикач, це
[послабило б саме ту систему тривог, яку мали розширити](/uk/portfolio/split-every-operational-secret-per-host-125/):
поки пінгує хоча б один хост, перевірка лишається зеленою для обох, навіть коли другий уже мертвий.
Кожен перемикач став окремим значенням для свого хоста, а поля перемикача для наявного хоста свідомо
залишили порожніми, тож конфігурація показує, що з'єднання передбачене, а значення ще треба вписати.

## Де виникають проблеми

**Виміряне значення потрапляє в мітку.** Оскільки мітки визначають тривогу, мітка з поточним числом
перетворює кожну зміну цього числа на нову тривогу й завершує попередню. Отримувач бачить потік пар
«спрацювала — завершилася» через одну проблему. Залишайте мітки для ідентичності й маршрутизації, а
числа виносьте в анотації.

**Тривога мерехтить.** Умова, що коливається біля порогу, спрацьовує, завершується й знову
спрацьовує кожні кілька хвилин, і кожне повідомлення про завершення каже читачеві неправду. Подовжте
`for`, щоб короткий сплеск минав раніше, ніж про нього хтось дізнається, і додайте
`keep_firing_for`, щоб коротке падіння нижче порогу не закривало тривогу.

**Silence живе довше за свою причину.** Хтось вимикає звук тривоги на час робіт із надто широкою
умовою та щедрим часом завершення, і справжній збій наступного тижня надходить беззвучно. Обмежуйте
silence так вузько, як дозволяє робота, ставте кінець близько до того часу, коли роботу мають
завершити, і пишіть у полі коментаря, хто його створив і навіщо.

**Широкий маршрут поглинає вужчі.** Alertmanager обходить дерево маршрутизації по черзі і, якщо
маршрут не має `continue: true`, зупиняється на першому дочірньому маршруті, що підходить. Загальний
маршрут, поставлений над маршрутом окремої команди, забирає тривоги цієї команди з собою. Ставте
найвужчі маршрути першими й перевіряйте дерево на прикладах міток, перш ніж на нього покладатися.

**Усе стає терміновим.** Диск, заповнений на 70%, сертифікат, що спливає наступного місяця, і
платіжний сервіс, який повертає помилки, дзвонять на той самий телефон, і незабаром на телефон
перестають зважати. Будіть людей лише через умови, які вимагають дій негайно, решту надсилайте в
чергу заявок чи щоденне зведення, а правило, яке місяцями спрацьовує без жодної реакції, видаляйте.

## Що почитати далі

- [Alerting overview](https://prometheus.io/docs/alerting/latest/overview/) — як правила тривог у
  Prometheus і Alertmanager ділять між собою роботу та з яких кроків складається їхнє налаштування.
- [Alerting rules](https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/) —
  синтаксис правил, зокрема `for`, `keep_firing_for`, мітки, анотації та шаблони.
- [Alertmanager](https://prometheus.io/docs/alerting/latest/alertmanager/) — групування,
  пригнічення, silences і робота кількох екземплярів Alertmanager для високої доступності.
- [Alertmanager configuration](https://prometheus.io/docs/alerting/latest/configuration/) — дерево
  маршрутизації, таймери групування, правила пригнічення й налаштування кожного отримувача, серед
  них `send_resolved`.
- [Alerting practices](https://prometheus.io/docs/practices/alerting/) — власні поради проєкту про
  те, заради чого варто будити людей, а що має йти іншими каналами.

<https://engineer.company/uk/glossary/alert/>
