Непрерывная интеграция

Материал из CustisWiki
Перейти к: навигация, поиск

Репликация: База Знаний «Заказных Информ Систем» → «Непрерывная интеграция»

Любые правки этой статьи будут перезаписаны при следующем сеансе репликации. Если у вас есть серьезное замечание по тексту статьи, запишите его в раздел «discussion».

Коротко о главном

Термин

Непрерывная интеграция

Синонимы

Паттерн

English

Continuous Integration (CI)

В коде

Значение

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

Непрерывная интеграция в Custis
  • В проектах Custis, реализуемых на Java, в качестве сервера CI используется Jenkins.

Непрерывная интеграция

Введение

«Continuous Integration» — это лекарство от страха. Помогает при программировании.
                                         Dr. Zoidberg ©

Согласно Wikipedia термин Continuous Integration введен Мартином Фаулером (Martin Fowler) и Кентом Беком (Kent Beck). Данный термин был придуман ими для обозначения практики частой сборки (интеграции) проекта. Максимально частая сборка является логичным продолжением цепочки

итерационные сборки -> ночные сборки -> непрерывная сборка

В настоящее время Continuous Integration (непрерывная интеграция) одна из практик применяемых в семействе гибких (Agile) методологий. В подобных методологиях она удачно сочетается с другими практиками, такими как модульное(unit) тестирование, рефакторинг, стандарт кодирования. Но даже без них можно получить пользу от непрерывной интеграции.

Основные принципы

Каждое изменение должно интегрироваться

Слово continuous в термине Continuous Integration означает «непрерывный/непрекращающийся». Это означает, что в идеале сборка вашего проекта должна идти буквально все время. Каждое изменение в системе контроля версий (например CVS) должно интегрироваться без пропусков или задержек. Организация ночных сборок — это хорошая практика, но это не continuous integration. Ведь результаты такой ночной сборки будут доступны лишь на следующий день, когда их актуальность для разработчиков уже значительно снижена. На практике довольно часто реализуют оба процесса и непрерывную интеграция и ночные сборки — более редкую интеграцию. В очень крупных проектах это требование иногда невозможно соблюсти, но интеграция каждые сутки это предел за который не стоит уходить. Принцип непрерывной интеграции не выполним без другого условия — «Сборка должна идти быстро».

Быстрая сборка

«Сборка должна идти быстро» — точнее не более 10 минут. Если после одного небольшого коммита ваш интеграционный сервер будет уходить в 2-х часовое пыхтение на сборку, тестирование и разворачивание от этого будет мало пользы. Разработчики будут уже далеко, над решение других проблем, им будет сложно вернуться и понять причины сбоя, если таковой был. Ведь суть непрерывной интеграции в получении быстрого feedback. Вдобавок, поздний ответ с сервера может отвлечь их от другого дела.

В случае если все этапы процесса никак не удается втиснуть в приемлемые временные рамки можно разделить его на несколько частей. При каждом коммите производить лишь саму сборку и минимальный набор тестов (smoke tests), чтобы уменьшить время. А по ночам проводить полный цикл интеграции, результаты которого команда будет анализировать с утра. Но это скорее вынужденная мера, а не пример для подражания.

Сделайте тесты

Тесты просто необходимо включать в continuous integration процесс, в противном случае вы не можете быть уверены в качестве и работоспособности своего проекта. Чем тестов больше, тем лучше, в разумных пределах конечно. Основными двумя ограничителями на количество тестов будет:

  • время интеграции — сборка по-прежнему должна оставаться быстрой, основное тестирование можно перенести «на ночь»,
  • наличие автоматизированных тестов — не все тесты требуют автоматизации, нет смысла делать автоматизированные тесты только для самих тестов, они должны быть целесообразны.

Чем лучше ваши тесты, тем раньше находятся ошибки и раньше исправляются. Как известно, чем раньше ошибка исправлена, тем дешевле ее исправление. Это одно из основных преимуществ практики непрерывной интеграции — снижение стоимости исправления ошибок (не всех конечно). Попутно наличие хорошего набора тестов в процессе интеграции дает больше уверенности в том, что проект работает правильно.

Именнно присутствие тестов одно из отличий интеграции от нажатия кнопки Build в вашей любимой IDE.

Интеграция на специальной машине

Организовывать процесс необходимо на специально выделенной машине. Такая машина по своей конфигурации и набору прикладных программ должна максимально соответствовать окружению в котором проект будет развернут (production enviroment). Очевидно, что полного совпадения достичь практически невозможно — маловероятно, что эксплуатироватся программа будет на машине с установленными средствами сборки, тестирования и проч. Но точное совпадение версий операционных систем (и сервис паков) необходимо.

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


Методы

Continuous Integration сервер

Хотя в принципе практика continuous integration не требует никакого технического и программного обеспечения, гораздо удобнее, проще и дешевле наладить процесс с использованием таких средств. Такие средства называются сервера интеграции (continuous integration server)- специализированные приложения для автоматизации данного процесса.

Наиболее известный из серверов интеграции пожалуй CruiseControl. CruiseControl это сервер для интеграции приложений на java, написанный на java. Так же широко распространен его собрат (точнее портированная версия) под .NET — CruiseControl.NET.

Ниже приведена схема организации такого сервера интеграции: [svg]

Ручной процесс

Хотя решение с выделенным сервером для continuous integration кажется простым и дешевым, у него есть противники. Точнее, сторонники ручного процесса. Один из таких Джеймс Шор (James Shore) в своей статье Continuous Integration on a Dollar a Day пишет, как правильно организовать continuous integration процесс без специализированных приложений, вроде CruiseControl. В этой статье мы не касаемся данного вопроса.

Процесс интеграции

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

  • Trigger — обязателен
  • Update — обязателен
  • Analyse — не обязателен
  • Build — а как без него?
  • UnitTest — крайне желателен
  • Deploy — нужен по обстоятельствам
  • Test — не обязателен, но крайне желателен
  • Archive — желателен
  • Report — обязателен

Trigger

Цикл интеграции начинается со срабатывания триггера. Это может быть одно из следующих событий:

  • изменение в системе контроля версий
  • изменение в файловой системе
  • определенный момент времени
  • сборка другого проекта
  • нажата «красная» кнопка
  • изменение на веб-сервере

Стоит отметить, что не все CI сервера поддерживают все возможные варианты триггеров, но основные (система контроля версий и файловая система) поддерживаются большинством.

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

Update

На данном этапе CI сервер делает update своей локальной копии исходного кода проекта. В процессе update выясняются изменения в коде (и не только) произошедшие с последней интеграции. Выяснение изменений необходимо для того, чтобы в случае сбоя можно было легко выяснить причину и найти ответственного.

Analyse

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

  • наличие типичных ошибок
  • статические характеристики кода: сложность, размер, прочее
  • соответствие принятым стандартам кодирования
  • другое

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

Build

Один из основных этапов процесса это сборка проекта. Здесь происходит компиляция (трансляция) исходных кодов в исполнимые файлы или какой-то другой результат. Поскольку сервер интеграции представляет собой специально выделенную машину (смотрите здесь) со строго определенной конфигурацией, результат только этой сборки можно считать конечным. Больше никаких «Проект собирается на моей машине!». Есть только одно место, где проект может собираться — это интеграционный сервер.

Естественно сборка является обязательным этапом интеграции.

UnitTest

В методологии Extreme Programming модульное (unit) тестирование является неотъемлемой частью разработки приложения. Модульные тесты изначально автоматизированы, их включение в процесс интеграции крайне желательно. Поскольку часто у разработчиков нет времени или желания запускать такие тесты до того как изменения отправлены в систему контроля версий, дополнительное их исполнение никогда не будет лишним. Дополнительную информацию можно извлечь, измеряя покрытие модульных тестов. Эта метрика поможет лучше контролировать качество выпускаемого продукта.

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

Deploy

После того как мы убедились в некоторой работоспособности проекта — он собирается (этап Build) и все модульные тесты проходят (UnitTest) проект необходимо «развернуть». В случае веб-приложения это выкладывание на веб-сервер (сервер приложений) и запуск. Для GUI приложений это (пере)установка в системе.

Этап развертывание должен проходить как можно более «чисто», подробнее смотрите здесь. При этом для последующего тестирования часто необходимо привести приложение в некое «стандартное» состояние:

  • «залить» дамп базы
  • настроить в стандартном режиме
  • убрать следы предыдущей деятельности приложения

Test

После того ка приложение «развернуто» необходимо его протестировать. Здесь имеются ввиду автоматические функциональные тесты, иначе говоря на данном этапе проводится регрессионное тестирование.

После прохождения регрессионных тестов можно считать, что интеграция прошла успешно и в проект не внесено правок, которые могут привести к его неработоспособности (здесь все зависит от вашего набора тестов модульных и функциональных). В противном случае интеграция не успешна — код содержит ошибки и требуется его исправление/доработка.

Тестирование это один из «фатальных» этапов процесса, ошибка на котором означает сбой сборки. Всего есть несколько таких «фатальных» этапов:

  • Build — проект не собирается
  • UnitTest — модульные тест не прошли или покрытие упало ниже заданного уровня
  • Test — регрессионные тесты не прошли или покрытие упало ниже заданного уровня

Иногда к ним присоединяют этап Analyse — если в коде обнаружено несоответствие стандартам кодирования, то это является ошибкой.

Archive

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

Continuous integration процесс можно использовать как формализацию процесса передачи версии проекта на тестирование. К примеру можно настроить сервер публиковать свежую версию каждые две недели и сообщать об этом тестировщикам по электронной почте. Тестировщики всегда будут знать откуда брать свежую и «правильную» версию. А наличие регрессионных и модульных тестов является своего рода первичным (smoke) тестированием и гарантирует (в некоторой степени конечно) работоспособность данной версии. Таким образом на тестирование не попадет версия, которая имеет существенные недостатки препятствующие тестированию.

Report

В конце идет важный этап генерации и публикации отчетов. Отчеты включают в себя следующее:

  • причина сборки — например изменения в репозитории
  • изменения в исходных кодах — здесь возможны два варианта изменения от последней сборки или от последней успешной сборки
  • отчеты по статическому анализу кода — все результаты какие есть
  • лог сборки
  • лог модульных тестов — какие тесты прошли и, что важнее, какие не прошли
  • лог регрессионных тестов — аналогично модульным тестам
  • статистика сборок проекта:
    • общее число удачных/провальных сборок
    • распределение удачных/провальных сборок во времени
    • статистика результатов статического анализа кода
  • все другие метрики используемые и собираемые в проекте — это поможет менеджеру проекта видеть все и сразу

Механизм публикации отчета может быть разный и даже не один. Это может быть IRC или jabber бот, рассылка по электронной почте, публикация на web или ftp сервере, специализированные клиенты позволяющие узнать статус сборки.

Наиболее эффективна публикация результатов несколькими различными методами сразу. Например рассылка короткого письма команде, только в случае провала сборки, и публикация полного отчета на веб сервере.

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

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

Профиты

Так какую пользу можно получить о внедрения непрерывной интеграции в своем проекте?

В первую очередь это безболезненная интеграция всего проекта. Интеграция различных модулей и правок разных программистов перестает быть делом в принципе, она происходит «сама» без участия людей и если что-то не так, вы об этом узнаете. Конечно, сейчас редкость, что проект имеет особую стадию интеграции, когда из кучи разных модулей пытаются сделать приложение, но все же не надо недооценивать пользу от непрерывной интеграции.

Больше никаких «Это работает на моей машине!». Если что-то не работает на сборочном сервере — значит оно не работает вообще. Аргументы программиста, что у него все работает в данном случае не помогут. Сервер интеграции становится судьей в таких вопросах и этот судья беспристрастен.

Все анализаторы кода и тесты, которые вы используете и написали, обязательно запускаются над каждой сборкой. Если в систему контроля версий попал «плохой» код — вы об этом узнаете. И не важно, нарушен ли один из стандартов кодирования, или статический анализатор кода показывает, что в код попала потенциальная ошибка или тесты не прошли, а может просто покрытие кода модульными тестами упало ниже необходимого минимума. Вы об этом узнаете и сможете принять меры.

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

Чем больше и серьезней проведена работа по настройке сервера интеграции, тем больше пользы можно получить. Если ваш сервер просто собирает проект после каждого изменения в коде, то польза от него не так велика, но и усилий на него почти не потрачено.

Continuous Improvement

После того как вы наладили процесс непрерывной интеграции вам может показаться, что дело сделано: сервер работает, билды собираются, почта идет и все хорошо, пока нет никаких ЧП (вроде поломки сервера). Но это не так. Сам процесс требует постоянной наладки, подстройки. Если сначала у вас не было никаких тестов, то их нужно сделать. После вы захотите собирать информацию о покрытии вашего приложения тестами, затем изменение такого покрытия во времени, возможно какие-то еще специфичные метрики. Ну, а если оказалось, что больше улучшать нечего, подождите какое-то время и такая необходимость появится.

Ссылки

Публикации

  • www.pragprog.com — книга «Pragmatic Project Automation». Глава, которая посвящена непрерывной интеграции доступна бесплатно.
  • www.amazon.com — книга Пола Дюваля (Paul Duvall) о непрерывной интеграции, получившая Jolt Award в 2008 году
  • www.ozon.ru — эта же книга изданная на русском языке

Инструменты

Сервера интеграции

  • CruiseControl — сервер интеграции для Java (см. так же CruiseControl).
  • ThoughtWorks Cruise — коммерческий сервер интеграции от компании ThoughtWorks (есть бесплатная версия).
  • CruiseControl.NET — сервер интеграции для .NET (см. так же CruiseControl.NET)
  • CruiseControl.rb — сервер интеграции для Ruby.
  • Hudson — open-source сервер интеграции, создан как альтернатива CruiseControl. Функциональность расширяется плагинами.
  • Bitten — open-source сервер интеграции написанный на Python, интегрируется с Trac.
  • TeamCity — коммерческий сервер интеграции от компании JetBrains для java и .NET (есть бесплатная версия).

Инструменты сборки

  • Ant — средство сборки для Java
  • Maven — средство сборки для Java
  • NAnt — аналог Ant под .NET

Статический анализ

  • PMD — анализ кода Java
  • Findbugs — анализ кода на типичные ошибки
  • Simian — поиск повторов (copу+paste) в коде Java
  • FXCop — анализ кода .NET
  • QALab — объединение логов нескольких инструментов анализа кода, сбор статистики
  • Panopticode — объединение логов нескольких инструментов анализа кода для Java, графическое представление результатов

Модульное тестирование и покрытие

  • JUnit — де-факто стандарт модульного тестирования Java
  • TestNG — инструмент нового поколения для модульного тестирования Java
  • NUnit — модульные тесты для приложений .NET
  • Cobertura — измерение покрытия кода модульными тестами для Java
  • Clover — анализ покрытия кода тестами для Java
  • Clover.NET — анализ покрытия кода тестами под .NET

Юмор