- PVSM.RU - https://www.pvsm.ru -
Около года назад перед нашей командой была поставлена задача стартовать разработку серверных частей ряда игровых MMO проектов. Специфика такого рода проектов помимо требований к гибкости, стабильности и масштабируемости также включает в себя:
Более того, в дальнейшем нашу команду предполагалось расширить, возможно за счет аутсорс разработчиков, в том числе и для задач поддержки. В этих условиях для успешной реализации было решено наравне с версионированием проектов, пакетированием и стандартизацией ряда шагов разработки внедрить и практику continuous delivery [1].
Цель данной статьи – рассказать о проделанных шагах, принятых решениях и описать полученный результат.

Исторически сложилось, что основным языком разработки серверных веб-приложений в нашей компании является PHP, поэтому это во многом предопределило выбор инструментов.
Итоговый список:
При выборе модели ветвления за основу была взята “A successful Git branching model”, описанная здесь [16] с одним небольшим отличием: проводить 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 обратно:

Все проекты оформлены как composer-пакеты.
Для переиспользования функционала от одного проекта к другому широко применяется выделение какого-то обособленого функционала в отдельный пакет.
Это сопровождается заменой одного пакета другим, разделением одного пакета на два или переносом функционала из одного пакета в другой. В таких условиях для более тонкого контроля было используется семантическое версионирование [17] пакетов.
Этот тип версионирования поддерживается в composer с использованием символа “~”, например:
"require": {
...
"alawar/packet-post-process-server": "~1.3",
...
},
В случае с PHP, говорить о сборке в классическом смысле, как процессе конвертации исходников проекта в исполняемый код, нельзя. Тем не менее, так как основной задачей по-прежнему является получение готового к использованию ПО, то название “сборка” вполне корректно.
Этапы сборки:
Для реализации сборки в корне каждого проекта находится сборочный phing-скрипт с target'ами:
Сборочный скрипт для большинства проектов одинаков и отличается лишь названием проекта: аттрибутом name, тега project.
Реализация автоматического тестирования проектов сделана при помощи двух фреймворков: Behat и PHPUnit.
Использование первого дает существенное преимущество не только для тестирования, но и для создания так называемой living documentation [18]. Тесты на Gherkin являются одной из отправных точек при знакомстве с проектом нового программиста, при проведении code review, а также ряде других работ.
Несмотря на знакомство с материалами тут [19] и тут [20], единых рекомендаций относительно глубины и детальности этих тестов у нас нет, поэтому их содержимое может варьироваться от например таких:
Сценарий: Получение пользовательского бонус кода и списка возможных наград
# Пусть мы получили бонус код для какого-то игрока
Пусть мы успешно отправили запрос:
| action | uid |
| get-user-bonus-code | player1 |
Тогда мы получим ответ в соответствии с шаблоном "GetUserBonusCodeResponse.txt"
# Тогда запросив список возможных наград мы получим награды за использование выбранного бонус кода
Пусть мы успешно отправили запрос:
| action | code |
| get-rewards-info | Полученный бонус код |
Тогда мы получим ответ в соответствии с шаблоном "template8.txt"
до таких:
Сценарий: Получение и обработка данных
Пусть веб-сервис приложения получает данные по отчетам
И после этого запускается команда на обработку данных
Тогда в БД появятся обработанные согласно схеме данные
PHPUnit используется не только для реализации unit-тестов, наличие и содержимое которых полностью остается за программистом, но и для запуска Behat тестов, с использованием небольшого workaround [21]'а. Это дает возможность запускать все тесты одной командой, а также иметь единые отчеты по результатам работы тестов и покрытию ими кода.
Сборка производится с использованием CI-сервера Jenkins. При этом для каждой релизной ветки releases/X.Y заведено отдельное сборочное задание, которое на staging среде:
Само сборочное задание, равно как и сборочный скрипт были построены на основе описанных здесь [22]. Именно этим и обусловлен столь богатый список дополнительных утилит.

В дополнение к указанному по ссылке выше списку плагинов были установлены:
Требовалось найти решение позволяющее одновременно управлять развертыванием нескольких приложений, каждое из которых может быть установлено на несколько групп серверов (testing, production).
Первоначально deployment осуществлялся phing-скриптом, который согласно файлу настроек выполнял ряд действий:
Это было не совсем удобно в силу полного отсутствия поддержки инсталляции на удаленные сервера.
После нескольких экспериментов с Capistrano [26], Magallanes [27] и другими инструментами, в дополнение к этому скрипту было реализовано консольное приложение Installer. Оно копирует на нужную удаленную группу серверов инсталляционный скрипт с нужными настройками и выполняет его там.
Также в это приложение были заложены команды по получению возможных версий приложения и запросу установленной на серверах версии (на картинке показа возможность обновления проекта в production environment'е с версии 1.0.19 до 1.0.20):

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

Данное консольное приложение было развернуто на сборочном сервере, к нему был сделан веб-интерфейс в виде параметризуемого сборочного задания Jenkins, выполняющего консольную команду:
/home/projects/installer/installer.phar $command $recipe $environment
где,
И это задание в свою очередь было отмечено как downstream project для сборочных заданий релизных веток с использованием плагина Parameterized Trigger Plugin [28].
В итоге нами была успешно решена задача реализации continuous delivery со следующей последовательностью шагов:
Используемая модель ветвления способствует тому, что разные ветки со временем начинают сильно отличаться друг от друга, это приводит к проблемам внедрения новых фич в старые релизы. Пока это не стало критичным, но есть мысль попробовать вернуть интеграционную ветку develop, а для подготовки A/B-версий использовать другую технику, возможно, что-то навроде feature toggles [29].
Есть интерес попробовать различного вида интеграции с трекером Jira. Как-то например автоматизировать:
Текущее время работы composer'а составляет порядка нескольких минут, а большое количество собственных пакетов приводит к сильно разросшейся секции repositories файлов composer.json. Хочется поэкспериментировать с Satis [30], для решения этих проблем.
Мы успешно решили поставленные перед нами проблемы:
Следует заметить, что данная схема почти не используется для фактического обновления проектов в production, так как не покрывает всех проблем выпуска нового и возможно не совместимого с предыдущей версией релиза. Ее основное применение быстрая и автоматизированная доставка нового функционала на все сервера, где развернуто приложение, и обновление в тех местах, где это возможно, либо не критично – тестовые и предназначенные для закрытого бета-тестирования сервера.
Suerte!
Автор: grelkin
Источник [31]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/php-2/48404
Ссылки в тексте:
[1] continuous delivery: http://en.wikipedia.org/wiki/Continuous_delivery
[2] Git: http://git-scm.com/
[3] Gitolite: https://github.com/sitaramc/gitolite/
[4] Composer: http://getcomposer.org/
[5] Phing: http://www.phing.info/
[6] Jenkins: http://jenkins-ci.org/
[7] phpunit: https://github.com/sebastianbergmann/phpunit/
[8] behat: http://behat.org/
[9] phploc: https://github.com/sebastianbergmann/phploc
[10] phpmd: http://phpmd.org/
[11] pdepend: http://pdepend.org/
[12] phpcs: http://pear.php.net/package/PHP_CodeSniffer/
[13] phpcpd: https://github.com/sebastianbergmann/phpcpd
[14] phpcb: https://github.com/Mayflower/PHP_CodeBrowser
[15] phpdox: https://github.com/theseer/phpdox
[16] здесь: http://nvie.com/posts/a-successful-git-branching-model/
[17] семантическое версионирование: http://semver.org/
[18] living documentation: http://vimeo.com/43612884
[19] тут: http://www.elabs.se/blog/15-you-re-cuking-it-wrong
[20] тут: http://blog.josephwilk.net/ruby/telling-a-good-story-rspec-stories-from-the-trenches.html
[21] workaround: https://gist.github.com/jakzal/1298503
[22] здесь: http://jenkins-php.org/
[23] EnvInject: https://wiki.jenkins-ci.org/display/JENKINS/EnvInject+Plugin
[24] ChuckNorris: https://wiki.jenkins-ci.org/display/JENKINS/ChuckNorris+Plugin
[25] CI-Game: https://wiki.jenkins-ci.org/display/JENKINS/The+Continuous+Integration+Game+plugin
[26] Capistrano: https://github.com/capistrano/capistrano
[27] Magallanes: https://github.com/andres-montanez/Magallanes
[28] Parameterized Trigger Plugin: https://wiki.jenkins-ci.org/display/JENKINS/Parameterized+Trigger+Plugin
[29] feature toggles: http://en.wikipedia.org/wiki/Feature_toggle
[30] Satis: http://getcomposer.org/doc/articles/handling-private-packages-with-satis.md
[31] Источник: http://habrahabr.ru/post/202214/
Нажмите здесь для печати.