Я пришел работать в крупную компанию студентом, в силу специфики прошлого опыта попал в отдел поддержки (NOC, Software Maintenance), в котором как раз в этот момент образовалась новая команда Build Engineering Team. Поначалу нас было всего трое, но в течение почти четырех лет команда выросла в целый отдел. Было накоплено огромное количество опыта, разработаны документы, практики и стандарты построения CI сред, а так же множество специфических решений для крупных заказчиков. Уже позже, пройдя несколько других компаний и став сначала программистом, потом лидером команды, и, наконец, проектным менеджером, я стал смотреть на процесс построения CI с несколько иной позиции.
Стандартное представление о Continuous Integration
Верхи и низы
В каждом проекте есть верхи и низы, руководство и исполнители. Все работают вместе, как одна команда, но претензии к процессу CI у всех разные. Часто случается так, что проектный менеджер считает процесс CI чем-то вроде украшения: «Ну, там, сделайте, чтобы сборка запускалась, когда новый код попал в репозиторий, и чтобы обязательно запускались тесты». Как это ни печально, многие менеджеры имеют слабое представление о преимуществах правильной и осмысленной непрерывной интеграции. Менеджер просто хочет отчитаться перед своим начальством и заказчиками, что «вот на моем проекте» все соответствует новомодным практикам Agile, Scrum, и так далее, нужное подчеркнуть. Это ласкает эго, успокаивает совесть (ну заказчику то обещали, что у нас все на высшем уровне) С другой стороны, программисты и тестеры воспринимают это как дополнительную, ненужную работу. Среду интеграции нужно настроить, а результаты автоматических тестов нужно разобрать и исправить ошибки. Менеджер ведь не сядет сам всем этим заниматься? Итого, верхи не могут, а низы не хотят. Как чаще всего решается эта проблема? Менеджер на одном из проектных митингов указывает пальцем на одного из членов команды и говорит: «Так ты, да-да ты, не прячься, я все равно достану тебя из-под стола, ты будешь билд инженером, ну ты знаешь, как это все должно быть. В общем, я хочу, чтобы картинка ожила». Знакомая ситуация? К чему это приводит, я расскажу чуть позже.Непрерывная интеграция
Если попробовать поискать в открытых источниках формулировку что же такое непрерывная интеграция, то большинство говорит, что это процесс, который позволяет автоматически собрать, протестировать и установить приложение. Основные компоненты подобной среды сборки:- Scheduler – это может быть Cruisecontrol (.Net), Hudson, Team City и так далее. Приложение, которое позволяет в автоматическом режиме по расписанию или по определенному событию запустить сборку, затем агрегировать результаты, разослать сообщения заинтересованным лицам
- Builder – Ant, Nant, make, maven и так далее. Собственно средство, которое умеет в правильном порядке запустить последовательность действий, чтобы в итоге получить собранное приложение
- 3rd party tools – сторонние приложения, которые автоматически тестируют, готовят численные характеристики вроде процента покрытия исходного кода юнит тестами, автоматически проверяют код на соответствие лицензиям (BlackDuck) и еще множество вещей
Непрерывная интеграция в оффшорной модели разработки
Жестокая реальность
Все непрерывно говорят об увеличении качества исходного кода, золотых стандартах, получаемых потрясающих результатах, но почти никто не говорит о реальных цифрах, затратах и прочем. Лично для себя я вывел два основных параметра непрерывной интеграции, которые имеют значение при оффшорной модели разработки.- Осмысленность – покрывает ли полученная прибыль затраты на создание и поддержку CI
- Правильность – в общем случае это баланс между проблемами, решаемыми с помощью CI и проблемами которые создает CI (правильная CI решает больше проблем, чем создает)
Стоимость
Попробуем разобраться в ситуации. Возьмем, для примера, случай описанный выше (небольшой проект, отсутствие серьезного опыта в вопросах CI). Я на сто процентов уверен, что в контракте четко не прописано время, которое будет потрачено билд инженером на разработку и поддержку среды. Как максимум, заложено часа два-три на изначальную подготовку (установка необходимого ПО, настройка серверов приложений на локальных компьютерах программистов и тд). Итого бюджет составляет примерно ноль. В данном примере возьмем стоимость человеко-часа равной 50 долларам. Давайте составим так называемый «profit and lost report». Lost (затраты):- Аренда выделенного сервера (аппаратного или виртуального) составляет от 50 до 200 долларов в месяц (это включает амортизацию, затраты на обслуживание, электричество, включая питание кондиционеров, аренду офисного помещения предназначенного для серверной комнаты). Поверьте, эти цифры взяты не с потолка, а весьма детально рассчитаны в масштабах достаточно большой компании, обладающей собственным офисом, штатом IT support, специально оборудованными комнатами для размещения серверов. Итого 1-4 человеко-часа
- Первоначальная настройка среды – для человека немного знакомого с CI около 4 человеко-часов
- • Поддержка среды – оптимистическая оценка равна 1 человеко-час в месяц
- Если CI создан больше для галочки (по моим подсчетам это треть проектов), прибыль составляет круглый ноль
- В проектах для мобильных платформ, где установка приложения не предполагается, а автоматические средства проверки кода либо отсутствуют, либо это только юнит тесты и подсчет покрытия, прибыль тоже в районе нуля
- Если CI среда умеет хотя бы устанавливать приложение на сервер, то возможно тестеры сэкономят время необходимое на ручную установку приложения. Этот случай сильно зависит от специфики самого проекта
- Если менеджер все-таки заботится о качестве кода и включает исправление ошибок найденных всевозможными Checkstyle, PMD в планы на следующую итерацию, то прибыль составит минус несколько человеко-часов (обсуждение возможной прибыли от исправления этих ошибок оставим за скобками). Ой, как же так, наверное, это стоило перенести в предыдущий список?
Мотивация
У каждого человека присутствует толика здоровой лени. А у программиста со стажем работы в 5-7 лет этой толикой можно железнодорожный вагон забить под завязку. Он уверен в себе, знает, что он делает, имеет набор укоренившихся практик, стиль написания и прочее. Просто взять и настроить на сервере кучу софта, которое проверит его код и скажет, что он в куче мест неправ, вызовет бурю негодования в стиле: «Да кто они такие!?». Ниже несколько способов, позволяющих решить часть проблем.Старая добрая диктатура
Натолкнулся на этот способ на одном интересном ирландском проекте. Большинство систем контроля версии имеют такую вещь, как pre-commit и post-commit hooks. То есть перед тем, как код реально попадает в репозиторий, над ним можно совершить какое-либо действие. Дальше проще, берем тот же PMD (Java/C++/Perl/etc source code checker) и натравливаем его на свежий код. Если он выдал хотя бы одну ошибку, завершить коммит нельзя. Интеграция сторонних приложений с серверной частью систем контроля версий реализуется достаточно примитивно. Негатив от такого решения можно смягчить, переложив ответственность на дядю, то есть, сказав, что это договоренность с заказчиком, и ничего не поделаешь. Тренируйте навык «глаза как у кота из Шрэка» перед зеркалом.Ground rules
Схож с предыдущим методом, но предполагает исключения. В самом начале проекта определяются правила, которым должны следовать все программисты при разработке, сулятся кары небесные на голову тех, кто не следует правилам. Обычно работает чуть хуже, чем диктатура, но вызывает меньше негатива.Положительная мотивация
Можно попробовать устроить соревнование внутри команды, между несколькими командами за право называться иконой качества. Победителям можно выделить бонус из бюджета. Плохо только то, что подобный метод работает для настоящей команды, а не толпы людей, собранных для работы над проектом (иначе будет не соревнование, а конфликт). И желательно, чтобы команда была высокопрофессиональной.Осмысленность и правильность непрерывной интеграции
Осмысленность
Как было показано выше, осмысленность присутствует, если менеджер и команда (да-да командиры, мы не одни на проекте работаем) видят смысл, реальную отдачу, полезность и прибыль от организации непрерывной интеграции. Как этого добиться? Перед началом проекта сесть и посчитать:- Каков размер нашего проекта, какое количество кода планируется написать?
- Насколько сложен процесс установки и тестирования полученного продукта, занимает ли он от силы пару минут, либо это сложный процесс, для которого отдельный документ впору писать?
- Есть ли у нас инженер, который способен правильно разработать среду?
- Какой бюджет нам доступен на разработку непрерывной интеграции? Стоит ли при нашем бюджете вообще начинать это, или полученный шедевр доктора Франкенштейна нам только все испортит?
Правильность
Чтобы вы лучше понимали мой критерий правильности, я обращусь к нескольким примерам из жизни. Оба проекта большие, проектная команда обоих больше 30 человек, и разработка идет не один год. Естественно, что такой срок вырабатывается набор правил, техник и процедур, используемых на проекте. Я хочу рассказать вам о той части этих процедур, которая связана с непрерывной интеграцией. Не хочу называть имен, названий – это неважно. Важен результат, который получился в одном и втором случае.Проект номер один
У меня закончился проект, и команду распределили по другим проектам. Один парень попал на большой долгострой, Java, несколько UI приложений, back-end, база данных, тысячи исходных файлов, тонны статических ресурсов. Поскольку мой клиент периодически находил мелкие проблемы в нашем приложении, я обращался к этому парню за помощью и между делом общался с ним. Через несколько дней он мне признался, что уже два дня мучается с Ant билдом и не может его завести. Я был свободен и решил тряхнуть стариной, подошел к его рабочему месту и попытался вникнуть в суть проблемы. И тут же испытал культурный шок. Конфигурационный файл Ant-а составлял больше шести тысяч строк. Если отбросить строк пятьсот на переменные (properties), пути (path-like structures), заголовки и пустые блоки, останется примерно пять с половиной тысяч строк XML. Примерный подсчет показывает, что в среднем одна функция (target) занимает 50-100 строк, то есть 50-100 различных функций. С учетом взаимных зависимостей между ними, Ant скрипт напоминал кастрюлю со свежесваренными спагетти. Дальше стало еще более интересно, Java машина жаловалась на недостаток памяти при запуске установки приложения. Увеличение размера кучи вплоть до максимума делу не помогло. Мы пригласили человека, который этот скрипт «дорабатывал», он пришел и посоветовал сначала запустить одну процедуру, в результате ничего не поменялось. Java машина продолжала ругаться на один специфический плагин. Поиск в Google показал, что у этого плагина проблемы со стабильностью, и есть зарегистрированные баги, связанные с ошибкой недостатка памяти. Ответственный за скрипт человек развел руками и ушел. «Помощь зала» (те опрос проектной команды) тоже не дала ничего, кто-то просто отмахивался, кто-то кивал на «ответственного». Вот к чему часто приводит техника «Эй ты, будешь ответственным за Continuous Integration»Проект номер два
Я участвовал в этом проекте более трех лет. Проект представлял собой целую платформу для бизнеса путешествий. Платформа включала в себя приложения для продажи билетов, бронирования отелей и автомобилей конечным пользователям; приложения для работы туристических агентств, call center, административную оболочку, приложение мониторинга лог файлов, несколько backend-ов, огромную базу данных. Для этой платформы был разработан специфический Ant-based framework, который позволял собирать приложения из маленьких обособленных кусочков, каждый из которых мог иметь зависимости на остальные модули. Так, например, к динамическому приложению продажи билетов (Tomcat war file) можно было подключить специализированный статический сайт, разработанный под конкретного заказчика; разные приложения могли быть собраны на разных версиях библиотек и так далее. В какой-то момент возникла проблема установки платформы на множество различных серверов. У каждой подкоманды было свое приложение (QA, Development, Staging-Demo). Приложение устанавливалось на разные сервера, при каждой установке требовалось на лету менять параметры в конфигурационных файлах. Содержать десятки небольших, пусть даже хорошо структурированных и документированных Ant скриптов, было попросту неудобно. К тому же, программисты не могли воспользоваться этими скриптами локально, они работали только на сервере. Я решил сделать на базе Ant-а несколько специфичных функций (tasks), которые бы позволили производить установку всей платформы, опирались на простой в написании и понятный конфигурационный файл, были достаточно умны, чтобы отслеживать зависимости между отдельными приложениями платформы и производить установку на локальные или удаленные сервера приложений. К сожалению, имплементация является проприетарной, но конфигурационный XML файл содержал простейшие тэги и был понятен абсолютно всем. Сервера приложений и пакеты установки: <appserver name="tomcat" type="tomcat"> <deploymentpackage name="static" dedicateddeployment="true" appserver="apache"> <deploymentpackage name="backend" dedicateddeployment="false" appserver="jboss"> Возможность налету менять файлы внутри пакета: <deploymentpackage name="ui" dedicateddeployment="true" appserver="tomcat" depends="static,backend"> <item name="tomcat/webapps/site.war" type="war" relativedeploymentpath="webapps/site.war"> <subitem name="WEB-INF/classes/Default.xml" type="plain"> <replace token="@@@webservicehostaddress@@@" value="server"/> </subitem> </item> </deploymentpackage> Программистам не всегда нужно устанавливать всю платформу сразу, но ненужные части можно просто закомментировать. В Ant скрипте вызов специфической функции (task), которая проводила всю установку, занимал одну строчку, подобных конфигурационных файлов могло быть до десятка, и они передавались в Ant динамически. Это решение легко было использовать как мне на основном сервере CI, так и программистам локально, и иногда даже тестерам. Причем знание Ant было необязательным, достаточно было правильно оформить конфигурационный файл. Второй пример более сложный. При таком числе UI приложений количество регрессионного тестирования просто зашкаливало. Целая бригада тестеров целыми днями нажимала одни и те же кнопки, пристально вглядывалась в те же самые формы на экране. «Федор, уже нужно что-то делать», - высказалось начальство. Примерно день мы потратили на разработку концепции автоматического тестирования, собрали информацию о необходимых сторонних приложениях, их возможностях и так далее. Выбрали JMeter в качестве средства автоматизированного тестирования, нашли пути обхода его ограничений. Перед каждым тестом необходимо было выполнить предварительные действия (как-то: создать профиль пользователя перед входом в систему), а после теста выполнить чистку (удалить тестовый профиль). JMeter не умел выполнять связанные зависимостями группы тестов; поддерживал файл конфигурации для тестов (чтобы, например, на лету подставить URL приложения или данные формы), но был не в состоянии автоматически поддерживать несколько разных конфигураций; производил отдельные, слабо читаемые HTML файлы отчетов на каждый отдельный тест. Framework:- Позволяет делать многоуровневую группировку тестов (Pre-Test / Post Test / Main Test -->Test --> Use Case --> UI Application)
- Позволяет подключать конфигурационные файлы динамически на разных уровнях группировки
- Агрегирует полученные результаты в единый HTML файл (древовидное представление результатов плюс общая статистика). Дополнительно в отчет включаются лог файлы серверов приложений
- Интеграция с общей средой сборки проекта
Стоимость внедрения непрерывной интеграции
В этом разделе я попробую в человеко-часах описать стоимость внедрения правильной непрерывной интеграции и расскажу что для этого необходимо.Команда
Для любого дела необходима стабильная, профессиональная команда. Когда люди приходят и уходят, не приобретая значимого опыта, процесс становится слабоуправляемым и непредсказуемым. В состоянии ли инженер воплотить сложную, многофункциональную среду, если он до этого написал пару простейших билд файлов? Я сильно сомневаюсь. Скорее всего, в результате мы получим неказистого монстра, слабо шевелящего уродливыми конечностями. Первое необходимое вложение – это один или несколько человек, которые готовы стать ядром команды билд инженеров. Наилучшими в этом случае являются инженеры NOC, которые обладают знаниями из многих областей, как-то программирование, администрирование операционных систем, тестирование. Но это не является строго необходимым, скорее решает мотивация и желание достичь результатов. Необходимо освободить этих людей насколько это возможно от их текущих обязанностей и поставить им задачу.Задача
Первая задача и цель – это построение абстрактной среды непрерывной интеграции. Для Java и Android проектов она может быть полностью унифицирована. В принципе скриптовые языки (PHP, Python, Perl) тоже могут использовать схожую структуру среды (структура каталогов в системе контроля версий, базовые алгоритмы сборки проекта). Например:- lib – директория с любыми сторонними библиотеками, необходимыми во время сборки
- resources – директория с ресурсами, конфигурационными файлами
- tools – набор подключаемых библиотек функций (например, ant скрипты с динамическими функциями macrodef)
- vcs – репозиторий исходного кода
- build.xml – корневой Ant скипт с набором стандартных процедур (targets)
- Nightly – полная сборка, тестирование и установка на выбранные среды (Dev, QA, Staging)
- Automatic – автоматическая сборка проекта (возможно без установки) для верификации стабильности, обычно срабатывает при обновлении репозитория исходного кода
- Forced – сборка, которую можно запустить принудительно, предназначена для выборочной проверки и\или установки приложения, когда это необходимо
Релоцировались? Теперь вы можете комментировать без верификации аккаунта.