Перейти к содержимому

Инцидент-менеджмент и postmortem

Алерт пришёл в 3:12 ночи: HighLatencyP95, p95 вырос с 120 мс до 1.4 секунд. Что дальше? Кто решает, насколько это страшно? Кто пишет пользователям? Кто откатывает релиз? Если на эти вопросы ответа нет — это не «командная работа», это толпа в чате, каждый со своим мнением, пока бизнес теряет деньги. Наблюдаемость из прошлой главы (трейсинг и алертинг) говорит тебе, что сломалось. Инцидент-менеджмент — это дисциплина как на это реагировать: кто, в каком порядке, с какой скоростью.

В малых командах на это смотрят свысока: «у нас три человека, какие роли, давайте просто починим». Работает, пока инциденты простые. Но хаос масштабируется быстрее людей, и весь арсенал (severity-матрица, таймлайн, postmortem) пригодится любой команде — для pet-проекта он даже важнее: запаса по прочности нет.

Разберём, что считать инцидентом (не каждый алерт — инцидент!), как классифицировать его по severity SEV1–SEV4, какие роли нужны и по какому таймлайну двигаться: detection → triage → mitigation → resolution → review. Отдельный блок — коммуникации. Затем самое ценное — blameless postmortem, который делает систему сильнее, а не ищет виноватого. И финал: как error budget превращает инциденты в объективные сигналы и зачем проводить учения (game day), когда всё работает.

Не всё, что пищит, — инцидент. Различай четыре вещи:

  • Алерт — сигнал системы мониторинга. Алертов много, они шумят, большинство снимаются сами. Алерт — повод посмотреть.
  • Инцидент — событие, которое деградирует сервис для пользователей или грозит деградацией в ближайшем будущем. Критерий — не «страшное в логах», а влияние на пользователя: растёт p95, падает availability, теряются данные.
  • Баг — дефект в коде. Баг становится инцидентом, когда доехал до продакшена и бьёт по пользователям. Баг в staging — работа в бэклоге.
  • Запрос поддержки — один пользователь не может войти. Причина у него одного — тикет. Не могут войти все — SEV1.

Формулировка «деградация для пользователей» широкая: покрывает и «API лежит», и «почта задерживается на час» (для SaaS с онбордингом — инцидент, новые пользователи не активируются). И наоборот: диск на 92% — алерт warning, но ещё не инцидент, пока сервис не отдаёт 5xx.

Классификация определяет: скорость реакции, кого будить, чем жертвовать. Одна матрица на всю команду, вывешенная на виду.

Severity Определение Реакция Пример
SEV1 Сервис недоступен или теряются данные для всех пользователей Немедленно, все руки, эскалация вне команды База недоступна, API отдаёт 100% 5xx
SEV2 Существенная деградация для многих: часть функций не работает, p95 вырос в разы В течение 15–30 минут в рабочее время; дежурный вне его Оплата падает для 20% платежей; p95 ×10 после релиза
SEV3 Незначительная деградация, обходные пути есть В рабочее время, по приоритету бэклога Экспорт CSV работает, но медленно; degraded фича-флаг
SEV4 Нет влияния на пользователей: инфраструктурный долг, аномалия без последствий Планово Диск заполнен на 90%, но сервис работает

Два принципа, которые делают матрицу рабочей, а не декоративной:

  1. Severity присваивается по влиянию, а не по причине. Симптом «p95 вырос в 10 раз» — это SEV2, пока ты не понял, что это один фоновый воркер (SEV3), или что база вот-вот ляжет (SEV1). Severity динамический: пересматривается по мере triage.
  2. У каждого severity — владелец и SLA реакции. SEV1 — «кто угодно, прямо сейчас». SEV2 — дежурный on-call. SEV3/4 — тикет в бэклог. Без владельца матрица — красивая таблица.

Классические роли — Incident Commander (IC), Communications Lead и Operations Lead. В команде из трёх человек роли раздаются по ходу — но называть их вслух обязательно, иначе все чинят и никто не координирует.

  • Incident Commander (IC) — единая точка принятия решений. Не обязан чинить; его задача — держать картину: что знаем, что пробуем, что дальше. Слово IC решает споры: «деплоим фикс сразу» vs «сначала реплицируем».
  • Communications (comms) — весь внешний и внутренний поток: статус в чат команды, статус-страница для пользователей, ответы поддержке. Ops не отвлекается на «а что отвечать клиенту?» — comms уже ответил по шаблону. Правило: нет подтверждённой информации — говорим «расследуем, обновление через 30 минут», а не молчим.
  • Ops (operations) — собственно починка: метрики, логи, трейсы, откат, hotfix. Может быть несколько человек, но IC должен знать, кто чем занят.
detection ──► triage ──► mitigation ──► resolution ──► review
(минуты) (минуты) (часы) (часы/дни) (дни, вне паники)

Источники по убыванию ценности: алерт из мониторинга (лучший случай — узнали до пользователей), жалоба пользователя, заметил сам разработчик. Первым узнаёт пользователь — дырка в наблюдаемости, это отдельный action item postmortem.

Первые 10–15 минут: подтвердить симптом, присвоить severity, назначить роли. Главное решение — стратегия: чинить вперёд (forward fix) или откатывать (rollback). Откат дешевле, пока схема БД обратимо совместима. Если триггер — миграция базы, откат может быть невозможен (см. главу про PostgreSQL-бэкапы) — тогда triage обязан сразу включить план восстановления данных.

Цель mitigation — не починить, а сделать больно перестало. Набор: откат релиза, feature flag off, увеличить реплики, отключить интеграцию. Иногда — осознанная деградация: «отключаем рекомендации, остальное работает». Правило: mitigation без фикса — нормально, фикс без mitigation — рискованно. Откат дешевле элегантного hotfix в разгар инцидента.

Сервис вернулся к базовой линии, подтверждено метриками, а не «кажется, работает». Подожди 15–30 минут стабильных графиков, потом resolved.

Разбор идёт вне режима пожаротушения, через 3–5 рабочих дней после resolution.

Набор каналов для малой команды:

  • #incidents в мессенджере — единый канал, только по делу. Значимое событие — одной строкой с timestamp: «14:32 IC: подтверждён релиз 2.14.0 как триггер, откатываем». Чат станет скелетом таймлайна postmortem.
  • Status page / пост для пользователей — шаблон: что наблюдаем (без внутренней кухни), влияние, следующее обновление через N минут, workaround если есть.

Три правила профессионалов: обновления по таймеру (каждые 30 минут для SEV1/2, даже «без изменений»), единый голос (пишет только comms), честность про данные (риск потери данных — сказать сразу).

Цель postmortem — не «кто виноват», а «как система допустила это и что изменим, чтобы повторение стало маловероятным или дешёвым». Пока инцидент воспроизводим действиями одного человека в обычном процессе — проблема в системе.

  1. Summary — что случилось, влияние, длительность. Две-три фразы без контекста.
  2. Severity и метрики влияния — потраченный error budget, минуты деградации, затронутые запросы.
  3. Timeline — по минутам: detection → triage → mitigation → resolution. Источник — #incidents-чат, метрики, логи деплоя.
  4. Root cause и contributing causes — отдельно! Root cause — непосредственная причина (отсутствующий индекс после миграции). Contributing — всё, что сделало инцидент возможным и дорогим: нет canary-выкатки, алерт p95 с for: 10m, ручной откат.
  5. Что сработало хорошо — обязательный раздел: алерт сработал, откат откатил, коммуникации шли по расписанию. Иначе разбор — самобичевание.
  6. Corrective actions — конкретные, с владельцем и дедлайном. Не «усилить тестирование», а «проверка миграций на sequential scan для таблиц > 1M строк в CI, владелец: Анна, до 20 числа».
# Postmortem: p95 API вырос в 10 раз после релиза 2.14.0
- Дата: 2026-09-08 | Severity: SEV2 (начат как SEV3, повышен) | Ведущий: on-call
- Влияние: p95 1.4 s вместо 120 мс в течение 47 минут; ~12% запросов с таймаутом;
error budget сгорел на 8% месячного. Данные не пострадали.
## Timeline (UTC+3)
- 14:31 — алерт HighLatencyP95 (for: 10m) в Telegram, on-call подтвердил (14:34)
- 14:40 — triage: триггер = релиз 2.14.0 (deploy 14:12). IC назначен, чат #incidents
- 14:47 — mitigation: rollback на 2.13.2 через деплой-пайплайн
- 15:18 — p95 вернулся к 130 мс; наблюдение за стабильностью
- 15:35 — resolved, статус-страница обновлена
- 16:10 — postmortem-черновик создан из лога #incidents
## Root cause
Миграция 20260908_add_orders_index выполняла DDL-транзакцию (обычный
CREATE INDEX и ALTER TABLE, не CONCURRENTLY). Блокировка AccessExclusiveLock
от DDL встала в очередь за долгим запросом к orders — все SELECT по orders
встали в очередь блокировок.
## Contributing causes
1. Миграции деплоятся в той же транзакции, что и код (нет отдельного флага/этапа)
2. Алерт p95: for=10m — детект занял 18 минут от начала деградации
3. Нет canary-выкатки: релиз ушёл на 100% инстансов сразу
## Что сработало
- Алерт сработал до первых жалоб; rollback пайплайном отработал за 7 минут
- Единый канал #incidents дал таймлайн без реконструкции по памяти
## Corrective actions
| Действие | Тип | Владелец | Дедлайн |
|---|---|---|---|
| Миграции с DDL — отдельный этап деплоя, до кода | prevent | Анна | 2026-09-20 |
| Проверка в CI: долгие блокировки миграций на prod-анонимизированном дампе | prevent | Анна | 2026-09-27 |
| Выкатка: canary 5% → 30 мин наблюдения → 100% | prevent | Борис | 2026-10-04 |
| Алерт p95: for=10m → for=3m на burn rate ×6 | detect | Влад | 2026-09-15 |
| Runbook «p95 вырос после релиза» в docs/ | detect | Влад | 2026-09-12 |

Заметь разделение типов действий: prevent (не дать случиться), detect (узнать раньше), mitigate (сделать дешёвым). Здоровый postmortem содержит все три — чинится не только причина, но и процесс обнаружения.

Postmortem не живёт в вакууме — его влияние измеряется через error budget из главы про алертинг. Связка: сгорание бюджета порождает инцидент, инцидент — postmortem, postmortem — corrective actions, замедляющие трату бюджета. Два правила:

  1. Каждый SEV1/SEV2 обязан иметь postmortem. Считай количество разборов за квартал метрикой здоровья: много инцидентов с быстрыми review — нормально; много инцидентов без review — команда тонет.
  2. Corrective actions приоритизируются через стоимость бюджета. «Сгорели 8% месячного бюджета за 47 минут» — аргумент, чтобы поставить canary-выкатку выше новой фичи.

Обратная сторона: если инцидентов много, а бюджет почти не тратится — у тебя медленные недетектированные деградации. Худший случай: чини detection.

Ротацию из прошлой главы (первичный/вторичный, эскалация) доводим до готовности двумя практиками:

Handover. Каждая смена начинается с 10 минут передачи: какие инциденты/алерты открыты, какие флаги крутили, что «дымится». Без handover — два дежурных, ничего не знающих друг о друге.

Game day (учения). Раз в квартал ломай систему намеренно и проверяй процесс целиком: пришёл ли алерт, взял ли его дежурный за 5 минут, сработала ли эскалация, нашёлся ли runbook. Варианты поломок: убить контейнер, заполнить диск, «потерять» подсеть до базы, просрочить сертификат (на staging!). Метрики: time-to-detect/ack/mitigate — процесс деградирует незаметно, пока его не измеряют.

#!/usr/bin/env bash
# game-day.sh — учебный инцидент «приложение недоступно» (на staging!)
# Запуск из cron в нерабочее время учения, чтобы логика была максимально боевой.
TARGET="staging-app-01"
LOG=/var/log/game-day.log
echo "$(date -Is) GAME DAY: stopping app on $TARGET" >> "$LOG"
ssh deploy@"$TARGET" "docker stop pet-api"
# Автопроверка процесса: алерт InstanceDown должен прийти за <= 2 минут,
# дежурный должен подтвердить (ack) за <= 5 минут. Фиксируем факт в лог.
sleep 1800 # 30 минут на отработку инцидента командой
ssh deploy@"$TARGET" "docker start pet-api"
echo "$(date -Is) GAME DAY: restored" >> "$LOG"

Учения — по расписанию, с предупреждением руководства, но без предупреждения дежурного — иначе это театр, а не проверка ротации. После — мини-postmortem на 15 минут: что сработало, что нет, один action item.

  1. Нет откатного плана до релиза. Плохо: «откатим как-нибудь, если что». Хорошо: перед каждым деплоем в runbook релиза одна строка: «rollback: deploy.sh 2.13.2, миграция необратима? да/нет». Если откат невозможен — это известно ДО инцидента, и mitigation планируется заранее.
  2. Severity навсегда. Плохо: triage присвоил SEV3 и не пересматривает, хотя симптомы растут. Хорошо: severity — живой ярлык, IC пересматривает его каждые 15–30 минут; повышение до SEV1 объявляется явно, со сменой режима работы.
  3. IC, который чинит. Плохо: командующий ушёл в дебаг, решения не принимаются, роль фактически пустует. Хорошо: IC говорит больше, чем печатает; если начал «проверить один момент» — передаёт командование вслух другому.
  4. Молчание перед пользователями. Плохо: «пока не разберёмся — писать нечего», статус-страница пустует 2 часа. Хорошо: обновление по таймеру каждые 30 минут, включая «расследуем, нового нет»; молчание читается как «нам плевать».
  5. Postmortem без contributing causes. Плохо: «root cause: отсутствовал индекс. Закрыто». Через месяц тот же инцидент с другим запросом. Хорошо: минимум два-три contributing cause с действиями; «что сработало хорошо» — обязательный раздел, без него команда учится на негативе и скрывает инциденты.
  6. «Виноват человек» как вывод. Плохо: «Иван забыл индекс, проведём беседу». Иван уволится, система останется такой, что забытый индекс доезжает до прода. Хорошо: blameless-рамка — «почему review/CI/выкатка не поймали это до прода», действия против системы, а не человека.
  7. Учения ради галочки. Плохо: game day, о котором знает дежурный заранее — он всё сделает идеально, процесс не проверен. Хорошо: учение внезапно для дежурного (но анонсировано руководству), замеры time-to-detect/ack/mitigate, один реальный action item после.
  1. Чем инцидент отличается от алерта и от бага? Алерт — сигнал мониторинга, сам по себе не инцидент. Баг — дефект кода, инцидент — баг (или иная причина), повлиявший на пользователей в продакшене. Критерий инцидента — деградация сервиса для пользователей, а не страшность записи в логах.
  2. Как определяется severity инцидента? По влиянию на пользователей: SEV1 — недоступность/потеря данных для всех, SEV2 — существенная деградация для многих, SEV3 — незначительная с обходными путями, SEV4 — без влияния. Severity присваивается по симптому, динамически пересматривается по ходу triage и определяет скорость реакции.
  3. Зачем нужна роль Incident Commander, если все и так решают проблему? Без единой точки решений параллельные действия конфликтуют: один откатывает, другой катит фикс. IC держит картину, распределяет людей, разрешает споры и защищает ops от отвлечений. IC не чинит сам — командование и починка разделены намеренно.
  4. Чем mitigation отличается от resolution? Mitigation — снижение ущерба: откат, feature flag off, отключение интеграции. Resolution — полное восстановление до базовой линии. Цель mitigation — перестать терять бюджет и пользователей, а не найти идеальный фикс в разгар инцидента.
  5. Что такое blameless postmortem и почему он работает? Разбор без персональных претензий: фокус на системных причинах (почему процесс допустил ошибку до прода), а не на том, кто нажал кнопку. При жёсткой персональной ответственности люди скрывают инциденты, а скрытые инциденты не разбираются и повторяются. Blameless не отменяет обязательность corrective actions.
  6. Какие разделы должен содержать postmortem? Summary, severity и метрики влияния (сгоревший error budget), timeline, root cause и отдельно contributing causes, «что сработало хорошо», corrective actions с владельцами и дедлайнами. Типы действий: prevent, detect, mitigate.
  7. Зачем проводить game day, если инцидентов и так хватает? Реальные инциденты проверяют только то, что сломалось. Game day проверяет процесс целиком в контролируемых условиях: алерты, эскалацию, runbook’и, скорость реакции. Метрики учений показывают деградацию процесса до того, как она дорого обойдётся.
  8. Как error budget связан с инцидентами? SEV1/SEV2 — по определению быстрое сгорание бюджета. Отсюда правила: каждый SEV1/SEV2 требует postmortem, corrective actions приоритизируются через стоимость сгоревшего бюджета. Если инциденты есть, а бюджет не тратится — проблема в detection.

Мини-проект: полный цикл инцидент-менеджмента на вымышленном, но реалистичном кейсе «p95 вырос в 10 раз после релиза». Сохрани артефакты в репозитории pet-проекта: docs/incidents/2026-09-08-p95-regression/.

  1. Severity и роли (30 минут). Ситуация: релиз 2.14.0 в 14:12, в 14:21 p95 вырос с 120 мс до 1.4 с, часть запросов падает по таймауту. Присвой severity по матрице и обоснуй. Назначь роли из трёх человек (ты + два коллеги), обоснуй выбор IC.
  2. Таймлайн. Адаптируй заготовку из главы: detection, triage с решением «откат vs фикс вперёд» (обоснуй), mitigation, resolution, review. Запись: время, событие, источник.
  3. Коммуникации. Напиши три обновления статус-страницы: «investigating», «identified», «resolved». Отдельно — 8 строк условного чата #incidents: строка = значимое событие с timestamp.
  4. Postmortem. Заполни шаблон: правдоподобный root cause (с миграцией БД), минимум три contributing causes, раздел «что сработало хорошо». Влияние посчитай честно: 47 минут деградации, твой реальный RPS, месячный error budget — сколько процентов сгорело (формула — в главе про алертинг)?
  5. Corrective actions. Таблица минимум из пяти действий по типам prevent, detect, mitigate. Каждое — с владельцем, дедлайном и измеримым критерием. «Добавить мониторинг» не принимается; «алерт на lock waits > 50 за 2 минуты, for: 1m» — принимается.
  6. Учение. Сымитируй инцидент на pet-проекте (секундная задержка в hot-эндпоинте и выкат), зафиксируй фактами: time-to-detect, time-to-ack, time-to-mitigate. Целевые: ≤ 5 / ≤ 5 / ≤ 15 минут. Один action item, если не попал.

Критерии результата: в docs/incidents/ четыре файла (severity-матрица, таймлайн + обновления, postmortem, corrective actions); postmortem содержит все разделы; у каждого action item есть владелец и дедлайн; цифры влияния посчитаны из реальных метрик.