Вывод нового релиза проекта в продакшн требует тщательного соблюдения баланса между скоростью развёртывания и надёжностью решения. В компании Slack ценят быстрые итерации, короткие циклы обратной связи, оперативную реакцию на обращения пользователей. Кроме того, в компании имеются сотни программистов, которые стремятся к максимально возможной продуктивности.
Авторы материала, перевод которого мы сегодня публикуем, говорят, что компания, которая стремится придерживаться подобных ценностей и при этом растёт, должна постоянно совершенствовать свою систему развёртывания проектов. Компании нужно вкладывать силы в прозрачность и надёжность рабочих процессов, делая это для того чтобы эти процессы соответствовали бы масштабам проекта. Здесь речь пойдёт о рабочих процессах, сложившихся в Slack, и о некоторых решениях, которые привели компанию к использованию в ней существующей сегодня системы развёртывания проектов.
Каждый PR (pull request) в Slack должен быть обязательно подвергнут код-ревью и должен успешно пройти все тесты. Только после того, как будут выполнены эти условия, программист может выполнить слияние своего кода с веткой master проекта. Однако развёртывание такого кода выполняется только в рабочие часы по североамериканскому времени. В результате мы, за счёт того, что наши сотрудники находятся на рабочих местах, полностью готовы к решению любых неожиданных проблем.
Каждый день мы выполняем около 12 запланированных развёртываний. В процессе каждого развёртывания программист, назначенный главным по развёртыванию, отвечает за вывод новой сборки в продакшн. Это многошаговый процесс, который обеспечивает плавный вывод сборки в рабочий режим. Благодаря такому подходу мы можем обнаруживать ошибки до того, как они затронут всех наших пользователей. Если ошибок окажется слишком много — развёртывание сборки можно откатить. Если же некая отдельная проблема обнаружена после релиза, для неё легко можно выпустить исправление.
Интерфейс системы Checkpoint, которой пользуются в Slack для развёртывания проектов
Процесс развёртывания нового релиза в продакшне можно представить состоящим из четырёх шагов.
Каждый релиз начинается с новой ветки релиза, с момента в нашей Git-истории. Это позволяет назначать релизу теги и даёт место, в которое можно вносить оперативные исправления для ошибок, найденных в процессе подготовки релиза к выпуску в продакшн.
Следующий шаг работы заключается в развёртывании сборки на промежуточных (staging) серверах и в запуске автоматического теста на общую работоспособность проекта (smoke test). Промежуточное окружение — это продакшн-окружение, в которое не попадает внешний трафик. В этом окружении мы проводим дополнительное ручное тестирование. Это даёт нам дополнительную уверенность в том, что изменённый проект работает правильно. Одних лишь автоматизированных тестов для обретения подобной уверенности недостаточно.
Развёртывание в продакшне начинается с dogfood-окружения, представленного набором хостов, которые обслуживают наши внутренние рабочие пространства Slack. Так как мы — весьма активные пользователи Slack, применение такого подхода помогло обнаружить множество ошибок на ранних стадиях развёртывания. После того, как мы удостоверились в том, что базовый функционал системы не нарушен, выполняется развёртывание сборки в canary-окружении. Оно представляет собой системы, на которые идёт примерно 2% продакшн-трафика.
Если показатели мониторинга нового релиза оказываются стабильными, и если после развёртывания проекта в 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 более безопасным. Эта необходимость и привела нас к совершенствованию нашей системы развёртывания. Собственно говоря, выше мы и обсуждали эту вот усовершенствованную систему. В недрах системы мы продолжаем пользоваться технологиями быстрого и атомарного развёртывания. Изменилось то, как именно выполняется развёртывание. Наша новая система предназначена для постепенного выполнения развёртывания нового кода на разных уровнях, в разных средах. Теперь мы используем более совершенные, чем раньше, вспомогательные инструменты и средства для мониторинга системы. Это даёт нам возможность отлавливать и устранять ошибки задолго до того, как они получат шанс добраться до конечного пользователя.
Но мы не собираемся останавливаться на достигнутом. Мы постоянно улучшаем эту систему, применяя более совершенные вспомогательные инструменты и средства автоматизации работы.
Уважаемые читатели! Как устроен процесс развёртывания новых релизов проектов там, где работаете вы?
Авторы материала, перевод которого мы сегодня публикуем, говорят, что компания, которая стремится придерживаться подобных ценностей и при этом растёт, должна постоянно совершенствовать свою систему развёртывания проектов. Компании нужно вкладывать силы в прозрачность и надёжность рабочих процессов, делая это для того чтобы эти процессы соответствовали бы масштабам проекта. Здесь речь пойдёт о рабочих процессах, сложившихся в 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 более безопасным. Эта необходимость и привела нас к совершенствованию нашей системы развёртывания. Собственно говоря, выше мы и обсуждали эту вот усовершенствованную систему. В недрах системы мы продолжаем пользоваться технологиями быстрого и атомарного развёртывания. Изменилось то, как именно выполняется развёртывание. Наша новая система предназначена для постепенного выполнения развёртывания нового кода на разных уровнях, в разных средах. Теперь мы используем более совершенные, чем раньше, вспомогательные инструменты и средства для мониторинга системы. Это даёт нам возможность отлавливать и устранять ошибки задолго до того, как они получат шанс добраться до конечного пользователя.
Но мы не собираемся останавливаться на достигнутом. Мы постоянно улучшаем эту систему, применяя более совершенные вспомогательные инструменты и средства автоматизации работы.
Уважаемые читатели! Как устроен процесс развёртывания новых релизов проектов там, где работаете вы?