Выпуск 14. Апрель 2014

Тестирование в Perl. Лучшие практики | Содержание | Атрибуты в Perl

Pjam — сервер сборки перловых приложений

Автором представлена собственная разработка для сборки приложений, написанных на языке Perl

Здравствуйте, меня зовут Алексей Мележик. В этой статье я хочу рассказать o pjam — сервере сборки приложений, написанных на Perl.

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

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

Однажды я узнал o разработке под названием pinto и поближе познакомился с этим продуктом (о нем в том числе пойдет речь в данной статье). Естественным образом ко мне пришла идея создания сборочного сервера на базе pinto, так появился pjam. Я являюсь автором данного продукта.

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

Итак, знакомьтесь — pjam.

Один раз собери — семь раз поставь

Немного предыстории.

Идея сборки приложений на выделенном сервере с последующей установкой на целевых машинах известна давно, это методика чаще всего применяется для ПО, написанного на языках C++ или Java. При данном подходе установка приложений происходит в два этапа. Первый — компиляция программы из исходных кодов на специальном сборочном сервере и получение так называемого дистрибутива, второй — собственно деплоймент (выкладка) готового дистрибутива и его конфигурация на целевых серверах. Преимуществом такого подхода является возможность многократно устанавливать единожды собранный дистрибутив на машинах с одинаковым окружением. Платой за такую архитектуру является необходимость поддержки окружения сервера, на котором происходит сборка, в соответствии с окружениями устанавливаемых серверов. Однако, такая задача вполне решаема при применении современных методов системного администрирования.

Таким образом, данный способ сборки и установки приложений может быть применен также для приложений, написанных на скриптовых языках, таких как Perl или Python.

Yet another build server

Итак, pjam — сервер сборки перловых приложений. Упрощенная схема работы сервера состоит в следующем: исходные коды забираются из системы контроля версий, компилируются вместе с зависимостями и упаковываются в единый архив. В итоге мы получаем дистрибутив, готовый к установке на соответствующих целевых машинах. Фактически все, что нужно админу для того, чтобы запустить приложение (я упускаю стадию конфигурации и прочие детали) — это закачать архив, распаковать его, добавить в PERL5LIB библиотеки, собранные в дистрибутиве:

wget http://your.pjam.server/projects/1/builds/273/artefacts/app.tar.gz && tar -xzf app.tar.gz && export PERL5LIB=app/cpanlib/lib/perl5

Здесь cpanlib/ — директория внутри дистрибутива, в которую установлены все зависимости.

Сборка и борьба с зависимостями

Описание принципов работы pjam хочется начать с описания ядра его функциональности, а именно — процесса сборки дистрибутива из исходных кодов. Самое сложное и неприятное в этом процессе, с чем сталкивается любой build-инженер, причем не важно, идет речь о сборке Java-приложений или любых других (Perl здесь не исключение), — это зависимости. Для управления зависимостями pjam использует pinto — перспективная разработка, уже используемая многими Perl-разработчиками для деплоймента приложений и управления CPAN-репозиториями. Но прежде чем говорить о специфике использовании pinto в pjam, немного расскажу о том, какие именно зависимости бывают в pjam-проектах.

CPAN-модули

Итак, в процессе создания сборок pjam также сталкивается с проблемой разрешения зависимостей. Под зависимостями здесь понимается два типа сущностей: первый тип зависимостей — уже известные многим Perl-программистам CPAN-модули. Как правило, такие модули лежат либо в публичных, либо в приватных CPAN-репозиториях. Pjam можно настроить на использование одного или нескольких таких CPAN-репозиториев, чтобы он «знал», откуда брать CPAN-модули1.

Проекты и компоненты

Второй тип зависимостей, обрабатываемых в pjam, — это части приложения, расположенные в виде исходного когда в системе контроля версий2. В предметной области pjam такие части называются компонентами. Очень часто, особенно в больших приложениях, удобно делить исходный код приложения на отдельные куски и размещать их по разным ресурсам системы контроля версий. В случае с subversion это могут быть отдельные проекты и/или отдельные директории одного проекта в репозитории, в случае с git — отдельные git-репозитории. Таким образом, в контексте pjam каждое собираемое приложение является проектом, который в свою очередь содержит упорядоченный список компонентов, каждый из которых представлен свои ресурсом в системе контроля версий. В процессе сборки pjam проходит по данному списку компонент за компонентом и делает сборку всего проекта. Минимальным требованием к содержимому компонента является наличие в его исходном каталоге в системе контроля версий правильного сборочного файла формата Build.PL или Makefile.PL3.

Как происходит сборка

Разрешение зависимостей

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

<получить компонент из системы контроля версий> && perl Build.PL|Makefile.PL && ./Build|make manifest && ./Build|make dist  && <добавить дистрибутив компонента в локальный репозиторий>

Полученный дистрибутив посредством pinto (система управления CPAN-репозиториями) добавляется в локальный pinto-репозиторий, при этом все зависимости (а также зависимости зависимостей, т.е. рекурсивно), объявленные в сборочном файле компонента также добавляются в репозиторий. В итоге компонент «превращается» в обычный CPAN-модуль, помещенный в локальный репозиторий.

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

Фаза компиляции

Второй фазой сборки проекта в pjam является собственно компиляция всех собранных зависимостей, уже находящихся в локальном репозитории5. Делается это очень просто. Дистрибутив каждого компонента устанавливается в локальную директорию сборки с помощью того же pinto:

pinto --root <локальный pinto-репозиторий> install -s <стек для сборки> -l '<локальная директория сборки>'

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

Обработка ошибок

Конечно же следует упомянуть о том, что на любой из фаз сборки могут возникнуть ошибки, начиная от недоступности системы контроля версий, неопределенными зависимостями, до ошибок компиляции со сторонними библиотеками или провалом модульных тестов, в этом случае pjam прерывает процесс сборки и выводит соответствующее сообщение в лог сборки. Детализация лога может быть также настроена в самом проекте.

Инкрементальные сборки

Одной из замечательных особенностей pjam-сервера является технология «инкрементальных» сборок. Процедура сборки, описанная выше, может повторяться из раза в раз, для одного и того же проекта, отражая тем самым изменения в коде, вносимые разработчиками и фиксирующими их в системе контроля версий. При инкрементальном сборочном процессе очередная сборка «наследует» состояние предыдущей в виде:

  • состояния локального pinto-репозитория на момент завершения предыдущей сборки;
  • локальной директории предыдущей сборки.

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

Что нам дает pinto?

Сама идея инкрементальных сборок не нова, но вся соль в том, что на низком уровне pjam используется pinto для сохранения состояния предыдущих сборок. Когда запускается очередная сборка, происходит следующие:

  • локальная сборочная директория копируется в новую сборку;
  • pinto-стек6, отражающий состояние репозитория на момент завершения предыдущей сборки, копируется в новый стек очередной сборки;
  • запускается сборочный процесс.

Не вдаваясь в специфику pinto, это означает, что при данной архитектуре возможно следующее:

  • Видеть, что именно обновилось в очередной сборке. Получается элементарным сравнением стеков предыдущей и текущей сборки.

    pinto diff build-previous-stack-id build-new-stack-id
  • Сравнивать две различные сборки. Получается элементарным сравнением стеков сборок.

  • Откатить состояние проекта до требуемой сборки, включая состояние локального репозитория и стека. Достигается просто копированием стека сборки, к которой требуется осуществить откат, в новую сборку.

    pinto new build-old-stack-id build-new-stack-id

Все операции со стеками достаточно дешевые (не требуют много времени для выполнения) и, что важно, уже реализованы в самом pinto, остается только правильно использовать в самом pjam.

Асинхронные сборки

Немного расскажу о том, как создаются сборки с точки зрения конечного пользователя pjam-интерфейса. Любая сборка, конечно же, занимает немалое время. Что бы не заставлять пользователя ждать, pjam использует технологию обработки асинхронных задач. Это означает, что сборки не выполняются мгновенно, а ставятся в очередь и затем обрабатываются асинхронным шедулером (delayed_job). Для пользователя интерфейса это означает, что ему не нужно ждать, пока сборка закончится, он добавляет сборку в очередь и по ее окончанию получает соответствующее уведомление через jabber или видит ее обновленный статус в интерфейсе.

Параллельные сборки

К сожалению, из-за специфики pinto (использование файловых блокировок при работе с репозиторием), выполнение сборок из очереди происходит последовательно. Фактически, параллельное выполнение сборок невозможно, хотя и технически реализуемо в самом шедулере.

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

Yet Another CI-сервер?

Отличия и схожести с каноническим сервером непрерывной интеграции.

Начнем с перечисления тех особенностей, которые любой CI-сервер должен предоставлять, и которые есть в pjam.

  • Интеграция с системой контроля версий — есть, но пока только для subversion. В будущем, возможно, будет добавлена поддержка git.
  • Уведомление о статусах сборок — происходит посредством jabber-клиента, настройки jabber-аккаунта и jabber-сервера задаются на странице конфигурации pjam. Каждый проект имеет уникальный список пользователей, которым будет приходить рассылка уведомлений.
  • Работа со сборками и артефактами — каждая успешная сборка в pjam порождает архив (т.н. артефакт), который может быть загружен для деплоймента. Есть функции удаления, «заморозки» сборки (защита от случайного удаления), к сборке можно добавить описание или присвоить статус релиза. Есть очень удобная функция загрузки дистрибутива последней успешной сборки в проекте.

Чего нет в pjam (по сравнению с тем же Jenkins).

  • Вызова сторонних задач (remote hooks) по факту завершения сборки. Функция не то что бы очень необходимая, пока не уверен, что хочу добавлять ее.
  • Ротации сборок.
  • Аутентификации. Хотя действия пользователей логируются и отображаются через интерфейс, всегда можно увидеть, с какого хоста (делается попытка преобразования ip-адресов в имена хостов) что и когда было изменено в конфигурации проекта, а также кто запустил сборку.
  • Автоматического опроса системы контроля версий на появление новых коммитов и запуска сборок по данному событию. Сборки инициируются явно через интерфейс, возможен запуск сборок в стиле RESTful API обычными клиентами curl или wget.

В общем и целом pjam не является многофункциональным CI-сервером, как тот же Jenkins, хотя имеет минимальный набор функций, позволяющий использовать его в процессах непрерывной интеграции. Я лично делаю сборки в pjam, а деплоймент совершаю посредством Jenkins, загружая дистрибутивы последних успешных сборок из pjam.

Документация по установке и дистрибутив pjam

Документация находится на странице проекта на GitHub — https://github.com/melezhik/pjam-on-rails. На данный момент pjam ставится получением кода из GitHub и далее настраивается и запускается как стандартное Rails-приложение. В будущем я рассматриваю вариант более конвекционной дистрибуции.

Откуда я взял идеи для pjam

Почему pjam написан на Ruby?

Обычно это первый вопрос, который задают люди услышав о pjam. На самом деле pjam — это всего лишь обертка вокруг pinto, ядро системы реализуется именно в нем. Pjam предоставляет интерфейс к сборщику. А так это web-интерфейс, мне было проще написать на Ruby on rails. Ну… и как сказал мой коллега yakudza — теперь осталось переписать pjam c Ruby на Perl (:

Заключение

Если вас заинтересовал данный продукт, вы можете легко установить его и начать им пользоваться. Pjam все еще находится в стадии разработки, без стабильного релиза, но тем не менее большинство функций оттестировано и их поведение достаточно стабильно. Ну и конечно, всегда есть возможность сообщить об ошибках или поучаствовать в разработке, заходите на https://github.com/melezhik/pjam-on-rails.

Алексей Мележик


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

  2. На данный момент pjam поддерживает только subversion, но в будущем автор может добавить поддержку и других систем контроля версий.

  3. Данный файл определяет процесс компиляции исходного кода, а также указывает на зависимости, которые требуются данному компоненту. Существует несколько известных перловых пакетов, с помощью которых можно создавать подобные файлы, например, Module::Build или ExtUtils::MakeMaker.

  4. К сожалению, термин дистрибутив явно перегружен при употреблении в технической литературе на данный момент. Под ним может пониматься в зависимости от контекста: дистрибутив всего приложения (об этом уже говорилось); дистрибутив CPAN-модуля (архив, загружаемый с CPAN-зеркала); любой исходный код, упакованный в архив.

  5. pinto-репозиторий является одновременно сущностью сугубо специфической для pinto, но в тоже время имеет интерфейс CPAN-репозитория.

  6. pinto-стек — это выборка подмножества версий модулей, лежащих в репозитории (CPAN index view).

    Стеки — очень мощное средство, позволяющее, например, всегда ставить требуемые версии модулей. Приближенной аналогией стеков могут быть ветви исходного кода в системе контроля версий. Стеки позволяют иметь несколько подмножеств CPAN-индексов в одном локальном репозитории. Подход, применяемый в pjam, — это создание нового стека под каждую сборку. Фактически это равноценно созданию мгновенного снимка репозитория для конкретной сборки.


Тестирование в Perl. Лучшие практики | Содержание | Атрибуты в Perl
Нас уже 1393. Больше подписчиков — лучше выпуски!

Комментарии к статье