Расскажу немного о разработке на моем текущем месте работы.
Мы используем гибкую методологию разработки (Agile), причем сначала мы практиковали Scrum, а сейчас мы работаем по Kanban. Изначально я сам довольно скептически относился к подобному мероприятию, но буквально через пару недель я убедился что это работает.
Описание подобных процессов разработки — тема большого поста, возможно даже не одного, к тому же, есть большое количество литературы в сети, поэтому я не буду здесь рассказывать что это такое, ограничусь лишь тем что эти методологии предполагает частые релизы, в случае скрама релиз происходит после завершения итерации (время итерации выбирается самой командой и обычно занимает от 1 до 3 недель), в случае с канбаном фича запускается в продакшон сразу после того, как она была сделана (ну и протестирована, конечно).
Частые релизы — это не просто хорошо, это отлично: налаженный и частый процесс релиза, избавляет разработчика от страха перед обновлениями, а заказчик видит прогресс.
Такой процесс разработки отлично накладывается на Continuous Integration. Про него тоже пишут книжки и длинные статьи, вкратце, — это выполнение частых автоматизированных сборок проекта для скорейшего выявления и решения интеграционных проблем (wikipedia).
Мы разрабатываем распределенную систему, в качестве backend(API) у нас perl, клиентский код пишется на javascript, в качестве сервера непрерывной интеграции — jenkins ci.
Для тех кто не знаком с ним объясню на пальцах что такое jenkins: это сервис с web-интерфейсом, где настраиваются задачи для сборки. Задача представляет собой пошаговую инструкцию что должен выполнить сервер для сборки какого-либо пакета, в нашем случае это rpm. Например, обычно это происходит так:
- сделать checkout из VCS (системы контроля версий);
- запустить тесты;
- скомпилировать и собрать необходимую структуру файлов;
- запаковать в rpm-файл;
- поместить в репозитарий.
Для создания rpm-пакета требуется написание spec-файла, который тоже представляет собой набор инструкций для сборки и установки пакета на конечной машине.
Итак: есть CI-сервер, на котором собирается rpm пакет, пакет потом попадает в yum репозитарий, и есть конечный сервер или кластер серверов, на которой данный пакет ставится.
У себя в проекте мы используем модули с CPAN — повторное использование кода это хорошо, но влечет за собой некоторые накладные расходы, за которые многие недолюбливают perl.
Зависимости
Рассмотрим пример: нам понадобилось использовать некий хэш-алгоритм, задача довольно тривиальная, берем модуль на CPAN и ставим в своё девелоперское окружение:
# cpanm Some::CPAN::Module
после чего пишем код:
use Some::CPAN::Module; sub encrypt { my $string = shift; return Some::CPAN::Module::mkHash($string); }
и покрываем его тестом:
ok(encrypt("test") eq Some::CPAN::Module::mkHash("test"), "encrypt function");
код работает, покрыт тестом, мы собой очень даже довольны. Комитим его и пытается собрать пакет. Сборка у нас настроена по уму: на стадии сборки пакета запускаются unit-тесты, что очень удобно: не проходят тесты — значит плохой код, собирать дальше нет смысла.
И тут неприятность: тест не проходит — на сборочном сервере нет необходимого модуля.
Или другая ситуация: тестов у нас нет, и этот код уезжает в пакете в репозитарий и получаем не стартующий сервис, но уже на продакшон-сервере, где ошибку отлавливать гораздо сложнее.
Для сборки и прохождения тестов добавляем в spec-файл зависимость:
BuildRequires: perl-Some-CPAN-Module
Для разворачивания на конечном сервере добавляем:
Requires: perl-Some-CPAN-Module
Если повезло, то нужный rpm уже есть в yum-репозитарии, если нет — нужно собрать пакет и поместить его в yum-репозитарий.
Итак, пакет в репозитарии, ставится на билд/продакшон-сервер, но одна проблема, у него другая другой версия: за время что мы писали код и тесты, автор модуля исправил критическую уязвимость в своем модуле и выпустил новую версию, которая вдруг почему-то стала не совместима с предыдущей, тест по-прежнему не проходит, сборка фейлится, сервис не стартует.
Решение: выясняем версию модуля с которой наш код работает, собираем rpm c этой версией и указываем точную версию модуля в speс-файле.
BuildRequires: perl-Some-CPAN-Module = 1.41 Requires: perl-Some-CPAN-Module = 1.41
Другая проблема может произойти, если у модуля с CPAN есть свои зависимости, например, Some::CPAN::Module зависит от Other::CPAN::Module, в результате на девелоперском окружении и на билд-сервере после сборок-установок пакетов версии опять разъехались и тест фейлится, сервис по-прежнему не стартует. И таких модулей в реальном проекте может быть не один десяток. В узких кругах это называется медузой зависимостей.
Решение: можно также отследить зависимости у зависимостей и все их указать в spec-файле, но это решение требует много сил и времени на отслеживание и последующую сборку. Которое при нашей методологии разработки лимитировано.
Возможно альтернативное решение: поднятие собственного зеркала CPAN, например, при помощи pinto но это является темой данной статьи.
Есть другое решение.
Основная проблема CPAN в том что его придумали довольно давно и каждый кто хотя бы раз выкладывал свой модуль на CPAN понимает о чем я пишу. К счастью, в последнее время для perl’а стали появляться современные инструменты.
Carton
Я хочу рассказать про инструмент, под названием Carton. Его автор, Tatsuhiko Miyagawa, является также автором кучи полезных утилит среди которых starman и cpanm.
Что такое Carton? Это менеджер зависимостей для перловых модулей. Написан он под впечатлением от Bundler для Ruby.
Суть его сводится к тому, что в директории проекта создается файл, в котором декларируются зависимости — cpanfile. В этом файле разработчик перечисляет список необходимых ему модулей и их версии:
requires 'Some::CPAN::Module', '1.45';
далее запускается команда:
carton install
и в директорию local/lib ставится необходимый модуль и также создается cpanfile.snapshot, в котором сохраняется информация об установленных с CPAN модулях. Этот файл нужно также добавить в VCS, чтобы в дальнейшем на билд-сервере при запуске carton install приехали зависимости из снапшота, тем самым гарантируя идентичность окружения.
Все что теперь нужно это добавить в @INC
директорию local/lib.
Можно указать точную версию, которая необходима или допустимый диапазон версий:
requires 'Some::CPAN::Module', '>= 1.41, < 1.46';
Более того можно указать модули, специфичные для разных окружений, например:
osname 'MSWin32' => sub { requires 'Win32::File'; }; on 'test' => sub { requires 'Test::More', '>= 0.96, < 2.0'; recommends 'Test::TCP', '1.12'; }; on 'develop' => sub { recommends 'Devel::NYTProf'; };
Carton работает в упряжке вместе с cpanm еще одной полезной утилитой того же автора, именно ей мы можем быть благодарны за непосредственную установку модулей.
Теперь когда все зависимости у нас имеются в cpanfile, снапшот девелоперского окружения в cpanfile.snapshot и все это помещено в VCS в spec-файле перед запуском тестов можно добавить команду carton install.
Эту же команду можно и добавить в стадию установки при разворачивании пакета на сервер для которого предназначается rpm-пакет.
Но мы пошли дальше.
У автора Carton’a видение работы его утилиты следующее: Carton ставится на все необходимые сервера, далее утилитой типа capistranо выполняется команда сразу на всех необходимых удаленных машинах:
# cap carton install
Немного странно, необходимо скачать из интернета или с локального зеркала CPAN весь ворох зависимостей, а этом может занимать довольно продолжительное время, а что если в этот момент возникли какие-то сетевые проблемы?
Есть более консистентный способ, мы можем выполнить установку модулей на этапе сборки rpm-пакета. И мы решили создать über-rpm-пакет, в который мы поместили сразу все зависимости. Этот пакет объявляется зависимостью пакета с кодом и он может быть поставлен как на билд-сервере для прохождения тестов при сборке, так и на продакшоне.
Такой подход популярен для проектов, например, на scala — создается über-jar в котором все зависимости проекта + код, на go пошли еще дальше — создают über-бинарник, в котором все зависимости уже вкомпилированы.
Теперь, когда мы настроили и автоматизировали процесс сборки, мы можем не испытывать страх перед релизами и медузой зависимостей, а сосредоточиться на алгоритмах и продукте который мы производим 🙂
ссылка на оригинал статьи http://habrahabr.ru/post/198376/
Добавить комментарий