{"index":4,"slug":"editorial-2027-11-field-mistakes-revisions","title":"Incident runbook: как пройти от симптома к проверенному rollback","excerpt":"Практический маршрут инцидента: зафиксировать симптом и границу влияния, проверить одну гипотезу, выполнить обратимое действие и убедиться, что восстановилась система, а не только один график.","contentHtml":"

Проблема во время инцидента начинается с фразы «сервис упал после релиза». Она смешивает наблюдение, время и причинность. На практике симптом может выглядеть иначе: HTTP 503, рост p95 latency, очередь сообщений или ошибки только у одного метода и в одном регионе.

Цена такой неточности — лишние изменения и потеря следов причины. Оператор перезапускает всё подряд, меняет несколько параметров или откатывает код, не проверив совместимость со схемой базы. Ошибка исчезает на минуту, а команда не знает, что именно помогло и не осталось ли повреждённое состояние.

Тезис: runbook должен ограничивать неопределённость

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

Начинайте с полей, которые можно заполнить без интерпретации: timestamp, affected endpoint, регион, версия, доля ошибок, latency, saturation и baseline. Запись «всё медленно» не задаёт ни области поиска, ни критерия улучшения. Запись «POST /payments, EU, 5xx 8%, p95 1,8 с, baseline — предыдущее окно» уже позволяет выбрать следующий шаг.

Механизм: сигнал, гипотеза, действие, восстановление

Статус 500, задержка и error rate описывают разные стороны одного события. Они могут иметь общий корень, но не обязаны. Поэтому классификация сигнала должна быть детерминированной и прозрачной: высокий приоритет получает отказ или превышение error threshold, затем проверяется latency threshold. Классификатор помогает выбрать срочность, но не доказывает причину.

Гипотеза связывает наблюдение с проверяемым признаком: «после изменения лимита pool выросло ожидание соединения; проверка — метрика pool wait в том же временном окне». В формулировке должны быть условие и ожидаемый сигнал. Гипотеза «виноват backend» слишком широкая: её нельзя опровергнуть одним локальным измерением.

Первое изменение должно уменьшать blast radius. Подойдут отключение feature flag, остановка нового consumer или перевод небольшой доли трафика на старую версию — если такой путь предусмотрен архитектурой. Перезапуск без фиксации метрик может убрать симптом и одновременно удалить полезный контекст.

\"Петля
Каждое действие в runbook должно иметь ожидаемый эффект, условие возврата и отдельную проверку результата.

Минимальный рабочий пример

Ниже — небольшая чистая функция. Она принимает HTTP status, latency и error rate, применяет два явно заданных порога и возвращает классификацию. Числа в примере учебные: функция не знает бизнес-критичность endpoint, не отправляет уведомления и не выбирает rollback.

function classifySignal({ status, latencyMs, errorRate, latencyLimitMs = 1000, errorLimit = 0.05 }) {\n  if (!Number.isInteger(status) || !Number.isFinite(latencyMs) || !Number.isFinite(errorRate)) {\n    return { ok: false, reason: 'signal-invalid' };\n  }\n  if (status >= 500 || errorRate >= errorLimit) {\n    return { ok: true, severity: 'high', reason: 'availability-or-error-threshold' };\n  }\n  if (latencyMs >= latencyLimitMs) {\n    return { ok: true, severity: 'medium', reason: 'latency-threshold' };\n  }\n  return { ok: true, severity: 'low', reason: 'signal-below-threshold' };\n}\n\nconsole.log(classifySignal({ status: 503, latencyMs: 820, errorRate: 0.08 }));\n// { ok: true, severity: 'high', reason: 'availability-or-error-threshold' }

Проверяемость здесь важнее полноты. Для одинакового входа функция возвращает одинаковый результат; невалидный status или нечисловая метрика не превращаются в уверенный severity. Реальный runbook должен отдельно описать источник каждой метрики, допустимое окно и владельца действия.

Карта диагностики

Симптом → причина → проверка → действие
СимптомВозможная причинаПроверкаПервое действие
5xx растёт сразу после выкатаНовая версия или flag затронули часть трафикаСравнить версии, долю трафика и error rate по endpointОграничить flag или traffic split, затем повторить измерение
p95 растёт, 5xx не меняетсяОжидание соединения, CPU или внешнего ответаСопоставить latency breakdown, saturation и pool waitНе перезапускать всё; убрать один изменённый лимит, если он подтверждён
Очередь увеличивается после откатаConsumer остановлен или не справляется с потокомПроверить lag, rate обработки и ошибки consumerВернуть только совместимый consumer или остановить входной поток по policy
5xx снизился, операции не завершаютсяСервис доступен, но есть незавершённое состояниеСверить успешные операции, очередь, дубли и состояние данныхНе закрывать инцидент; запустить recovery-проверку и сохранить факты
Timeout при rollbackИзменение не применилось или control plane недоступенПроверить фактическую версию и audit log, а не только код командыОстановить новые изменения и выбрать заранее описанный ручной путь

В таблице указаны направления проверки, а не универсальные причины. Один симптом может иметь несколько объяснений. Оператор должен подтвердить выбранную ветку своим сигналом и записать результат рядом с timestamp.

Rollback не равен восстановлению

Rollback возвращает конфигурацию, feature flag или binary. Recovery означает, что система снова выполняет допустимую работу и данные остаются согласованными. Можно успешно вернуть старую версию и оставить очередь, двойные записи или несовместимые события. Поэтому после rollback проверяйте не только 5xx, но и lag, успешность операций, saturation и консистентность данных.

До отката проверьте совместимость. Старая версия должна читать текущую схему базы и понимать события, которые уже создала новая версия. Если перед инцидентом была миграция, порядок действий нельзя восстанавливать по памяти: в runbook нужны матрица совместимости, ожидаемый эффект, риск и способ вернуть сам rollback.

Критерий завершения должен быть измеримым. Формулировка «график выглядит лучше» не подходит. Нужны конкретные условия: error rate ниже согласованного порога, latency вернулась к допустимому диапазону, очередь не продолжает расти, а контрольная бизнес-операция проходит. Интервал наблюдения зависит от окна метрики и характера нагрузки; его задают заранее, а не в момент усталости.

Порядок действий

  1. Запишите timestamp, scope, версию и один измеримый симптом. Сохраните ссылку на dashboard и baseline.
  2. Проверьте границу влияния: регионы, методы, версии, долю трафика и затронутые зависимости.
  3. Сформулируйте одну гипотезу и одну проверку. Для проверки заранее назовите сигнал, который должен измениться.
  4. Выберите одно обратимое действие. Запишите ожидаемый эффект, риск и условие возврата до выполнения команды.
  5. После действия выдержите заданное окно и сравните error rate, latency, saturation, очередь и бизнес-сигнал.
  6. Если сигнал не улучшился, остановите ветку и зафиксируйте отрицательный результат. Не добавляйте второе изменение, пока не понятен эффект первого.
  7. После стабилизации сохраните timeline, фактическое состояние версии и оставшиеся риски. Не удаляйте временные изменения до проверки recovery.

Ограничения и критерий готовности

Этот маршрут не заменяет on-call график, права доступа, SLO, уведомления и локальную матрицу severity. Классификатор не видит traces, не отличает бизнес-критичный endpoint от второстепенного и не знает, можно ли безопасно повторить операцию. Порог 5% и latency 1000 мс в примере не являются рекомендацией для конкретной системы.

Runbook готов, если оператор может без устного контекста ответить на пять вопросов: какой факт зафиксировать, где проходит граница влияния, какую гипотезу проверить, какое действие обратимо и по какому сигналу считать recovery подтверждённым. Для каждого опасного шага должны существовать условие остановки и путь возврата.

Минимальная проверка инструкции — проиграть один сценарий на тестовом окружении с контролируемым 503 и пройти ветку до конца. Такой прогон проверяет структуру runbook, но не доказывает поведение production, отсутствие флаков или безопасность реального rollback. Фактические результаты конкретной системы нужно приложить отдельно.

Проверяемые источники

"}