Хабрахабр

CICD: бесшовный деплой на распределенные кластерные системы без даунтаймов

Выкладываю второй доклад с нашего первого митапа, который провели в сентябре. В прошлый раз можно было почитать (и посмотреть) про использование Consul для масштабирования stateful-сервисов от Ивана Бубнова из BIT.GAMES, а сегодня поговорим про CICD. Точнее расскажет об этом наш системный администратор Егор Панов, который отвечает за доступность инфраструктуры и сервисов в Pixonic. Под катом — расшифровка выступления.

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

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

  1. отличная экспертиза тестировщиков;
  2. плотное с ними взаимодействие;
  3. время, которое мы выдаем на тестирование.

Соответственно, если мы не выстроим свои процессы, например, деплой или CI (continuous integration) — рано или поздно мы придем к тому, что длительность тестирования все время будет увеличиваться и увеличиваться. И мы либо будем медленно все делать и потерям рынок, либо просто будем взрываться при каждом деплое.

Некоторые скажут, ну да, я поставлю Jenkins, быстро что-то накликаю, вот у меня готовый CICD. Но CICD-процесс построить не так уж и просто. Начнем по порядку. Нет, это не только инструмент, это еще и практики.

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

Это работает быстрее. Мы выделили разные репозитории: деплой у нас в одном репозитории, тесты — в другом. Потому что есть один важный момент в этом пункте — нужно выстроить простой и прозрачный для всех гитфлоу. Может быть вам не подойдет, но для нас это гораздо удобнее. Для нас, например, деплой живет своим гитфлоу, который больше похож на GitHub-флоу, а серверная разработка живет по своему гитфлоу. Конечно, вы можете где-то скачать готовый, но в любом случае его нужно под себя подстроить, улучшить.

Нужно настроить полностью автоматическую сборку. Следующий пункт. Этот вариант долго не прожил, появился bash-скрипт. Понятно, на первом этапе разработчик сам лично собирает проект, потом сам лично его деплоит при помощи SCP, сам запускает, сам отправляет, кому нужно. Он прожил очень долго, за это время мы успели увеличиться в серверах до 500, настроить конфигурирование серверов на Puppet, накопить на Puppet легаси, отказаться от Puppet, перейти на Ansible, а этот билдсервер продолжал жить. Ну а так как окружение разработчиков все время меняется, появился специальный выделенный билдсервер.

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

После этого переписали все так, чтобы весь билд мог происходить на абсолютно любом агенте TeamCity и переписали его на Ansible, потому что конфигурирование на Ansible, почему бы тогда этим же инструментом не воспользоваться и для деплоя.

А почему? Следующее правило: чем чаще коммитишь, тем лучше. И на самом деле даже больше, чем каждый коммит. Потому что есть четвертое: каждый коммит собирается. Собственно, быстрая обратная связь, все замечательно. Я уже сказал, что у нас TeamCity, а он позволяет запустить коммит из вашей любимой IDE (вы догадываетесь, какую имею ввиду).

Как только ты настроил автоматический деплой, тут же нужно настроить автоматическую нотификацию в Slack. Сломанный билд чинится сразу же. Поэтому: человек узнал — сразу же починил. Все мы прекрасно знаем, что разработчик знает, как его код работает, только в тот момент, когда он его пишет.

Тут все просто, мы выбрали Ansible и AWX. Тестируем на окружении, повторяющем прод. Забыл сказать, у нас есть компоненты как Linux, так и Windows. Кто-то может быть спросит, а как же Docker, Kubernetes, OpenShift, где все проблемы из коробки давным давно решены? Соответственно, у нас есть приложение на Windows, которое плохо упаковывается в контейнер; есть приложение на Linux (которое на Java), которое прекрасно запаковывается, но это и незачем, оно прекрасно работает, где бы ты его не запустил. И, например, Photon-сервер, который на Windows, мы только-только недавно смогли упаковать более менее нормально в 10 Гб докер-контейнер. Это же Java.

Они оба прекрасно работают с виндой, но Ansible оказался гораздо проще для нас. Дальше мы выбирали между Ansible и Chef. В AWX есть секреты, есть графики, история. Когда мы уже установили AWX — вообще все огонь стало. Можно вообще далекому от всего этого человеку показать, он сразу все увидит и все станет понятно.

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

Замечательно.
7 пунктов — и мы уже построили какой-то CI-процесс. Кто читает наши статьи на Хабре, тот уже видел, как мы выбирали Graylog и как устанавливали. На следующей схеме не видно, но сбоку есть еще Graylog. В любом случае он помогает отдебажить, если какая-то проблема все-таки случилась.


Вот на этой базе уже можно переходить к деплою.

Скажу один лайфках: если вы используете Ansible, обязательно добавьте вот этот serial, который на слайде.
Но про деплой я уже рассказал во втором пункте, поэтому не буду сильно останавливаться на этом. А мы потерять один сервер легко можем и ты просто его заново перезаливаешь, никто и не заметил. Не раз бывало, что ты запускаешь что-то, а потом понимаешь, а я не то запустил, или не туда, или не то и не туда, а потом видишь, что это всего лишь один сервер.

Плюс установили хранилище артефактов на Nexus — он является единой точкой входа абсолютно для всех, не только для CI.

Ну и так как нексусы могут работать как прокси-сервисы в разных регионах, то они ускоряют деплой, установку rpm-пакетов, докер-образов, чего угодно.
И это очень помогает нам обеспечить повторяемость.

Например с Photon-сервером нам это не удалось. Когда закладываешь новый проект, желательно выбирать компоненты, которые легко поддаются автоматизации. А вот Cassandra, например, очень удобно обновляется и автоматизируется. В любом случае, это было лучшее решение по другим параметрам.

Клиент приходи на APP-сервер, где у него лежит профиль в БД Cassandra, а потом идет на мастер-сервер, который при помощи матчмейкинга дает ему уже гейм-сервер с какой-то комнатой.
Вот пример одного из наших проектов. Все остальные сервисы сделаны в виде «приложения — БД» и обновляется точно таким же образом.

Нам это удалось. Второй момент — нужно обеспечить простую малосвязанную архитектуру для деплоя.

У нас есть выделенные сервисы дискавери, которые реконфигурируют балансировщик, поэтому мы просто приходим к app-сервер, тушим его, он вылетает из балансировки, обновляем и все.
Смотрите, обновляем например app-сервер. И так с каждым по отдельности.

Клиент пингует каждый мастер-сервер в регионе и идет на тот, на котором пинг лучше. Мастер-сервера обновляются почти идентично. Соответственно, если мы обновляем мастер-сервер, то может быть игра чуть-чуть помедленнее будет идти, но обновляется это легко и просто.

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

Если вам нужен пример, то есть Elasticsearch-кластер.
Ключевой момент здесь, это ендпоинты, которые есть у каждого из компонентов, и с которыми легко и просто общаться. И он сразу в этом же JSON отдает всякие разные метрики и высокоуровневую информацию о кластере: зеленый, желтый, красный. При помощи обычных http-запросов в JSON можно легко с ним общаться.

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

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

Нет этого ощущения «о, релиз!», всё собрались и мурашки по коже. На самом деле я уже и не слежу, когда у нас там деплой происходит, когда релиз. Это реально круто. Сейчас это такая рутинная операция, я в чатике периодически вижу, что что-то зарелизилось, ну и ладно. Ваши сисадмины будут реветь от радости, когда вы так сделаете.

Нам есть, что улучшать. Но мир не стоит на месте, он иногда подпрыгивает. Это потребует еще доработать логирование, чтобы была не какая-то отдельная история, а четко: вот так билд собирался, так тестировался, так деплоился и так он ведет себя на проде. Например, логи билда хотелось бы тоже засунуть в Graylog. И continuous monitoring — с этим история посложнее.

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

Вопросы из зала

А что происходит, когда вы задеплоили какой-то мусор в продакшн? Например, вы что-то по перформансу не рассчитали и на интегрейшене все ок, а в продакшене смотрите — у вас серваки начинают падать. Как вы откатываетесь обратно? Есть какая-то кнопка «спаси меня»?

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

По поводу автоматического обновления деплоя: почему вы вносите изменения на текущий сервер, а не добавляете новый и просто добавляете его в таргетгруп или балансер?

Так быстрее.

Вы делаете изменения на рабочем сервере? Например, если вам нужно обновить версию Java, вы меняете стейт инстенса на Амазоне, обновляя версию Java или еще что-то, то как вы откатываетесь в таком случае?

Да, может быть придется перезалить сервер. Да, каждый компонент работает прекрасно с новой версией и старой.

Бывают изменения стейтов, когда возможны большие проблемы…

Тогда взорвемся.

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

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

Как вы понимаете, что все плохо? Вы говорите, если каждый коммит собирается и если все плохо — разработчик сразу все правит. Какие процедуры с коммитом проделываются?

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

сначала каждый коммит выкатывается и тестеры его смотрят? Т.е.

Мы одну практику из этих 12 пунктов сделали — ускорились. Ну не каждый, это практика. Но в идеале потом приходишь к этому и все работает. На самом деле это долгая и упорная работа, может быть в течении года. Да, нужны какие-то автотесты, хотя бы минимальный набор, о том что у тебя это все работает.

Вы сказали, что Докера у вас вроде бы нет, что же такое сервер? И вопрос поменьше: там на картинке App-сервер и так далее, вот что там мне интересно? Голая Java или что?

Где-то это Photon на винде (гейм-сервер), App-сервер — это Java-приложение на Томкате.

никаких виртуалок, никаких контейнеров, ничего? Т.е.

Ну Java это, можно сказать, контейнер.

И Ансиблом все это раскатывается?

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

Зависимость к компоненту или к сервису? А базу данных как деплоите?

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

А база у вас тоже железная или база где-то в облаке в Амазоне?

Есть маленькие, RDS — это уже не железная, виртуальная. Самая большая база железная, но есть и другие. Те маленькие сервисы, которые я показывал: чаты, лиги, общение с фейсбуками, кланы, что-то из этого — RDS.

Мастер-сервер — что он из себя представляет?

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

Вы сказали, что берете старые артефакты и заливаете — а что с данными происходит при этом? Я правильно понимаю, что на каждую выкатку вы пишите (если появились какие-то фичи) миграцию на обновление данных? Вы пишите миграцию на откат базы?

Да, пишешь ручками миграцию, а что делать. Это очень редкая операция с откатом.

Т.е. Как обновление сервера синхронизируется с обновлениями клиента? А сервер поддерживает и старую и новую версию? нужно вам новую версию игры выпустить — вы сначала обновите все сервера, потом обновятся клиенты?

Т.е. Да, мы разрабатываем c feature toggling и feature dimming. Можно абсолютно спокойно обновиться, увидеть то, что у тебя все работает, но фичу эту не включать. это специальная ручка, рычаг, который позволяет какую-то фичу включить потом. А когда ты уже клиента разогнал, тогда можешь фичидиммингом на 10% подкрутить, посмотреть, что все окей, а потом на полную.

у вас есть какой-то процесс для разработки? Вы говорите, у вас отдельно хранятся части проекта в разных репозиториях, т.е. Значит тесты, которые лежат отдельно нужно, как можно быстрее поправить. Если вы меняете сам проект, то у вас тесты должны упасть, потому что вы поменяли проект.

Эта схема с разными репозиториями очень хорошо работает, только если есть какое-то очень плотное общение. Я говорил вам про кита «плотное взаимодействие с тестировщиками». Для нас это не проблема, все друг с другом легко общаются, есть хорошая коммуникация.

в вашей команде репозиторий с тестами поддерживают тестировщики? Т.е. А автотесты отдельно лежат?

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

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

В любом случае ты будешь привязан к клиенту. Заставлять пользователей клиента каждые пять минут или каждый день новую версию скачивать — не айс идея. С клиентом история посложнее, у нас есть какая-то релизная тактика и мы ее придерживаемся. Классно, когда у тебя web-проект, в котором ты можешь хоть каждый день обновляться и ничего не нужно делать.

Есть ли какая-то автоматизация, разворачиваемая разработчиками? Ты рассказал про автоматизацию выкатки на продуктовые сервера, а (я так понимаю) есть еще автоматизация выкатки на тест — а что с dev-средами?

Единственное, это уже не железные сервера, а в виртуалке, но суть примерно такая же. Почти то же самое. При этом на том же самом Ansible мы написали (у нас Ovirt) создание этой виртуалки и накатку на нее.

У вас вся история хранится в одном проекте вместе с продовыми и тестовыми конфигами Ansible или оно отдельно живет и развивается?

Dev (devbox мы это называем) — это история, когда все в одном паке, а на проде — это распределенная история. Можно сказать, что это отдельные проекты.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть