Проверка отказом: учения, chaos-эксперименты, game day
В понедельник в 11:20 у платёжного сервиса умирает первичный узел базы. У команды есть горячая реплика, автоматическое переключение и документ, где написано «RTO 2 минуты». Через две минуты сервис всё ещё лежит. Через двадцать выясняется: реплика поднялась, но приложение продолжает ходить на старый адрес, потому что DNS-запись обновляется скриптом, у которого полгода назад отозвали права после наведения порядка в IAM. Скрипт падал молча — его выполнение никто не проверял, потому что оно случалось ноль раз.
Итог: 74 минуты простоя вместо заявленных двух. Резерв был. Документ был. Работающего переключения не было. Это не история про халатность. Это структурное свойство любого механизма, который срабатывает редко: пока вы не активировали его специально, у вас нет свойства «переживаем отказ X» — у вас есть гипотеза о том, что вы его переживаете. Разница между этими двумя вещами обнаруживается ровно в один момент — когда отказ случается сам, в неудобное время, без наблюдателя с секундомером и с настоящими пользователями в качестве испытательного стенда.
Эта глава — про то, как переводить гипотезы в свойства: что именно ломать, каким способом, с какой периодичностью, сколько это стоит и где инженер упирается в решение, которое принимает не он.
Три разные практики, которые постоянно путают
Под зонтиком «chaos engineering» в разговоре обычно смешивают три занятия с разными целями, разной ценой и разными участниками. Смешение вредно: команда говорит «мы не готовы к chaos в проде» и заодно отказывается от учений, которые стоят час и не трогают прод вообще.
| Учение (drill) | Chaos-эксперимент | Game day | |
|---|---|---|---|
| Что проверяет | людей и процедуру | свойство системы | и то и другое разом, вместе с координацией |
| Главный вопрос | «дежурный дойдёт до смягчения?» | «гипотеза о поведении верна?» | «мы как организация справимся?» |
| Где выполняется | стенд или ролевая игра | стенд, потом прод с узким радиусом | стенд + прод, объявленное окно |
| Длительность | 30–90 минут | 5–30 минут на прогон | полдня–день |
| Участники | 1–3 человека | 1–2 человека + автоматика | 5–15 человек, включая продукт |
| Стоимость прогона | часы | доли процента бюджета ошибок | человеко-дни |
| Что на выходе | находки по рунбукам, доступам, связи | подтверждённая или опровергнутая гипотеза | список правок с владельцами |
| Можно начать | сегодня | после того, как есть наблюдаемость | после двух-трёх успешных учений |
Порядок внедрения именно такой, слева направо. Chaos-эксперимент в системе, которую вы не видите в метриках, — это не эксперимент, а способ устроить аварию с непонятной причиной. Game day без предварительных учений — это восемь человек, которые полдня смотрят, как двое разбираются с тем, что можно было найти за час. И терминологическая честность: «game day» как формат придумал не Netflix. Джесси Роббинс завёл его в Amazon в середине 2000-х, взяв идею напрямую из подготовки пожарных: Resilience Engineering: Learning to Embrace Failure, ACM Queue, 2012. Chaos Monkey появился позже и решал другую задачу — не тренировку людей, а принуждение разработчиков писать код, переживающий пропажу инстанса.
Арифметика латентного дефекта
Возьмём ту же пару «первичный узел + горячий резерв». Пусть первичный узел отказывает два раза в год; без резерва восстановление занимает 40 минут (поднять из бэкапа, прогреть, вернуть трафик), резерв обещает 2 минуты. Но переключение либо срабатывает, либо нет: обозначим долю удачных переключений c. Если переключение не сработало, простой оказывается длиннее, чем без резерва вообще — дежурный сначала ждёт автоматику, потом выясняет, почему её нет, потом разбирается, в каком состоянии остался резерв, и только потом идёт по ручному пути. Реалистичная оценка — 90 минут.
Ожидаемый простой на один отказ:
E[T] = 2·c + 90·(1 − c) = 90 − 88·c
c = 0,95 → 90 − 83,6 = 6,4 мин
c = 0,80 → 90 − 70,4 = 19,6 мин
c = 0,60 → 90 − 52,8 = 37,2 мин
c = 0,30 → 90 − 26,4 = 63,6 мин
Точка безубыточности (когда резерв не даёт ничего):
90 − 88·c = 40 → c = 50/88 = 0,568
Резерв с вероятностью срабатывания ниже 0,57 хуже, чем его отсутствие. Вы платите за второй комплект железа, за репликацию, за поддержание симметрии сред — и получаете простой длиннее, чем если бы честно восстанавливались руками: при двух отказах в год это 127 минут против 80. Проблема в том, что c не наблюдаем. Компания, купившая резерв, автоматически считает c = 1 — в документе так и написано, «RTO 2 минуты». Единственное измерение c в природе — настоящая авария: одно наблюдение, в худший момент, с оплатой пользователями.
Уверенность деградирует, а не держится
Хуже: c не константа. Он падает со временем, потому что резервный путь не участвует в повседневной работе, а всё остальное вокруг меняется. Типичные механизмы деградации:
- конфигурация первичного узла ушла вперёд (лимиты, расширения, параметры пула);
- версия схемы данных обновилась только на первичном;
- права IAM, сертификаты, ключи — отозваны или протухли в резервной зоне;
- квоты в резервной зоне не поднимались вместе с ростом нагрузки;
- DNS TTL, который никто не проверял, оказался 3600;
- скрипт переключения написан под старую версию CLI облака.
Ни один из этих пунктов не заметен, пока переключение не понадобится. По опыту команд с активной разработкой разумная оценка — около 7 процентных пунктов за квартал.
Отсюда выводится расписание — не «раз в квартал, потому что так у всех», а из простого условия. Если вы хотите держать c не ниже 0,80 при деградации 7 п.п. за квартал, а после успешного учения c возвращается к ~0,97, то интервал между проверками — не больше двух кварталов, с запасом — один. Если ваш стек меняется быстрее (еженедельные изменения инфраструктуры, миграции схемы), интервал сокращается до месяца, и тогда ручное учение становится дорогим — приходится автоматизировать.
И обратный вывод, который редко произносят вслух: если вы не готовы регулярно проверять резерв, честнее его не покупать. Деньги, потраченные на второй комплект, лучше вложить в ускорение восстановления по основному пути — оно проверяется само собой, каждым инцидентом. Об этом же — разговор про избыточность и её цену в главе про ёмкость.
Сколько девяток вы вообще способны обслужить
Второй расчёт, без которого разговор про учения превращается в ритуал. Месяц — 43 200 минут.
99 % → 0,01 × 43 200 = 432 мин = 7 ч 12 мин
99,5 % → 0,005 × 43 200 = 216 мин = 3 ч 36 мин
99,9 % → 0,001 × 43 200 = 43,2 мин
99,95 % → 0,0005 × 43 200 = 21,6 мин
99,99 % → 0,0001 × 43 200 = 4,32 мин = 4 мин 19 с
99,999 % → 0,00001 × 43 200 = 0,432 мин = 25,9 с
Теперь честный хронометраж восстановления с участием человека — цифры взяты из главы про дежурство и главы про реакцию на инцидент:
обнаружение (алерт по скорости сгорания) 2 мин
доставка пейджера, подъём, ноутбук 4 мин
вход, открытие дашборда, ориентация 3 мин
диагностика по рунбуку 6 мин
действие и подтверждение эффекта 3 мин
итого 18 мин
Один такой инцидент в месяц: 18 / 43 200 = 0,0417 % → 99,958 %
Два инцидента в месяц: 36 / 43 200 = 0,0833 % → 99,917 %
Первый вывод. 99,99 % несовместимы с человеком в контуре в принципе: 4,32 минуты — это меньше, чем доставка пейджера плюс подъём. Четвёртая девятка означает, что смягчение выполняет код: переключение, размыкатель, автоматический откат, отбрасывание нагрузки. Это код, который в бою выполняется единицы раз в год. Такой код по умолчанию сломан — ровно по той же причине, что скрипт с отозванными правами из вступления. Единственный способ проверить его — вызвать отказ специально.
Второй вывод. Прежде чем ставить целью четвёртую девятку, стоит посчитать её цену. Разница между 99,9 % и 99,99 % — 38,88 минуты в месяц.
Что обычно надо докупить для веб-сервиса среднего размера:
второй регион в активном режиме +18 000 USD/мес
межрегиональная репликация и трафик +4 000 USD/мес
разработка автоматического переключения ~4 человеко-месяца
постоянное поддержание симметрии сред ~0,5 инженера
итого ~25 000 USD/мес + 0,5 FTE
Порог окупаемости: 25 000 / 38,88 = 643 USD за минуту простоя,
то есть около 38 600 USD в час.
Чтобы час полной остановки стоил 38 600 USD, годовая выручка
должна быть порядка 38 600 × 8 760 ≈ 338 млн USD.
Это грубая оценка, и у неё есть законные исключения: договорные штрафы в SLA, регуляторные требования, репутационные эффекты, неравномерность выручки по времени (для ритейла минута Чёрной пятницы стоит в тридцать раз дороже средней). Но порядок величины устойчив: если ваша выручка измеряется десятками миллионов, а не сотнями, четвёртая девятка обычно не окупается прямым счётом.
Про «пять девяток» — 25,9 секунды в месяц — говорить в терминах денег вообще бессмысленно. Один нештатный перезапуск пода с 30-секундным прогревом съедает месячный бюджет целиком. Плановая миграция с 20-секундным окном без обслуживания — 77 % бюджета. Пять девяток — это класс систем, где отказ измеряется не в деньгах: связь экстренных служб, ядро платёжной сети, медицинское оборудование. У них другая регуляторика, другая структура затрат и другая архитектура (см. главу про основы надёжности). И отдельно: «99,999 %» в SLA облачного провайдера — почти всегда обещание про узкий SLI (обычно про control plane или доступность API, а не про то, что ваши данные обслуживаются), подкреплённое компенсацией в размере скидки за месяц. Это не обещание доступности, а размер скидки; как выбирать SLI, чтобы такого не происходило у вас, — глава про SLI и SLO.
Chaos-эксперимент — это эксперимент, а не диверсия
Слово «хаос» в названии практики сыграло с ней злую шутку: оно наводит на мысль о случайности. На деле работающая практика устроена как обычный научный эксперимент, а не как рулетка. Principles of Chaos Engineering формулируют это в пяти пунктах; в переводе на язык повседневной работы:
- Стационарное состояние измеримо. Есть показатель, который в норме ведёт себя предсказуемо, и вы знаете его нормальный диапазон.
- Гипотеза записана до прогона. Не «посмотрим, что будет», а «показатель не выйдет за границу X».
- События реалистичны. Ломаем то, что ломается на самом деле, а не то, что легко сломать.
- Радиус ограничен и стоп-условие автоматическое.
- Прогон повторяем. Один раз — это анекдот; регулярный прогон — это регрессионный тест на устойчивость.
Шаблон, который стоит завести
Название: Отказ реплики для чтения в зоне B
Владелец: команда каталога
Стационарное: доля успешных запросов /catalog ≥ 99,9 % за 5 мин,
p99 latency ≤ 220 мс
Инъекция: отбрасывание всех TCP-соединений к реплике зоны B
Радиус: поды каталога в зоне B (7 из 21), это ~33 % трафика зоны,
~11 % общего
Длительность: 6 минут
Гипотеза: доля успешных не опустится ниже 99,5 %,
p99 не превысит 400 мс,
алерт на скорость сгорания НЕ сработает
Стоп-условие: доля успешных < 99 % в течение 30 с
ИЛИ p99 > 600 мс
ИЛИ любой пейджерный алерт
Откат: снять правило firewall, время отката < 10 с
Стоимость: максимум 0,8 % месячного бюджета ошибок
Окно: вторник, 11:00–12:00, вне периода заморозки
Отменяем если: идёт инцидент любого уровня, выкатывается релиз,
дежурный один
Три строки в этом шаблоне отличают эксперимент от аварии. Стоп-условие — потому что человек, увлечённый экспериментом, всегда даёт «ещё минутку». Откат с измеренным временем — потому что откат, который сам занимает пять минут, обесценивает узкий радиус. И «отменяем если» — потому что учение, совпавшее с настоящим инцидентом, отнимает у команды именно тот ресурс, который ей сейчас нужен. Отдельно стоит отметить строку гипотезы «алерт НЕ сработает»: это очень полезный класс гипотез, проверяющий не систему, а настройку алертов. Если отказ трети реплик в одной зоне будит человека — у вас либо слишком чувствительный порог, либо недостаточная избыточность, и эксперимент показывает, какое именно.
Радиус поражения считается в бюджете ошибок
Эксперименты тратят бюджет ошибок так же, как аварии, — и это правильно. Планировать их надо в тех же единицах, что и релизы.
Сервис: 1000 rps, SLO 99,9 % по доле успешных запросов.
Запросов в месяц: 1000 × 86 400 × 30 = 2 592 000 000
Бюджет ошибок (0,1 %): 2 592 000 запросов
Прогон длиной 10 минут: 1000 × 600 = 600 000 запросов всего.
Худший случай — всё в радиусе провалилось:
радиус 0,5 % → 3 000 плохих → 0,12 % бюджета
радиус 5 % → 30 000 → 1,16 %
радиус 33 % → 198 000 → 7,64 %
радиус 50 % → 300 000 → 11,57 %
радиус 100 % → 600 000 → 23,15 %
Практическое правило: один прогон — не дороже 1–2 % месячного бюджета. Тогда программа из четырёх прогонов в месяц стоит около 5 % бюджета: заметно, но приемлемо, особенно если она предотвращает хотя бы одну аварию из тех, что съедают 20–30 % за раз. Механика самого бюджета — в главе про бюджет ошибок. Ещё один вывод из той же арифметики: ущерб линеен и по радиусу, и по длительности. Стоп-условие, срабатывающее за 30 секунд вместо десяти минут, делит ущерб на 20 — это дешевле и полезнее, чем сужать радиус до неинформативного. Поэтому первое требование к программе экспериментов — не «умейте ломать», а «умейте увидеть за 30 секунд». Если ваш мониторинг агрегирует с шагом в минуту и отстаёт на две, вы физически не сможете остановиться вовремя; сначала — разрешение метрик (глава про мониторинг), потом эксперименты.
Лестница зрелости одного сценария
Ключевой переход — второй сверху: «не выдержала» отправляет сценарий не в прод, а в бэклог. Соблазн «прогнать в проде, чтобы убедиться, что там тоже плохо» надо гасить: вы уже знаете ответ, а платить за него будут пользователи. Верхняя ступень — автоматический прогон — достижима и полезна далеко не всем. У Netflix ChAP автоматически подбирает эксперименты и гоняет их непрерывно (Automating chaos experiments in production, ICSE-SEIP 2019), но это работает поверх тысяч однотипных stateless-инстансов и зрелой платформы выкатки. В кластере из шести подов автоматический случайный убийца — не практика, а рулетка. Об этом подробнее ниже.
Что ломать: сценарии берутся из документов, а не из фантазии
Самая частая ошибка старта — сесть и придумывать, что бы такое сломать. Придуманные сценарии смещены в сторону эффектных и редких; реальные аварии происходят от скучного. Правильный источник сценариев — документы, в которых уже записаны ваши обещания.
правки со статусом «выкачена»"] --> P B["Таблица критичности:
заявленные фолбэки"] --> P C["Рунбуки:
каждый пейджерный алерт"] --> P D["План ёмкости:
заявленный запас и автоскейлинг"] --> P E["Архитектурные допущения:
«переживём отказ зоны»"] --> P P["Очередь сценариев
приоритет = ущерб × неуверенность"] --> Q{"Знаем заранее,
что произойдёт?"} Q -->|"да, и это записано"| R["Проверить дёшево:
стенд, узкий радиус"] Q -->|"нет"| S["Это находка ещё до прогона.
Сначала записать ожидание,
потом проверять"] S --> R R --> T{"Совпало
с ожиданием?"} T -->|"да"| U["Повысить реалистичность:
прод, шире радиус, регулярность"] T -->|"нет"| V["Правка в бэклог с владельцем.
Повтор после выкатки"] V --> R
Ветка «нет, не знаем, что произойдёт» — самая ценная и самая недооценённая. Если команда не может заранее записать ожидаемое поведение при отказе компонента, находка уже есть, и она бесплатная: у системы нет описанного поведения в этом режиме. Прогон в этом случае нужен не чтобы узнать ответ, а чтобы проверить записанное ожидание. Особенно продуктивны два источника. Первый — правки из постмортемов: у каждой есть статус «выкачена», и почти ни у одной — «проверена отказом». Разница между этими двумя статусами и есть содержание вашего первого game day. Второй — таблица критичности зависимостей из главы про деградацию: каждый заявленный фолбэк — готовая гипотеза с известным ожидаемым результатом.
Каталог инъекций по слоям
| Слой | Что вводим | Чем вводим | Что обычно находится |
|---|---|---|---|
| Процесс | убить, заморозить (SIGSTOP), исчерпать файловые дескрипторы | kill, stress-ng, chaos-агент |
нет graceful shutdown; соединения не закрываются; readiness врёт |
| Узел/под | остановить, отобрать CPU, заполнить диск | Chaos Mesh, LitmusChaos, AWS FIS | PDB не настроен; всё реплики на одном узле; данные на локальном диске |
| Зона | отрезать сеть зоны, остановить все узлы | AWS FIS, правила безопасности | кворум теряется при отказе одной зоны из трёх; квоты в других зонах не подняты |
| Сеть | задержка, потери, дублирование, разрыв | tc netem, toxiproxy, Pumba |
таймауты «из головы»; ретраи без джиттера; отсутствие дедлайнов |
| DNS | NXDOMAIN, задержка резолва, залипший TTL | подмена резолвера, toxiproxy | закешированный адрес переживает переключение; резолв в горячем пути |
| Зависимость | 500-е, медленные ответы, частичные ответы | прокси-инъектор, feature flag | фолбэк не включается; фолбэк медленнее основного пути |
| Хранилище | отставание реплики, отказ первичного, read-only | средства СУБД, firewall | приложение не различает отставание и отказ; переключение не тестировано |
| Ресурсы | исчерпать пул соединений, очередь, память | нагрузочный генератор | насыщение вместо деградации; нет отбрасывания нагрузки |
| Время | сдвиг часов, истёкший сертификат | подмена NTP, срок в тестовом CA | всё построено на синхронности часов; нет алерта на срок сертификата |
| Люди | ключевой инженер недоступен, нет доступа | правило «этот человек молчит» | знание в одной голове; доступ только у одного; рунбука нет |
Две строки заслуживают отдельного упоминания. Истёкший сертификат — самая частая и самая дешёвая в проверке причина аварий, которую почти никто не проверяет специально: прогон занимает полчаса в стенде (выпустить сертификат с истекающим сроком в тестовом CA), а находит целый класс — отсутствие алерта за 30 дней, ручное обновление, клиенты с закешированной цепочкой, mTLS между сервисами, о котором забыли (глава про транспортную безопасность). И человеческие инъекции — самый дешёвый и самый недооценённый слой: «сегодня Пётр не отвечает» не требует никакой инструментовки и находит ровно то, что убивает команды, — единственный носитель знания, единственный владелец доступа, рунбук, понятный только автору.
Учение: проверяются люди, а не только система
Chaos-эксперимент отвечает на вопрос «что делает система». Учение отвечает на вопрос «что делает человек», а это чаще оказывается узким местом: система-то, может, и переживёт, а вот дежурный полчаса ищет дашборд.
(дежурному не показывают) F->>S: инъекция: 60 % ошибок от сервиса цен S-->>D: алерт: скорость сгорания 14x на checkout Note over N: t=0 — пошёл отсчёт D->>S: открывает дашборд, ищет рунбук Note over N: t=4 мин — ссылка в алерте вела
на удалённую страницу (находка 1) D->>F: «нужен доступ к консоли фича-флагов» F-->>D: подсказка выдаётся по запросу
и записывается как находка 2 D->>S: выключает блок рекомендаций флагом Note over N: t=11 мин — SLI вернулся в норму F->>S: снять инъекцию F->>N: сверка: что произошло против ожидания N->>F: таймлайн, три находки, владельцы, сроки
Три детали устройства, без которых учение не работает.
Подсказки выдаются, но записываются как находки. Ведущий не молчит и не мучает участника: если человек застрял на четыре минуты, подсказка даётся. Но каждая подсказка — находка: она означает, что информация, которая должна была быть в рунбуке или в алерте, находится в голове ведущего.
Наблюдатель отдельно от ведущего. Ведущий занят инъекцией и подсказками, он не успевает вести таймлайн. Без таймлайна с отметками времени учение превращается в «ну вроде нормально прошло».
Сравнение с ожиданием, а не оценка участника. Формулировка разбора: «ожидали, что рунбук будет найден за минуту, — заняло четыре, потому что ссылка мертва», а не «Пётр долго искал». Это ровно тот же механизм, что и в постмортемах без обвинения, и он ломается ровно так же: учение, за плохой результат которого прилетает, мгновенно превращается в спектакль — участники готовятся заранее, ведущий выбирает лёгкие сценарии, и данные перестают поступать. Разбор механизма — в главе про постмортемы.
Самая дешёвая форма: разбор сценария без инъекции
Ролевая игра, известная как Wheel of Misfortune (описана в SRE-книге, гл. 28): ведущий берёт настоящий прошлый инцидент, участник «дежурит», ведущий отыгрывает систему — показывает графики, отвечает на команды словами. Никакой инструментовки, никакого стенда, час времени.
Отдача на потраченный час у этого формата выше, чем у чего угодно другого в главе, и он же — то, с чего стоит начинать команде любого размера. Что находится за первые три сессии:
- рунбуки, написанные автором для себя (никто другой по ним не проходит);
- алерты без ссылок или со ссылками на переехавшие страницы;
- дашборды, которые знает один человек, и отсутствие доступа к консоли у половины дежурных;
- расхождение в понимании, кто имеет право откатывать релиз без согласования.
Ни одна из этих находок не требует ломать прод — и каждая стоит десятков минут в реальной аварии.
Правила безопасности учения
- Настоящий инцидент побеждает учение. Всегда, немедленно, без обсуждения.
- Стоп-слово известно всем и означает мгновенную остановку без объяснений. Тот, кто его сказал, никогда не оказывается неправ.
- Учение помечено в календаре и в чате. Смежные команды должны знать, иначе вы получите второй инцидент — у соседей.
- Учения — в рабочее время. Ночное учение «чтобы по-настоящему» выжигает людей быстрее реальных аварий и почти ничего не добавляет: работоспособность ночью проверяется другими средствами — контрольной доставкой пейджера и разбором ночных инцидентов. Цена ночных подъёмов посчитана в главе про дежурство.
- Неанонсированные учения — только после того, как анонсированные проходят чисто, и только с ведома руководителя дежурства. Сюрприз в качестве стартовой практики — не эксперимент, а способ испортить отношения и потерять доверие к программе.
Game day: как выглядит день
Game day — это когда вы собираете людей, объявляете окно и прогоняете несколько сценариев подряд, включая один в проде. Смысл не в самих сценариях (их можно было прогнать по отдельности), а в проверке координации: связь, роли, эскалация, принятие решений под давлением, взаимодействие с продуктом.
- Разбор идёт сразу после сценария, а не в конце дня. К 16:00 половина деталей забыта. Тридцать минут после каждого прогона дают в разы больше материала.
- Прод — один сценарий и ближе к концу. К этому моменту команда разогрета, наблюдаемость проверена, а если что-то пойдёт не так, впереди ещё полтора рабочих часа и полный состав людей, а не вечер пятницы.
- День заканчивается отчётом, а не намерением его написать. Отчёт, написанный на следующей неделе, не пишется никогда.
Роли на день — те же, что в реакции на инцидент, плюс две специфические:
| Роль | Кто | Задача |
|---|---|---|
| Ведущий | обычно тот, кто готовил сценарии | инъекции, подсказки, тайминг, стоп |
| Наблюдатель | человек вне сценария | таймлайн с отметками времени, находки дословно |
| Командир | по обычному расписанию, не ведущий | координация участников, как в настоящем инциденте |
| Участники | дежурные и разработчики | работают так, как работали бы в бою |
| «Безопасник дня» | старший инженер | право остановить всё, следит за стоп-условиями |
| Представитель продукта | опционально, но полезно | видит цену деградации своими глазами |
Последняя роль — самый недооценённый пункт: продукт-менеджер, который час наблюдал, как выглядит отказ рекомендаций для пользователя, потом совершенно иначе участвует в разговоре про приоритет правок и про SLO. Подготовка первого game day — примерно 2 человеко-недели: сценарии, стенд, инструментовка, согласование окна. Последующих — 2–3 человеко-дня, если сценарии переиспользуются.
Каскад — это усиливающая петля, и это меняет то, что вы измеряете
Полезное переопределение: каскадный отказ и усиливающая петля обратной связи — одно и то же явление, описанное на двух языках. Разбор петель и их динамики — в главе про петли обратной связи, а задержки, из-за которых петли ведут себя контринтуитивно, — в главе про задержки. Механику именно каскадов в распределённых системах разбирает глава про режимы отказа.
+150 мс к сервису цен"] --> Q["Очередь на шлюзе растёт"] Q --> T["Клиентские таймауты"] T --> R["Повторы: нагрузка ×3"] R --> Q Q --> M["Измерять надо ЗДЕСЬ:
SLI на границе с пользователем"] R --> W["А смотрят обычно СЮДА:
«сервис цен же отвечает»"]
- Инъекция в одном месте, измерение — в другом. Интересна не реакция сломанного компонента, а передача возмущения. Если вы ломаете сервис цен и смотрите на метрики сервиса цен, вы измеряете инъекцию, а не систему.
- Малая инъекция может дать большой эффект — это и есть находка. Плюс 150 мс к одной зависимости не должны ронять продукт. Если роняют — вы нашли петлю усиления, а не «слишком сильную инъекцию».
- Эффект приходит с задержкой. Очередь растёт минуты, ретраи разгоняются десятками секунд, автоскейлер реагирует ещё позже. Шестиминутный прогон с наблюдением ровно шесть минут пропускает самое интересное: наблюдение должно продолжаться минимум вдвое дольше инъекции.
- Система может не вернуться после снятия инъекции. Это метастабильный отказ: причина устранена, а система остаётся в плохом состоянии, потому что петля питает сама себя. Именно поэтому в плане обязателен не только откат инъекции, но и процедура вывода системы из перегрузки — сброс очередей, временное отбрасывание нагрузки, поэтапный возврат трафика. Это же — важнейший аргумент в пользу узкого радиуса: эксперимент, который нельзя отменить простым снятием инъекции, обязан начинаться с долей процента трафика.
Цена
Время команды
Команда 8 инженеров, годовая ёмкость ≈ 8 × 1 800 = 14 400 ч
Программа:
Wheel of Misfortune, 1 ч/мес на всю команду 8 × 12 = 96 ч/год
Учение с инъекцией в стенде, 3 ч × 3 чел./квартал 36 ч/год
Game day 2 раза в год: 6 чел. × 8 ч + 16 ч подготовки 128 ч/год
итого 260 ч/год
Доля годовой ёмкости: 260 / 14 400 = 1,8 %
Для сравнения: один средний инцидент — 90 минут по пять человек в остром режиме, 4 часа на постмортем, 2–3 дня на правки — обходится примерно в 40 человеко-часов. Годовая программа учений стоит как шесть-семь инцидентов. И тут надо быть честным: строго доказать окупаемость программы нельзя. Предотвращённые аварии не наблюдаются. Всё, что можно предъявить, — находки: если каждый прогон даёт три-пять пунктов класса «в реальном инциденте это стоило бы получаса», арифметика сходится по порядку величины. Обещать больше — значит продавать веру.
Инструменты
| Инструмент | Что даёт | Что стоит |
|---|---|---|
| toxiproxy | задержки, разрывы, потери на уровне TCP-прокси | почти ничего; ставится рядом, без прав в кластере |
tc netem, Pumba |
сетевые условия на узле/контейнере | нужны привилегии на узле |
| Chaos Mesh, LitmusChaos | инъекции в Kubernetes: поды, сеть, IO, время | ещё один контроллер с широкими правами в кластере |
| AWS FIS, Azure Chaos Studio | отказы на уровне облака, включая зоны | привязка к провайдеру, поминутная оплата |
| Chaos Toolkit | описание экспериментов в JSON/YAML, драйверы | нужен свой каркас вокруг |
| Собственный фича-флаг «включить отказ» | самый точный радиус, минимум прав | код в проде, который умеет ломать прод |
Скрытая цена инструментов посерьёзнее лицензий: chaos-агент с правами на убийство подов — это ещё один демон в проде, который может стать причиной аварии сам. Известный класс инцидентов — агент, оставшийся включённым после эксперимента, или агент, у которого сбился селектор. Отсюда практическое требование: рубильник должен находиться вне того, что вы ломаете. Если кнопка отмены живёт в том же кластере и вы уронили его control plane, отменять нечем. Про устройство самого кластера — глава про Kubernetes. И отдельная ветка, о которой стоит знать: для распределённых алгоритмов и хранилищ инъекция в живой системе — не самый эффективный способ. Детерминированное симуляционное тестирование (подход FoundationDB, описание) и проверка заявленных гарантий через Jepsen находят классы дефектов, которые chaos-эксперимент не наберёт статистикой никогда. Подробнее — в главе про тестирование распределённых систем.
Бюджет ошибок и люди
Бюджет. Эксперименты тратят его наравне с авариями и релизами. Если бюджет уже сожжён, эксперименты в проде останавливаются вместе с релизами — по тому же правилу. Это не бюрократия: сжигать остаток на добровольную поломку, когда пользователи и так пострадали, нельзя. Люди. Учения — это когнитивная нагрузка сверх обычной работы, и она ложится на тех же людей, что и дежурство. Отсюда правила: не совмещать учение с дежурной сменой того же человека; не проводить в неделю релиза крупной функциональности; не ставить два дня подряд. Программа, которая добавляет 1,8 % ёмкости в календарь и 15 % в утомляемость, работать не будет.
Отдельно про алерты. Учение — единственный дешёвый способ проверить тезис из главы про алерты: алерт без действия обесценивает все алерты. В прогоне это видно моментально — если во время инъекции пришло семь уведомлений, из которых шесть не повлияли ни на одно решение дежурного, у вас есть список из шести кандидатов на удаление, подтверждённый наблюдением, а не мнением.
Где инженерное решение упирается в организационное
Прямым текстом: четыре вещи из этой главы инженер не может решить сам. Взгляд с другой стороны стола — в главе про дежурства и инциденты трека инженерного лидерства.
- Разрешение ломать прод. Это решение руководителя, и оформлять его надо как политику («эксперименты с радиусом до 5 % и автоматическим стоп-условием не требуют отдельного согласования»), а не выпрашивать каждый раз. Согласование на каждый прогон убивает регулярность, а без регулярности программа бессмысленна.
- Время в календаре. 260 часов в год не появляются из энтузиазма. Если они не выделены явно, учения проводятся первые два месяца, а потом отменяются в пользу срочного.
- Судьба находок. Game day, после которого находки не попали в бэклог с владельцем и сроком, — это два потраченных дня и минус доверие. Второй такой проводить не надо, пока не решён вопрос с приоритизацией.
- Компенсация за неурочное время. Если учение всё же приходится ставить в нерабочее время, это оплачиваемое время, а не проявление командного духа.
Что переносится из Netflix и Google, а что нет
Chaos Monkey работает у Netflix, потому что у Netflix тысячи взаимозаменяемых stateless-инстансов, автоматическая замена за минуты, трёхкратный запас ёмкости и платформа выкатки, умеющая откатывать сама (FIT: Failure Injection Testing). Ни одно из этих условий не выполняется в команде из восьми человек с шестью подами, один из которых — единственная реплика Redis. Google DiRT (Weathering the Unexpected, ACM Queue, 2012) — учения масштаба компании, с выделенной командой организаторов и правом останавливать целые датацентры. Полезно читать; воспроизводить целиком бессмысленно.
| Переносится почти без изменений | Не переносится |
|---|---|
| Гипотеза, записанная до прогона | Непрерывный автоматический прогон в проде |
| Стоп-условие и измеренное время отката | Случайное убийство инстансов |
| Радиус, посчитанный в бюджете ошибок | Выделенная команда chaos-инженеров |
| Wheel of Misfortune раз в месяц | Учения масштаба компании с остановкой ДЦ |
| Game day дважды в год | Собственная платформа автоматизации экспериментов |
| Сценарии из постмортемов и таблицы критичности | Расчёт на статистику по редким отказам |
| Проверка резерва раз в квартал | «Мы ломаем всё и всегда» как культурная норма |
Последняя строка левой колонки заслуживает пояснения. При трафике в тысячи запросов в секунду редкие классы отказов у вас просто не наберут статистики: отказ зоны случается раз в несколько лет, и вы никогда не увидите его в метриках достаточно раз, чтобы «выучить» поведение. Значит, единственный способ узнать поведение — вызвать его самому. Парадоксально, но чем меньше ваш масштаб, тем важнее эксперимент: у Google отказы всех классов происходят ежедневно сами по себе, у вас — нет.
С чего начинать команде из шести-десяти инженеров, в порядке отдачи на вложенный час:
- Wheel of Misfortune, час в месяц. Ноль инструментов. Первые три сессии дадут больше находок, чем следующий год чего угодно.
- Проверка одного резервного механизма в квартал. Того, на который вы реально рассчитываете: переключение БД, восстановление из бэкапа, второй канал доставки пейджера.
- Инъекция отказов зависимостей в стенде. Проверка заявленных фолбэков по таблице критичности. Toxiproxy или фича-флаг — этого достаточно.
- Восстановление из бэкапа целиком, с замером времени. Отдельный пункт, потому что «бэкапы делаются» и «из бэкапа можно восстановиться» — разные утверждения, и второе проверяли единицы.
- Первый прод-эксперимент с радиусом 0,5 % — только после того, как пункты 1–4 идут гладко.
- Game day — когда набралось хотя бы шесть-восемь сценариев и есть, что координировать.
Пункты 1–4 не требуют разрешения ломать прод, не тратят бюджет ошибок и закрывают, по опыту, большую часть разрыва между «написано» и «работает».
Как понять, что программа работает
| Показатель | Хороший знак | Ловушка |
|---|---|---|
| Находок на прогон | 3–5 в начале, снижение со временем | ноль находок значит либо зрелость, либо повтор одного и того же сценария |
| Доля правок из постмортемов, проверенных отказом | растёт к 60–80 % | статус «проверено» без прогона |
| Доля пейджерных алертов с рунбуком, пройденным незнакомым человеком | растёт | «прошёл» ставит автор рунбука |
| Время до смягчения в учении | сокращается | тренируется один и тот же сценарий |
| Расхождение между учением и реальным инцидентом | сокращается | учения подгоняются под удобные условия |
| Число прогонов в квартал | стабильно | превращение в KPI даёт формальные прогоны |
Последняя строка — главная. «Провести N учений за квартал» как цель гарантированно даёт N дешёвых, безопасных и бесполезных прогонов. Осмысленная формулировка цели — не количество прогонов, а доля заявленных свойств системы, проверенных отказом за последние два квартала. Считать её несложно: список берётся из таблицы критичности, плана ёмкости и правок постмортемов; в каждой строке — дата последней проверки. Пустая клетка — честное признание, что в этом месте вы не знаете, что произойдёт.
Типичные ошибки
- Начинать с прода. Первый прогон в проде до того, как сценарий отработан в стенде, — это не смелость, а незнание собственной наблюдаемости.
- Эксперимент без записанной заранее гипотезы. Без неё любой результат объявляется ожидаемым, и прогон не даёт информации.
- Стоп-условие «на глазок». Человек, ведущий эксперимент, всегда хочет «ещё минутку». Условие должно срабатывать автоматически.
- Рубильник внутри радиуса поражения. Кнопка отмены в том же кластере, консоль за тем же балансировщиком, флаг в той же базе.
- Инъекция и измерение в одной точке. Проверяется передача возмущения, а не реакция сломанного компонента.
- Наблюдение ровно на длину инъекции и без плана вывода из перегрузки. Самое интересное — очереди, ретраи, автоскейлер, невозврат — происходит после снятия, а метастабильный отказ переживает устранение причины.
- Драматизм вместо информации. Учение ночью «чтобы по-настоящему» и неанонсированный сюрприз как стартовая практика: дорого в людях, мало данных, минус доверие и второй инцидент у соседей.
- Разбор в конце дня. Детали забываются за час; разбор нужен сразу после каждого сценария.
- Находки без владельца, срока и выделенного времени на программу. Список наблюдений, который никуда не ведёт, отменяет смысл мероприятия; программа на энтузиазме живёт два месяца.
- Оценка людей вместо сравнения с ожиданием. Мгновенно превращает учение в спектакль — механизм тот же, что при поиске виноватого в постмортеме.
- Случайное убийство инстансов на маленьком масштабе. Без взаимозаменяемости и запаса ёмкости это лотерея, а не практика.
- Chaos-агент оставили включённым. Классика: эксперимент закончился, правило firewall осталось, авария случилась через два дня.
Мини-итог
- Механизм, срабатывающий редко, по умолчанию сломан. Резерв, фолбэк, скрипт отката и рунбук — гипотезы, пока их не активировали специально.
- Резерв с вероятностью успешного переключения ниже 0,57 хуже, чем его отсутствие:
E[T] = 90 − 88·cпротив 40 минут ручного восстановления. Величинаcне наблюдаема без учений и падает примерно на 7 п.п. за квартал — отсюда и берётся периодичность проверок. - Арифметика девяток задаёт границу применимости человека: 18 минут на инцидент дают потолок около 99,96 % при одном инциденте в месяц. 99,99 % (4,32 мин/мес) означает автоматическое смягчение, а автоматическое смягчение проверяется только вызванным отказом.
- Четвёртая девятка окупается прямым счётом примерно от 340 млн USD годовой выручки: 25 000 USD/мес дополнительных затрат против 38,88 сэкономленной минуты. Пять девяток — 25,9 секунды в месяц — исключают даже штатный перезапуск с прогревом.
- Ущерб эксперимента линеен по радиусу и по длительности. При 1000 rps и SLO 99,9 % десятиминутный прогон на 5 % трафика стоит 1,16 % месячного бюджета, на всём трафике — 23,1 %; рабочая доза — 1–2 % за прогон. Стоп-условие за 30 секунд вместо 10 минут делит ущерб на 20, поэтому сначала разрешение метрик, потом эксперименты.
- Сценарии берутся не из фантазии, а из документов: правки постмортемов со статусом «выкачена», заявленные фолбэки, рунбуки, план ёмкости, архитектурные допущения. Невозможность записать ожидание заранее — уже находка.
- Учение проверяет людей: рунбуки, доступы, связь, эскалацию. Wheel of Misfortune — час в месяц, ноль инструментов, максимальная отдача. Подсказки выдаются, но записываются как находки.
- Каскад и усиливающая петля — одно явление: ломать в одном месте, измерять на границе с пользователем, наблюдать вдвое дольше инъекции и иметь план вывода из перегрузки, а не только отката инъекции.
- Программа стоит порядка 1,8 % годовой ёмкости команды; доказать её окупаемость строго нельзя, предъявлять можно только находки. Четыре вещи решает не инженер: разрешение ломать прод (как политика, а не разовое согласование), время в календаре, приоритет находок и оплата неурочного времени.
- Из Netflix и Google переносятся гипотеза, радиус, стоп-условие и регулярность. Не переносятся непрерывный автоматический прогон, случайное убийство инстансов и расчёт на статистику по редким отказам — последнее как раз потому, что при малом масштабе редкие отказы сами по себе не происходят достаточно часто, чтобы чему-то научить.
Источники
- Principles of Chaos Engineering — короткий канонический текст, пять принципов.
- Casey Rosenthal, Nora Jones. Chaos Engineering: System Resiliency in Practice, O’Reilly, 2020 — самая полная книга по теме, с главами от практиков из разных компаний.
- Jesse Robbins, Kripa Krishnan, John Allspaw, Tom Limoncelli. Resilience Engineering: Learning to Embrace Failure, ACM Queue, 2012 — происхождение game day; там же рядом Weathering the Unexpected про устройство DiRT в Google.
- John Allspaw. Fault Injection in Production, ACM Queue, 2012 — почему инъекция в проде отличается от тестирования.
- Google. SRE book, ch. 17: Testing for Reliability — место инъекции среди прочих видов тестирования; ch. 28 — описание Wheel of Misfortune.
- Netflix. FIT: Failure Injection Testing — инъекция на уровне запроса; Ali Basiri et al. Automating chaos experiments in production, ICSE-SEIP 2019 — устройство ChAP.
- AWS Well-Architected. Testing reliability — чек-лист требований к программе проверок.
- Marc Brooker. Static stability using Availability Zones, AWS Builders’ Library — почему статическая стабильность надёжнее реакции на отказ.
- Инструменты: Chaos Mesh, LitmusChaos, AWS FIS, Azure Chaos Studio, Chaos Toolkit, toxiproxy, Pumba.
- Jepsen — проверка заявленных гарантий хранилищ; FoundationDB simulation testing — детерминированная альтернатива инъекции в живой системе.
- Nathan Bronson et al. Metastable Failures in Distributed Systems, HotOS 2021 — почему снятия инъекции недостаточно.
Что дальше
Учения проверяют то, что уже выкачено. Но самый частый источник аварий — не редкий отказ железа, а обычный релиз: по разным оценкам, от половины до двух третей инцидентов начинаются с изменения, которое кто-то только что выкатил. Хорошая новость в том, что этот класс отказов управляем лучше любого другого: изменение можно вводить постепенно, наблюдать за ним и убирать за секунды. Дальше — про флаги, канареечные выкатки, автоматические критерии остановки и про то, почему откат должен быть дешевле починки.