Как мы теперь реагируем на аварии на проде (и пара примеров)
Вот так может выглядеть инцидент. Это пример отлетевшего сертификата безопасности.
16:45: выкатываем изменение на один из проектов, добавился новый компонент. Автотесты видят нормальные 200-е ответы страниц, компонент проверяется вручную на страницах сайта.
17:41: QA сообщают, что часть автотестов главной страницы не отработана. На главной какой-то другой контент, а не главная.
17:42: аварийный слак-бот Валентин маршрутизирует инцидент, определяет команду, которая релизила новое обновление, создаёт конференц-кол и вызванивает каждого.
17:47: команда принимает решение откатывать релиз, на главной показывается одна из внутренних страниц.
18:16: у команды недостаточно прав на запуск отката, призывается команда С0.
18:22: запуск отката.
18:35: успешный откат.
Постмортем: с 16:45 до 18:35 пользователи видели не главную страницу, а одну из внутренних. Визуально разница между ними не очень большая, но на новой главной можно было только записаться на первый бесплатный урок, никакого дополнительного контента нет. Статистическая разница в динамике заявок говорит, что потери небольшие, примерно 20 тысяч рублей. Корневая причина: через ревью и первичное тестирование прошло изменение, которое заменяет главную:
gitlab.skyeng.link/skyeng/skyeng-frontend/-/merge_requests/21447/diffs#da99cd2d0926554dc5e7fac345fbdf2ad7923c04_0_10
imports: [CommonModule, NaturalFormModule, FreeLessonModule]
В модуле был импортирован модуль FreeLessonModule. А внутри FreeLessonModule прописываются роуты:
RouterModule.forChild ([{ path: '', component: FreeLessonComponent, pathMatch: 'full' }])
Прошёл импорт модуля вне описания корневых роутов, что привело к дописыванию нового правила роута, которое подменило главную. Избежать этого можно, не импортируя модули из shared-папки. Рассказали на ретро командам, как и что делать.
Что такое инцидент?
Это какое-то событие, которое означает для нас крупные денежные или репутационные потери. Например, если недоступен личный кабинет одного ученика — это баг. Если же он недоступен для всех учеников или нескольких их тысяч — это точно инцидент-блокер. То есть нужна массовость проблемы или прерывание основного бизнес-процесса. В этой ситуации к решению проблемы подключается команда, которая бросает все остальные дела и начинает заниматься только решением инцидента.
Пример про сертификаты
Вот что видел пользователь:
30 сентября 2021 г. у IdenTrust DST Root CA X3 закончился срок действия. Это случилось в 17:01 по Москве. В 17:11 мы открыли инцидент из-за полученных жалоб от пользователей системы и аллертов. Валидность сертификата сервера проверяется на клиентской стороне, мы не могли это исправить на своей стороне. Мы про это знали, но всё же выстрелили в ногу, вот пост про это.
Пользователи Windows 7/8/8.1, некоторых дистрибутивов Linux, Android ниже 7.1.1 и iOS ниже 10 не могут открыть SSL-соединения, поскольку используют сертификаты, восходящие по цепочке подтверждения к истекшему DST Root CA X3. По факту пострадало около 7% наших пользователей.
Одним из временных экстренных решений было предложить пользователям использовать Firefox, потому что он тащит за собой собственный набор сертификатов, и в свежих версиях браузера свежие сертификаты. Частично это помогло, но не всем. Кроме того, лиса спрашивает, открывать ли такую страницу, а не блокирует по умолчанию, что даёт возможность работать с просроченными сертификатами.
В конце мы сделали инструкцию для пользователей, как поставить новый корневой сертификат. Не сказать, что она была проста и понятна всем, но ещё часть проблем решила.
После этого инцидента мы узнали много нового про выстрелы себе в ногу, разобрали на ретро всё происходящее, внедрили процедуру обновления сертификатов, подключили платные сертификаты.
Как мы обычно узнаём про инцидент
Как правило, кто-то или что-то замечает, что ОНО НЕ РАБОТАЕТ. Это может быть тестировщик из QA, автотест, система мониторинга, клиенты (то есть поддержка), иногда — разработчики сами, сразу после релиза.
Из любого места слака у нас можно позвать бота Валентина, который нужен для того, чтобы запустить процесс реакции. Валентину нужно сказать, что случилось и какая у этого серьёзность, после чего он с нужным уровнем усердия подойдёт к решению задачи:
Дальше этот инцидент прилетает на отдельный disaster-канал, куда подключены все звонилки к дежурным опсам и инженерам команд ядра. В этот канал можно просто написать об инциденте, тогда Валентин спросит в канале «создать ли инцидент?».
В любом случае бот попросит назвать приоритет. Дальше сразу же бот создаст тикет в Джире и положит его в нерешённые инциденты. Также бот Валентин создаёт чат в Google Meets, где можно общаться голосом. В тред к сообщению об инциденте приходит дежурный опс (обычно это происходит на первой-второй минуте после регистрации) и определяет, может ли починить сразу одним движением. Если нет — понимает, по какому проекту это случилось, и сразу же тегает команду, которая может это решить. Если это общая проблема инфраструктуры — поднимает опсов, если свежий деплой — команду, которая это сделала, а если непонятно — находит сервис с проблемой и зовёт его команду.
После исправления проблемы дежурный отмечает пост смайликом , бот триггерится на это и переводит задачу по инциденту в Джира в статус Устранённые инциденты.
В чём же была боль
Всего пару лет назад мы решали инциденты куда медленнее и тонули в правках после них. Не на все случаи делался постмортем-тикет, а если делался, то изменения в процессах клались далеко в беклог, после чего почти никогда не исполнялись.
Вот, например, что-то падает в видеосвязи, команда прибегает в чат, поднимает упавшую ноду, затем уже в спокойной обстановке определяет корни проблемы, создаёт дальше группу идей и задач.
А дальше ничего не происходит!
Команды кладут эти задачи в беклоги со статусом minor (потому что текущие фичи надо пилить всё время), а потом через год при очистке беклога от minor’ов просто выкидывают. По факту весь процесс инцидент-менеджмента буксовал. Был фокус на починку, но не было фокуса на недопущение новых инцидентов. А инциденты редко повторяются один в один. Хотя у нас и такое было — повторы между ретро и выкаткой правок на прод через несколько месяцев.
Как устроено сейчас
Мы пришли к достаточно жёсткому SLA, заставляющему что-то делать сразу после аварии. Для каждого инцидента мы делаем оценку урона и обязательно записываем в базу знаний анализ произошедшего. Иногда это инциденты «просто нужно про такое знать», и тогда мы рассказываем про это в командах, а иногда это требует изменения каких-то процессов. Меняем их.
Предполагается, что среднее время исправления инцидента (defect escape rate) не должно превышать 30 минут. У нас сейчас среднее за 90 дней превышает (2 часа), а медианное время ниже. Это из-за одного особо долгого случая в 7 дней, который был на границе бага и инцидента.
Расследование должно быть проведено в течение 7 дней после решения, а все вошедшие в беклог изменения после инцидента (предотвращающие следующие инциденты) должны быть внедрены в течение 30 дней после расследования. Целевой показатель по ущербу — менее полумиллиона рублей в месяц.
Важной метрикой является среднее время до отказа — это среднее время между происходящими инцидентами, оно не должно быть меньше 3 дней.
Для каждого инцидента есть ещё несколько правил:
- Нанесённый убыток (пока плохо считаем репутационный, речь про прямой в виде недополученной за время простоя выручки). Есть механика подсчёта прямых и косвенных убытков для учеников с сорванными уроками. Также достаточно понятно считается стоимость простоя проектов вроде чего-то инфраструктурного. Если у вас настроен расчёт репутационных потерь из-за инцидентов, поделитесь в комментариях, как вы это считаете.
- Время начала и конца инцидента. Что именно случилось. Привязаны задачи на устранение проблем. Привязан затронутый сервис и пункт фреймворка стабильности (если такой есть). Указана задача, ставшая причиной инцидента (если такая есть).
- На встречу приглашается тимлид соответствующей команды или ответственный сотрудник, которого укажет тимлид. Проверяется, что разработчики+Оps тратят минимум в два раза больше времени на фиксы, чем тратит рабочая группа на встречи.
Если вы обратите внимание на последнюю формулировку, то можете заметить, что некоторые инциденты дешевле закрыть без внесения изменений в код, чем предотвращать в дальнейшем. Это редкие случаи, мы всё же стремимся к стабильности —, но такое случается. Точно так же можно посчитать, что проблема носит слишком непредсказуемый характер, чтобы вообще было возможно её предотвратить, либо такое предотвращение неоправданно усложняет всем жизнь — такие задачи тоже не решаем.
У инфраструктурной команды после инцидентов копилось очень много относительно небольших задач, но падали они чаще, чем могла расчистить команда. Для них мы переделали процесс следующим образом: в конце каждого месяца все задачи по фиксам инфраструктуры группируются по типу работ (очень много похожих), затем переставляем приоритеты. Всё minor закрываем, если они не были уже сделаны. Фокусируемся только на том, что нужно для неповторения крупных инцидентов.
Для команд разработки есть SLA по инцидентам в 30 дней на решение задач, поставленных в рамках ретро. Долгое время задачи после ретро зависали в беклоге и не решались. Сейчас мы пересмотрели приоритеты задач, направленных на предотвращения повторения инцидентов, и рекомендуем команде брать их в ближайший спринт, чтобы успеть в SLA 30 дней. Или совместно принимаем решение в рамках ретро, что инцидент является minor, его повторение невозможно и соответственно изначально не ставим задачи на предотвращение.
Вот так, например, выглядит дашборд стабильности:
Из интересных эпизодов за последнее время:
- Миграция CDN привела к тому, что часть ссылок на картинки в контенте (например, в упражнениях) отвалилась. Это была ошибка в одном из регулярных выражений, определяющих путь в графике. Финансовых потерь из-за отмен уроков не было, но были репутационные потери в том, что преподаватели не могли получить полный контент для проведения урока.
- Отвалилась телефонная линия за Qrator.
- Превысили лимит эластика.
- Были проблемы с нодой с базой данных.
- Случайно грохнули папку с графикой для писем — просто обычная ошибка несогласованности.
Финансовый ущерб считают специальные скрипты, которые отслеживают статистические отклонения количества проведённых уроков за время инцидента. Это тоже достаточно автоматизировано.
Мы пока не оцениваем вероятность повтора инцидента по специальным скриптам. Сейчас мы категоризируем и приоритезируем инциденты и то, насколько они связаны.
Для ускорения решения задачи нужна классификация «что делать» и «насколько срочно» — появилась простая шкала приоритета. И если раньше очень много инцидентов приходилось на инфраструктурную команду, то теперь всегда есть связывание с задачей команды разработки, которая этот инцидент вызвала. По сути, мы начали вести статистику по командам разработки, кто и как весело нас уронил. До этого инцидент часто жил отдельно, а руководитель подразделения отдельно. На комиссию по разбору приходил разработчик из команды, а руководитель даже не знал, что происходит и решили потом как-то или нет. Новый процесс всё это очень прозрачно показывает, и это стало управляемым.
Первым шагом инцидент-менеджмента стала договоренность с CTO, руководителем инфраструктуры и руководителем тестирования о SLA-процессах инцидент-менеджмента.
Не могу сказать, что мы стали делать вообще всё принципиально иначе, но в таком процессе инциденты и задачи после них заимели понятный процесс решения, понятные SLA, понятный процесс постмортема и вообще получили больше внимания. В итоге получилось сильно сократить время решения (что прямо влияет на убытки) и получать больше решённых задач в конце разбора инцидента.
Важно показывать убытки подразделениям, передавать ответственность внутрь команд этих подразделений и держать решение инцидентов на виду у руководителя подразделения. Единый ответственный за процесс помогает настроить процесс и соблюдать правила, но без вовлечённости команд разработки процесс будет пустой тратой времени.
Вот тут есть пост про процесс, который был год назад. А вот ещё раз разбор истории с сертификатом.