Эволюция деплоя кода на Reddit

Мы постоянно внедряем новый код на Reddit.  Каждый инженер пишет код, проверяет его, тестирует и выпускает в продакшн. Это происходит очень часто – примерно 200 раз в неделю, в то время как деплой занимает всего 10 минут. На протяжении многих лет система, которая управляет всем этим, эволюционировала.  Давайте посмотрим, как все менялось.

Как начиналась история: последовательные и многократные развертывания (2007-2010)

Началом нынешней системы был скрипт Perl (да, да), который назывался push. Это было написано в то время, когда совершенно все было по другому.  Вся инженерная команда была настолько маленькой, что помещалась в небольшой конференц-зал. Reddit еще не был на AWS. Сайт работал на фиксированном количестве серверов, причем дополнительную емкость нужно было добавлять вручную, и состоял он из одного большого монолитного приложения Python с коротким названием r2.

 

1dff6-table

Одна вещь, которая не менялась с годами – это запросы, которые  классифицируются балансировщиком нагрузки и назначаются определенным «пулам» идентичных серверов приложений. К примеру, страницы листинга и комментариев обслуживаются 2-мя совершенно разными «пулами»  серверов. Хоть r2 и может обрабатывать любой запрос, отдельные пулы изолируются от всплесков трафика в других пулах. И если что-то сломается, то эта часть системы не потащит за собой другую.

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

# build the static files and put them on the static server
`make -C /home/reddit/reddit static`
`rsync /home/reddit/reddit/static public:/var/www/`>
# iterate through the app servers and update their copy
# of the code, restarting once done.
foreach $h (@hostlist) {
`git push $h:/home/reddit/reddit master`
`ssh $h make -C /home/reddit/reddit`
`ssh $h /bin/restart-reddit.sh`
}

Развертывание коды было последовательным. Это не только  звучит очень просто,  но и на деле все получается очень хорошо: ведь это позволяет нам использовать деплой canary. По сути, Canary deploy – это развертывание релиза для ограниченной группы пользователей с целью  проверки правильности его функционирования в реальных условиях. Цель состоит в том, чтобы определить потенциальную проблему как можно раньше,  ограничить количество клиентов, на которых она окажет негативное влияние(при этом просто нажав Ctrl-C). Кроме того, эта практика потенциально позволяет осуществить более легкое восстановление или возврат к предыдущей версии при наличии  такой необходимости.

Всех этих инструментов на том момент хватало, чтобы деплой был согласованным и повторяемым. Все работало достаточно быстро.  И все было хорошо.

Куча новых людей (2011)

Затем мы начали расширятся, и наша команда выросла до 6 инженеров. Нам нужно было срочно скоординировать деплой кода, так как некоторые инженеры из нашей команды большинство работы делали дома. Мы изменили технологию push таким образом, что стали узнавать когда развертывание началось и завершилось с помощью IRC чата. Сам  процесс деплоя конечно не изменился, но теперь система делала за вас работу и рассказывала что происходит в данный момент.

Работа со всей инфраструктурой перенеслась в общий чат. Мы запускали команды прямо из чата, при этом разработчики/сисадмины видели что происходит в режиме реального времени, могли просматривать историю изменений, запускали свои команды, поддерживали коммуникацию вокруг работы и даже обменивались опытом. Таким образом информация и рабочий процесс принадлежал всей команде. В то время – это было очень круто.

Это было началом использования ботов в рабочих процессах деплоя.  Было много разговоров том,  что нужно менять системы, которые управляли развертыванием прямо из чата, но мы пока довольствовались сторонними IRC-серверами. Мы очень переживали по поводу безопасности и работали только с односторонним  потоком информации.

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

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

Мы использовали uWSGI для управления рабочими процессами, и поэтому, когда мы перезагрузили приложение – все старые процессы были убиты и запущенны новые. Новым процессам понадобилось немного времени  чтобы подготовиться к обработке запросов, а в сочетании со произвольным доступом к пулу повлияло на его возможность обслуживать запросы. Поскольку список серверов рос(как и продолжительность деплоя), безопасность наших развертываний на севере мягко говоря  хромала“. 

Модернизация инструмента развертывания кода (2012)

Мы полностью переписали наш инструмент деплоя (кстати он написан на Python) и не стали менять его название. Если вы еще не забыли,  этот незаменимый инструмент назывался push. В новой версии мы улучшили очень много и ниже я вам поподробней расскажу что именно

Во-первых, был подобран список хостов из DNS, который мы не стали прописывать в коде(как это было раньше). Это позволяло нам в будущем  обновлять список независимо от инструмента развертывания

Ну а чтобы решить проблему последовательных перезапусков, мы просто перетасовали список узлов перед деплоем. Так как пулы серверов смешивало,  это позволило нам безопасно переходить на более высокую скорость и, таким образом, деплоить код намного быстрее.

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

Другое маленькое, но очень важное изменение заключалось в том, чтобы всегда устанавливать фиксированную версию кода.

Итак, важно подчеркнуть различия между кодом обновленного инструмента (ориентированным на списки хостов и SSH в них) и  выполняемыми командами. Он все еще сильно искажался потребностями r2, но у него уже был прото-API. Это позволило r2 контролировать свои собственные шаги деплоя, что упростило развертывание изменений в потоке сборки и выпуска.  Например, вот что можно было выполнить на отдельном сервере:

sudo /opt/reddit/deploy.py fetch reddit
sudo /opt/reddit/deploy.py deploy reddit f3bbbd66a6
sudo /opt/reddit/deploy.py fetch-names
sudo /opt/reddit/deploy.py restart all

Автомасштабирование (2013)

Затем мы просто влюбились в идею интернета вещей, которая подкупает возможностью объединить практически безграничный пул устройств и с легкостью управлять ими. Это позволило нам сэкономить массу денег.

Предыдущие улучшения, которые теперь автоматически вызывали список хостов из DNS помогли сделать нам этот очень нужный естественный переход. Хост-лист менялся теперь намного чаще и именно запуск автосканирования смог бы решить все наши проблемы.

Кстати, мы также переключились с uWSGI на Gunicorn примерно в это же время по разным причинам. На самом деле это не повлияло на развертывание кода в дальнейшем.

Так все и работало какое-то время.

Слишком много серверов (2014)

Со временем  увеличилось количество серверов, необходимых для обслуживания максимального трафика.  Это означало, что деплой кода.   В худшем случае нормальное развертывание занимало около часа. Это было плохо.

Мы переписали инструмент развертывания для параллельной обработки узлов.  Новая версия стала называться rollpin. Старый инструмент занимал очень много времени инициализируя  ssh-соединения и ожидая завершения команд, именно распараллеливание и помогло ускорить деплой кода и сохранить очень много времени (с 1 часа до 5 минут).

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

Самое важное изменение в инструменте это то, что API инструмента развертывания и инструмента, который жил на каждом сервере, был гораздо более четко определен и отделен от r2.

Ниже приведен пример развертывания, при этом выделенные команды представляют собой API, который выполнялся удаленно:

Слишком много людей (2015)

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

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

Две службы (2015 год)

Совершенно внезапно у нас появилась вторая услуга. Время шло, технологии развивались и пора было подумать о мобильной версии сайта. Это был совершенно другой стек, он  имел собственные сервера и процесс сборки. Это была первая реальная проверка созданного недавно API. С добавлением возможности выполнять шаги в разных локациях для каждого проекта теперь мы могли  управлять обеими службами в рамках одной и той же системы.

25 новых служб (2016)

В течение следующего года мы увидели взрывной рост в команде Reddit. Службы у нас увеличились с 2 до 25, а команды с 2 до 15. На тот момент большинство наших сервисов были построены на базе Baseplate, нашей базовой инфраструктуре или узловых приложениях, подобных мобильной сети.

Защита (2017)

Вместе с увеличением количества монолитных серверов увеличивалось и время развертывания. Мы хотели развернуть с большим количеством параллельных вычислений, но при этом было бы слишком много одновременных перезапусков на серверах приложений. Поэтому у нас были низкие мощности и мы не могли обслуживать входящие запросы, перегружая другие сервера приложений.

Основной процесс Gunicorn использовал ту же модель, что и uWSGI, и немедленно перезапустил всех работников. В то время как новые рабочие процессы загружались, мы  не могли обслуживать какие-либо запросы. Время запуска нашего монолита варьировалось от 10-30 секунд, что означало, что в течение этого периода мы не сможем обслуживать какие-либо запросы. Чтобы это обойти, мы заменили главный процесс gunicorn на менеджер Einhorn , сохранив при этом стек HTTP и контейнер WSGI. Einhorn перезапускает рабочие процессы, создавая при этом одного нового работника и ждет, что он объявит себя готовым, а затем убирает старого работника и повторяет все это до тех пор, пока не обновит абсолютно всех. то создало защитную сетку и позволило нам работать во время развертывания.

Ну куда же без проблем (. Как упоминалось ранее, для замены работника и его загрузки может потребоваться до 30 секунд. А это означало, что если ваш код содержал ошибку, он не сразу появится, и вы будете прокручивать сервера один за другим.  Чтобы это предотвратить, мы нашли способ заблокировать развертывание от перехода на другой сервер до тех пор, пока весь рабочий процесс не будет перезапущен. Это было сделано путем простого опроса состояния einhorn  и ожидания  готовности всех новых рабочих. Этот новый механизм позволяет нам деплоить код с огромной скоростью – за 7 минут  около 800 серверов, несмотря на  ожидание безопасности.

Итог

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

Инфраструктура Reddit должна поддерживать свою команду, поскольку она растет и постоянно создает новые вещи.  Темп роста компании  Reddit просто невероятен, и в данный момент мы работаем над очень большими и интересными проектами.

Ссылка на источник – https://redditblog.com/2017/06/02/the-evolution-of-code-deploys-at-reddit/