Pull to refresh

Continuous Delivery hecho en Alawar

Reading time7 min
Views5.7K
Около года назад перед нашей командой была поставлена задача стартовать разработку серверных частей ряда игровых MMO проектов. Специфика такого рода проектов помимо требований к гибкости, стабильности и масштабируемости также включает в себя:

  • необходимость A/B-тестирования разных версий одной и той же игры
  • возможность по максимуму переиспользовать функциональность от одной игры в другой
  • высокую вероятность географической удаленности от разработчиков занимающихся клиентской частью игры

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

Цель данной статьи – рассказать о проделанных шагах, принятых решениях и описать полученный результат.

image


Инфраструктура


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

Итоговый список:


Модель ветвления


При выборе модели ветвления за основу была взята “A successful Git branching model”, описанная здесь с одним небольшим отличием: проводить A/B-тестирование было решено путем подготовки отдельных релизных веток, формирующихся из разного набора feature-веток. В результате роль ветки develop была полностью возложена на релизные ветки, и сама эта ветка исчезла. В противном случае при создании следующего релиза мы были бы вынуждены включать в него все выпущенные до этого feature, что не всегда являлось приемлемым.

Эту ситуацию можно продемонстрировать на следующем примере. Напомним, что согласно оригиналу:
At least all features that are targeted for the release-to-be-built must be merged in to develop at this point in time. All features targeted at future releases may not—they must wait until after the release branch is branched off.

И допустим, что уже выпущены два релиза – релиз 1.0 с фичей A и релиз 2.0 с фичами A и B, и необходимо выпустить релиз 1.1 с фичами A и C. Так как develop ветка на данный момент уже содержит в себе фичи А и B, то наиболее простым решением будет создание feature ветки С от ветки релиза 1, и последующий ее merge обратно:

image

Пакетирование и версионирование


Все проекты оформлены как composer-пакеты.

Для переиспользования функциональности от одного проекта к другому широко применяется выделение какой-то обособленой ее части в отдельный пакет.

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

Этот тип версионирования поддерживается в composer с использованием символа “~”, например:

 "require": {
        ...
        "alawar/packet-post-process-server": "~1.3",
        ...
    },

“Сборка” проекта


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

Этапы сборки:

  • выкачивание зависимостей через composer
  • миграция БД, — обновление только структуры и статических данных базы данных

Для реализации сборки в корне каждого проекта находится сборочный phing-скрипт с target'ами:

  • build – для выполнения этапов сборки
  • runtests и runtest-with-coverage – для выполнения как сборки так и запуска тестов и сбора метрик

Сборочный скрипт для большинства проектов одинаков и отличается лишь названием проекта: аттрибутом name, тега project.

Тестирование


Реализация автоматического тестирования проектов сделана при помощи двух фреймворков: Behat и PHPUnit.

Использование первого дает существенное преимущество не только для тестирования, но и для создания так называемой living documentation. Тесты на Gherkin являются одной из отправных точек при знакомстве с проектом нового программиста, при проведении code review, а также ряде других работ.

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

Сценарий: Получение пользовательского бонус кода и списка возможных наград
  # Пусть мы получили бонус код для какого-то игрока
  Пусть мы успешно отправили запрос:
	| action              | uid     |
	| get-user-bonus-code | player1 |
  Тогда мы получим ответ в соответствии с шаблоном "GetUserBonusCodeResponse.txt"
  # Тогда запросив список возможных наград мы получим награды за использование выбранного бонус кода
  Пусть мы успешно отправили запрос:
	| action           | code                 |
	| get-rewards-info | Полученный бонус код |
  Тогда мы получим ответ в соответствии с шаблоном "template8.txt"

до таких:

Сценарий: Получение и обработка данных
	Пусть веб-сервис приложения получает данные по отчетам
	И после этого запускается команда на обработку данных
	Тогда в БД появятся обработанные согласно схеме данные


PHPUnit используется не только для реализации unit-тестов, наличие и содержимое которых полностью остается за программистом, но и для запуска Behat тестов, с использованием небольшого workaround'а. Это дает возможность запускать все тесты одной командой, а также иметь единые отчеты по результатам работы тестов и покрытию ими кода.

Сборочный сервер


Сборка производится с использованием CI-сервера Jenkins. При этом для каждой релизной ветки releases/X.Y заведено отдельное сборочное задание, которое на staging среде:

  • выполняет сборочный phing-скрипт с target'ами “build runtests-with-coverage”
  • собирает отчеты тестирования и результатов работы вспомогательных утилит
  • в случае безошибочного завершения процесса создает в репозитории новый тег вида $VERSION_NO.$BUILD_NUMBER, где $VERSION_NO – номер версии, получаемый из названия ветки, например 2.1, а $BUILD_NUMBER – порядковый номер сборки для данного сборочного задания

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

image

В дополнение к указанному по ссылке выше списку плагинов были установлены:


Деплоймент


Требовалось найти решение позволяющее одновременно управлять развертыванием нескольких приложений, каждое из которых может быть установлено на несколько групп серверов (testing, production).

Первоначально deployment осуществлялся phing-скриптом, который согласно файлу настроек выполнял ряд действий:

  • создавал файлы, папки и симлинки
  • делал checkout исходников нужной ветки/тега
  • выполнял сборку проекта
  • и так как каждый раз checkout выполнялся в новую папку вида 2012-01-01T23:59:59, то обновлял симлинк latest, указывающий на последнюю развернутую версию

Это было не совсем удобно в силу полного отсутствия поддержки инсталляции на удаленные сервера.

После нескольких экспериментов с Capistrano, Magallanes и другими инструментами, в дополнение к этому скрипту было реализовано консольное приложение Installer. Оно копирует на нужную удаленную группу серверов инсталляционный скрипт с нужными настройками и выполняет его там.

Также в это приложение были заложены команды по получению возможных версий приложения и запросу установленной на серверах версии (на картинке показана возможность обновления проекта в production environment'е с версии 1.0.19 до 1.0.20):

image

А формат файлов настроек был заменен на более удобный .yml:

image

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

/home/projects/installer/installer.phar $command $recipe $environment

где,
  • $command — имя выполняемой команды, например install, status, versions
  • $recipe — код присваемый версии проекта, предназначенной для инсталляции
  • $environment — опциональное имя группы серверов, на которые необходимо установить проект

И это задание в свою очередь было отмечено как downstream project для сборочных заданий релизных веток с использованием плагина Parameterized Trigger Plugin.

В итоге


В итоге нами была успешно решена задача реализации continuous delivery со следующей последовательностью шагов:

  • разработчик вносит изменения в релизную ветку
  • post-receive hook gitolite инициирует соответствующее этой ветке сборочное задание Jenkins
  • сборочное задание проводит тестирование и помечает успешную версию тегом
  • Jenkins запускает downstream project Installer с нужными параметрами для проекта и группы серверов, на которых проект надо обновить
  • Installer, последовательно пройдя по всем серверам группы, разворачивает на них свежую версию и обновляет симлинк latest

В дальнейшем


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

Есть интерес попробовать различного вида интеграции с трекером Jira. Как-то например автоматизировать:

  • создание веток под новые тикеты определенного типа
  • обновление статуса и/или комментариев к тикетам в соответствии с результатами тестирования
  • формирование change log'ов

Текущее время работы composer'а составляет порядка нескольких минут, а большое количество собственных пакетов приводит к сильно разросшейся секции repositories файлов composer.json. Хочется поэкспериментировать с Satis, для решения этих проблем.

Заключение


Мы успешно решили поставленные перед нами проблемы:

  • для создания A/B-версий используются отдельные ветки в системе контроля версий
  • пакетный менеджер позволяет переиспользовать функциональность от проекта к проекту
  • тесты на Gherkin и их реализация помогают существенно упростить подключение к проекту новых разработчиков
  • а описанная выше схема continuous delivery позволяет минимизировать время получения фидбека от разработчиков клиентской части игры

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

Suerte!
Tags:
Hubs:
+15
Comments4

Articles

Information

Website
company.alawar.ru
Registered
Founded
Employees
201–500 employees
Location
Россия