[Перевод] Методика развёртывания проектов, применяемая в Slack
Вывод нового релиза проекта в продакшн требует тщательного соблюдения баланса между скоростью развёртывания и надёжностью решения. В компании Slack ценят быстрые итерации, короткие циклы обратной связи, оперативную реакцию на обращения пользователей. Кроме того, в компании имеются сотни программистов, которые стремятся к максимально возможной продуктивности.
Авторы материала, перевод которого мы сегодня публикуем, говорят, что компания, которая стремится придерживаться подобных ценностей и при этом растёт, должна постоянно совершенствовать свою систему развёртывания проектов. Компании нужно вкладывать силы в прозрачность и надёжность рабочих процессов, делая это для того чтобы эти процессы соответствовали бы масштабам проекта. Здесь речь пойдёт о рабочих процессах, сложившихся в Slack, и о некоторых решениях, которые привели компанию к использованию в ней существующей сегодня системы развёртывания проектов.
Как процессы развёртывания проектов работают сегодня
Каждый PR (pull request) в Slack должен быть обязательно подвергнут код-ревью и должен успешно пройти все тесты. Только после того, как будут выполнены эти условия, программист может выполнить слияние своего кода с веткой master проекта. Однако развёртывание такого кода выполняется только в рабочие часы по североамериканскому времени. В результате мы, за счёт того, что наши сотрудники находятся на рабочих местах, полностью готовы к решению любых неожиданных проблем.
Каждый день мы выполняем около 12 запланированных развёртываний. В процессе каждого развёртывания программист, назначенный главным по развёртыванию, отвечает за вывод новой сборки в продакшн. Это многошаговый процесс, который обеспечивает плавный вывод сборки в рабочий режим. Благодаря такому подходу мы можем обнаруживать ошибки до того, как они затронут всех наших пользователей. Если ошибок окажется слишком много — развёртывание сборки можно откатить. Если же некая отдельная проблема обнаружена после релиза, для неё легко можно выпустить исправление.
Интерфейс системы Checkpoint, которой пользуются в Slack для развёртывания проектов
Процесс развёртывания нового релиза в продакшне можно представить состоящим из четырёх шагов.
▍1. Создание ветки релиза
Каждый релиз начинается с новой ветки релиза, с момента в нашей Git-истории. Это позволяет назначать релизу теги и даёт место, в которое можно вносить оперативные исправления для ошибок, найденных в процессе подготовки релиза к выпуску в продакшн.
▍2. Развёртывание в промежуточном окружении
Следующий шаг работы заключается в развёртывании сборки на промежуточных (staging) серверах и в запуске автоматического теста на общую работоспособность проекта (smoke test). Промежуточное окружение — это продакшн-окружение, в которое не попадает внешний трафик. В этом окружении мы проводим дополнительное ручное тестирование. Это даёт нам дополнительную уверенность в том, что изменённый проект работает правильно. Одних лишь автоматизированных тестов для обретения подобной уверенности недостаточно.
▍3. Развёртывание в dogfood- и canary-окружениях
Развёртывание в продакшне начинается с dogfood-окружения, представленного набором хостов, которые обслуживают наши внутренние рабочие пространства Slack. Так как мы — весьма активные пользователи Slack, применение такого подхода помогло обнаружить множество ошибок на ранних стадиях развёртывания. После того, как мы удостоверились в том, что базовый функционал системы не нарушен, выполняется развёртывание сборки в canary-окружении. Оно представляет собой системы, на которые идёт примерно 2% продакшн-трафика.
▍4. Постепенный вывод в продакшн
Если показатели мониторинга нового релиза оказываются стабильными, и если после развёртывания проекта в canary-окружении мы не получили жалоб, мы продолжаем постепенный перевод продакшн-серверов на новый релиз. Процесс развёртывания разбит на следующие этапы: 10%, 25%, 50%, 75% и 100%. В результате мы можем медленно передавать продакшн-трафик новому релизу системы. При этом у нас есть время на исследование ситуации в случае выявления неких аномалий.
▍Как быть, если в ходе развёртывания что-то пошло не так?
Внесение модификаций в код — это всегда риск. Но мы справляемся с этим благодаря наличию у нас хорошо подготовленных «главных по развёртыванию», которые руководят процессом вывода нового релиза в продакшн, наблюдают за показателями мониторинга и координируют работу программистов, выпускающих код.
В том случае, если что-то и правда пошло не так, мы стараемся обнаружить проблему как можно раньше. Мы исследуем проблему, находим PR, который вызывает ошибки, откатываем его, тщательно анализируем и создаём новую сборку. Правда, иногда проблема оказывается незамеченной до вывода проекта в продакшн. В подобной ситуации самое важное — это восстановить работу сервиса. Поэтому мы, до начала исследования проблемы, немедленно откатываемся до предыдущей рабочей сборки.
Строительные блоки системы развёртывания
Рассмотрим технологии, лежащие в основе нашей системы развёртывания проектов.
▍Быстрые развёртывания
Вышеописанный рабочий процесс может показаться, в ретроспективе, чем-то совершенно очевидным. Но наша система развёртывания стала такой далеко не сразу.
Когда компания была значительно меньше, всё наше приложение могло работать на 10 Amazon EC2-инстансах. Развёртывание проекта в такой ситуации означало применение rsync для быстрой синхронизации всех серверов. Раньше новый код от продакшна отделял всего один шаг, представленный промежуточным окружением. Сборки создавались и проверялись в таком окружении, а потом шли сразу в продакшн. Разобраться в такой системе было очень просто, она позволяла любому программисту в любое время развернуть написанный им код.
Но по мере того, как росло количество наших клиентов, росли и масштабы инфраструктуры, необходимой для обеспечения работы проекта. Скоро, учитывая постоянный рост системы, наша модель развёртывания, основанная на отправке нового кода на серверы, перестала справляться со своей задачей. А именно, добавление каждого нового сервера означало увеличение времени, необходимого на выполнение развёртывания. Даже стратегии, основанные на параллельном применении rsync, имеют определённые ограничения.
В итоге мы решили эту проблему, перейдя на полностью параллельную систему развёртывания, устроенную не так, как старая система. А именно, теперь мы не отправляли код на серверы, используя скрипт синхронизации. Теперь каждый сервер самостоятельно загружал новую сборку, узнавая о том, что это нужно сделать, благодаря наблюдению за изменением ключа Consul. Серверы загружали код параллельно. Это позволило нам поддерживать высокую скорость развёртывания даже в обстановке постоянного роста системы.
1. Продакшн-серверы наблюдают за ключом Consul. 2. Ключ меняется, это сообщает серверам о том, что им надо начать загрузку нового кода. 3. Серверы загружают tarball-файлы с кодом приложения
▍Атомарные развёртывания
Ещё одним решением, которое помогло нам дойти до многоуровневой системы развёртывания, стало атомарное развёртывание.
До использования атомарных развёртываний каждое развёртывание могло привести к появлению большого количества сообщений об ошибках. Дело в том, что процесс копирования новых файлов на продакшн-серверы не был атомарным. Это приводило к существованию короткого временного отрезка, когда код, в котором вызывались новые функции, оказывался доступным до того, как оказывались доступными сами эти функции. Когда такой код вызывался, это приводило к возврату внутренних ошибок. Это проявлялось в неудачных запросах к API и в «поломанных» веб-страницах.
Команда, которая занималась этой проблемой, решила её, введя понятие «горячих» (hot) и «холодных» (cold) директорий. Код в «горячей» директории отвечает за обработку продакшн-трафика. А в «холодных» директориях код, во время работы системы, лишь готовится к использованию. В ходе развёртывания новый код копируется в неиспользуемую «холодную» директорию. Затем, когда на сервере не будет активных процессов, производится мгновенное переключение директорий.
1. Распаковка кода приложения в «холодную» директорию. 2. Переключение системы на «холодную» директорию, которая становится «горячей» (атомарная операция)
Итоги: сдвиг акцента на надёжность
В 2018 году проект дорос до таких масштабов, когда очень быстрое развёртывание стало вредить стабильности продукта. У нас была весьма продвинутая система развёртывания, в которую мы вложили много сил и времени. Нам нужно было лишь перестроить и усовершенствовать процессы организации развёртывания. Мы превратились в достаточно крупную компанию, разработки которой использовались во всём мире для организации бесперебойной связи и для решения важных задач. Поэтому в центре нашего внимания оказалась надёжность.
Нам нужно было сделать процесс развёртывания новых релизов Slack более безопасным. Эта необходимость и привела нас к совершенствованию нашей системы развёртывания. Собственно говоря, выше мы и обсуждали эту вот усовершенствованную систему. В недрах системы мы продолжаем пользоваться технологиями быстрого и атомарного развёртывания. Изменилось то, как именно выполняется развёртывание. Наша новая система предназначена для постепенного выполнения развёртывания нового кода на разных уровнях, в разных средах. Теперь мы используем более совершенные, чем раньше, вспомогательные инструменты и средства для мониторинга системы. Это даёт нам возможность отлавливать и устранять ошибки задолго до того, как они получат шанс добраться до конечного пользователя.
Но мы не собираемся останавливаться на достигнутом. Мы постоянно улучшаем эту систему, применяя более совершенные вспомогательные инструменты и средства автоматизации работы.
Уважаемые читатели! Как устроен процесс развёртывания новых релизов проектов там, где работаете вы?