Как упавший продакшен делает нас лучше

Kate

Administrator
Команда форума
Неважно, сколько раз ты упал. Важно — сколько раз ты поднялся.
© Мой домашний сервер

Вы не ослышались. Речь не о том, что упавший продакшен — это хорошо. Это плохо. Но продакшен иногда падает, надо это признать. Этого надо не допускать, но к этому надо быть готовым. Каждое падение, каждый инцидент — это большой стресс, но даже в такой неприятной обстановке настоящий самурай может найти возможность стать лучше.
Инциденты — это прекрасная возможность получить новые знания и навыки. Они выдёргивают нас из ежедневной рутины и сталкивают лицом к лицу с неожиданными трудностями и новыми людьми. Когда все собираются вместе и решают одну важную проблему, стирается иерархия и начинается самое интересное.

image

Привет, Хабр! Я Игорь, и я тушу пожары занимаюсь разработкой в Ozon. Я расскажу вам не просто о том, как работать с инцидентами, а как обратить каждое (ну или хотя бы некоторые) происшествие на проде себе на пользу.

Как рулят инцидентами в Ozon
У нас выстроена достаточно хорошая культура работы с инцидентами. Есть специальная команда дежурных инженеров мониторинга, чья работа буквально бдить за графиками и рулить процессом, когда что-то идёт не так.

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

Все инциденты публичны, есть официальный созвон, никаких решений проблем «под одеялом» не бывает. Как правило, на достаточно серьёзных инцидентах быстро собирается группа достаточно серьёзных людей — от простых разработчиков до очень больших начальников. Присоединиться, чтобы послушать или предложить решение, может любой желающий. Если для решения проблемы требуется помощь со стороны, то дежурный инженер быстро её организует.
Также дежурный имеет возможность видеть, что одни проблемы вызывают другие, и не создавать несколько инцидентов для проблем, вызванных одной причиной.

Посмотреть по сторонам​


Инциденты часто возникают где-то между зонами ответственности разных команд, и, чтобы их разрешить, нужно действовать сообща. По ходу действий приходится знакомиться с людьми, узнавать, что вообще происходит в компании, но за пределами твоего уютного мирка. Узнаешь, как другие люди смотрят на твои сервисы и тебя самого, что в целом всегда полезно.
Очевидный пример — это инфраструктура и работа с людьми, поддерживающими её.

Отдел инфраструктуры
В Ozon есть специальные люди, отвечающие за инфраструктуру. Они заботятся о том, чтобы Kubernetes масштабировался, PostgreSQL бежал бодро, и о многом другом. Сами инженеры прикладных приложений могут притрагиваться к настройкам только при наличии очень большого желания и квалификации.

Мы можем месяцами не пересекаться с ребятами из отдела инфраструктуры, но, когда что-то идёт не так, мы с ними сталкиваемся лицом к лицу и должны взаимодействовать. Тут-то мы и узнаём, какой наш код плохой, и какие у нас неоптимальные запросы, как наша структура данных мешает таблицам вакуумиться, как размываются индексы при таких частых обновлениях данных, и т. д. Помимо объективной (и не очень) критики, есть шанс узнать такие вещи, о которых ты раньше и не подозревал, например какие запросы могут убить базу мгновенно, а какие — постепенно. Кто бы мог подумать (sarcasm), что UPDATE-запросы на произвольно большие выборки данных вызывают блокировки и такие штуки лучше делать частями! Кто бы мог подумать (на этот раз серьёзно), что GIN-индексы имеют большие проблемы с обновлениями и могут замедлять отдельно взятые UPDATE-запросы в сотни раз! О таком обычно в книжках не пишут — и шанс узнать об этом есть только при достаточно тесном контакте с достаточно узким специалистом.

Пример​

После удаления порядка миллиона строк из таблички размером четыре миллиона строк выборка из таблицы стала дико тупить. Собрали инцидент — и узнали от DBA, что большие удаления в PostgreSQL нарушают работу планировщика, так как ему приходится пропускать большое количество строк именно во время планирования. Итого время выполнения запроса было 0.5 мс, а время планирования запроса — 500 мс. Сделали табличке VACUUM — и ей полегчало.

Наблюдение за тем, как люди из команды инфраструктуры (да и из любой другой, к которой ты не имеешь прямого отношения) решают проблемы, сильно расширяет кругозор и добавляет вариантов действий в твой арсенал. Совершенно невероятные Bash-скрипты, которые нам, детям Kubernetes, не снились в самых страшных снах. Дебаг скомпилированного и уже давно запущенного PostgreSQL (с управлением и пошаговым режимом!), биндинг виртуальных машин к ядрам на лету, и не только. Всё это простой программист на JSON-ах имеет шанс увидеть, только когда матёрый сисадмин шарит экран и расчехляет терминал.

Узнать критические точки​


Инциденты обычно возникают в каких-то сложных частях системы. Когда инцидент затрагивает какую-то часть системы раз за разом, это сигнал, что где-то там есть важные механизмы, которым надо уделить время и разобраться. Это могут быть как механизмы языка программирования, так и внешние системы общего назначения, например базы данных или брокеры сообщений. Если у вас периодически течёт память, имеет смысл изучить работу сборщиков мусора. Если накапливаются лаги на топиках в Kafka, то можно, наконец, узнать, кто же такой этот Кафка и почему о нём все говорят. Таким образом я познакомился с тем, как работает репликация баз данных в PostgreSQL, и узнал, что, оказывается, долгие запросы на асинхронной реплике могут замедлять мастер. Получив необходимый минимум знаний во время инцидента, я могу пойти и изучить тему самостоятельно, точно зная, что эти знания мне пригодятся.

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

Пример​

У нас есть функционал асинхронной генерации различных отчётов для продавцов в формате XLS. Запросы от продавцов падают в очередь, откуда их разгребает пул воркеров, генерирует отчёты и складывает их в файловое хранилище, откуда их может забрать пользователь. Функционал был написан больше года назад, когда до него добрался особо крупный и особо нетерпеливый продавец. Он протыкал кнопку «Создать отчёт» суммарно 400 раз в течение десяти минут. Так как пользователь был крупный (не в смысле физических размеров, а в смысле ассортимента), то и отчёты для него должны были выдаваться немаленькие. Эти 400 немаленьких отчётов забили всю очередь и все воркеры. Для всех остальных пользователей генерация встала. Они же, видя, что для них отчёты не создаются, тоже повторно нажимали на кнопку «Создать отчёт» — и задания на генерацию радостно забивали очередь ещё сильнее. Слава богу алертов, у нас был алерт на размер очереди на генерацию отчётов. В течение 20 минут мы поняли первопричину и удалили все повторяющиеся задания. Функционал был восстановлен.

После этого случая мы добавили два нововведения: дедупликацию заданий, чтобы не забивать очередь повторами, и лимиты на пользовательские запросы. Теперь продавцы могут генерировать не более трёх отчётов одновременно. Причём лимиты мы поставили не только на генерацию отчётов, но и вообще на все действия пользователя — как асинхронные, так и синхронные. Потому что не должно быть такого, что все ресурсы заняты одним пользователем.

Быть готовым к трудностям​


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

Пример​

Часто в больших проектах с микросервисной архитектурой есть сервис, который отдаёт данные пользователю, собирая их из ряда других микросервисов (паттерн API Composition). Частой ошибкой в таких случаях бывает, что падение или ошибочный ответ одного из микросервисов вызывает ошибочный ответ всего API Composition. Очевидно, что некоторые данные не являются критичными и можно обойтись без них и отдать (пусть неполный) ответ пользователю.

Если отказал сервис, который хранит пользовательские аватарки, то, очевидно, можно отдавать наружу заглушку с картинкой а-ля «Тут должна была быть ваша аватарка».

Аналогично, если не удаётся записать лог или метрику, это не должно приводить ни к каким деградациям. Если метрики не пишутся, то это большая проблема, но пользователь не должен от этого страдать. Можно поднять алерт, написать в лог и разбираться в проблеме в рабочем, а не в экстренном порядке.

Конечно, бывают действительно критичные ситуации, в которых лучше ничего не делать и ответить ошибкой 500, но их на самом деле не так много. В этом плане мне очень нравится концепция языка Go, который своей конструкцией

if err != nil {
....
}

призывает нас задуматься над каждым возможным негативным сценарием. Многие на автомате шлёпают между фигурными скобками

return err

Не делайте так. Во-первых, как я сказал, не каждая ошибка требует немедленного прекращения работы, — иногда её можно просто залогировать. Во-вторых, даже если нужно вернуть ошибку, можно снабдить её понятными пояснениями. Тут нам поможет концепция error wrapping.


if err != nil {
return fmt.Errorf(“something went wrong in avatar service: %w”, err)
}


Код должен явно и громко давать понять, когда что-то идёт не так, как должно, когда происходят вещи, которые не должны происходить. В идеале по логу или алерту сразу должно быть понятно, что делать или как понять причину более детально.

Это всё очевидные советы из книжек, но в ходе инцидентов особенно понимаешь их важность.

Прозрачность рулит​


Я видел множество инцидентов, когда какой-то график или кусок лога были ключевым моментом в понимании их причин. Например, если вы пытаетесь что-то получить из соседнего сервиса, но не получаете, то обычно важно знать, по какой причине.
Согласитесь, между сообщениями

Error getting avatar from avatar service

и

Error getting avatar from avatar service: i/o timeout

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

Наблюдения за тем, как коллеги исследуют проблемы, читают логи, какие смотрят графики, какие инструменты используют, сильно помогли мне находить нужную информацию быстрее в следующий раз. Например, для исправления чего-то, что происходит прямо сейчас, можно читать логи сервиса «наживую», прямо из kubectl. А для поиска причин проблемы, которая произошла какое-то время назад, хорошо бы освоить язык запросов вашего сервиса хранения логов (в Ozon это Graylog). Понимание существующих логов и графиков даёт подсказки, чего не хватает, какие ситуации ещё можно замерить и залогировать.

Иногда в ситуациях, когда нет никаких прямых свидетельств проблемы в виде графиков и логов, а есть только пользовательский фидбэк, можно начать решать проблему с (cюрприз) добавления логов и метрик. Так вы поймёте масштаб бедствия, поймёте, что проблема реальна, и, главное, поймёте, когда она будет решена. На метрики позже можно будет назначить алерт — и о следующем возникновении проблемы вы узнаете раньше пользователей.

Пример​

У нас есть график количества товаров, которые приходят на модерацию, есть график количества товаров, которые из неё уходят как успешно промодерированные или, наоборот, отклонённые. И есть прекрасный график, который показывает соотношение входа и выхода. Если вдруг за последние три часа на модерацию попало в три раза больше товаров, чем было промодерировано, значит, возможно, что-то идёт не так. Поднимается алерт, поднимается дежурный, который начинает выяснять, не случилось ли чего. Важно понимать, что этот график не даёт понимания причин проблемы, но даёт знать, что проблема существует, а причины надо искать — уже на соседних графиках.

Знакомство с прекрасными людьми​


Инцидент — прекрасная возможность узнать коллег, с которыми ты обычно не пересекаешься. Многих людей, у которых я многому научился, я встретил только благодаря инцидентам, других возможностей у меня пожалуй, не было. Можно повстречать людей, которые по-другому мыслят, умеют делать какие-то другие вещи, нежели ты или люди из твоей команды. Может встретиться эксперт по конкретной технологии и рассказать то, о чём в книгах не пишут, как, например, эксперты по базам, о которых я писал выше. Можно узнать, что пользователем вашего сервиса являются люди с другого конца компании, делающие интересные проекты, и понять, что твой функционал важен и востребован.

Советы в заключение​


Инцидентов не надо бояться, как не надо бояться того, что пойдёт дождь. Дождь всё равно случится, а продакшен всё равно рано или поздно упадёт. В наших силах быть к этому готовыми, минимизировать последствия и максимизировать пользу. А пользы, как я описал, извлечь можно довольно много.

  • Принимай участие в инцидентах с первого дня работы в компании. Даже если ты просто слушатель, это поможет тебе быстрее узнать контексты и правила, и когда наступит твоя очередь тушить продакшен, ты будешь знать, что делать.
  • Принимай во внимание накалённость обстановки и не лезь куда не надо. Больше слушай. Вопросы можно записать и задать позже.
  • Будь честен с другими и с собой — особенно в том, что знаешь, что можешь сделать и чего не можешь. Если тебе задают вопрос, на который ты не знаешь ответа, то лучше так и сказать, ничего не придумывая.
  • Не стесняйся брать инициативу в свои руки, когда это уместно. Это прекрасная возможность вырасти профессионально.
 
Сверху