Я очень хорошо помню один из первых моих наблюдений большого программного проекта. Я взял летнюю стажировку в крупной английской компании электроники. Мой менеджер, который является частью группы QA, дал мне экскурсию сайта , и мы вошли в грандиозном огромный складе переполненном с кубиками. Мне сказали , что этот проект находится в разработке в течение нескольких лет и в настоящее время интегрирована, и интеграция несколько месяцев. Мой гид сказал мне , что на самом деле никто не знает , как долго он будет принимать до конца интеграции. Из этого я узнал историю о совместных проектах программного обеспечения: интеграция представляет собой длительный и непредсказуемый процесс.
Но это не может быть путь. Большинство проектов , сделанных моими коллегами в ThoughtWorks, и многие другие по всему миру, рассматривая интеграцию как не-событие. Каждый человек разработчики работают всего несколько часов езды от общего состояния проекта, и может быть интегрирован в это состояние в течение нескольких минут. Об интеграции обнаружены ошибки быстро и могут быть решены быстро.
Это различие не является результатом дорогостоящих и сложных инструментов. Суть его заключается в простой практике каждой команды интегрирующей часто, как правило , ежедневно с контролируемым исходным хранилища.
Когда я описывал эту практику людям, я обычно нахожу две реакции: «она не может работать (здесь)» и «делать это не будет иметь большого значения». То, что люди узнают, когда они пробуют это, – это намного проще, чем кажется, и это имеет огромное значение для развития. Таким образом, третья общая реакция – «да, мы делаем это – как бы вы могли жить без нее?»
Термин «Непрерывная интеграция» возник в процессе разработки экстремального программирования, как одна из его двенадцати оригинальных практик. Когда я начал работать в ThoughtWorks, в качестве консультанта, я поощрял проект, над которым работал, чтобы использовать технику. Мэтью Фоеммель превратил мои смутные наставления в сплошные действия, и мы увидели, что проект идет от редкой и сложной интеграции к описанному мной событию. Мэттью и я написали наш опыт в оригинальной версии этой статьи, которая была одной из самых популярных статей на моем сайте.
Хотя непрерывная интеграция – это практика, которая не требует специального инструмента для развертывания, мы обнаружили, что полезно использовать сервер непрерывной интеграции. Наиболее известным из таких серверов является CruiseControl, инструмент с открытым исходным кодом, первоначально созданный несколькими людьми на ThoughtWorks и поддерживаемый широким сообществом. С тех пор появилось несколько других серверов CI, как с открытым исходным кодом, так и коммерческим – включая Cruise от ThoughtWorks Studios.
Построение объекта с непрерывной интеграцией
Самый простой способ объяснить, что такое CI и как это работает, – показать быстрый пример того, как он работает с разработкой небольшой функции. Предположим, что я должен что-то сделать для части программного обеспечения, на самом деле не имеет значения, что это за задача, на данный момент я предполагаю, что она небольшая и может быть выполнена за несколько часов. (Мы рассмотрим более длинные задачи и другие вопросы позже.)
Сначала я беру копию текущего интегрированного источника на свою локальную машину разработки. Я делаю это, используя систему управления исходным кодом, проверяя рабочую копию из основной строки.
Вышеупомянутый параграф будет иметь смысл для людей, которые используют системы управления исходным кодом, но будьте бредовыми для тех, кто этого не делает. Поэтому позвольте мне быстро объяснить, что для последнего. Система управления исходным кодом хранит весь исходный код проекта в репозитории. Текущее состояние системы обычно называется «магистралью». В любое время разработчик может сделать контролируемую копию магистрали на своей собственной машине, это называется «проверка». Копия на компьютере разработчика называется «рабочей копией». (В большинстве случаев вы действительно обновляете свою рабочую копию на основной – на практике это то же самое.)
Теперь я беру свою рабочую копию и делаю все, что мне нужно для выполнения моей задачи. Это будет состоять как в изменении производственного кода, так и в добавлении или изменении автоматических тестов. Непрерывная интеграция предполагает высокую степень автоматизации, которая автоматически включается в программное обеспечение: объект который я называю автоматическое тестирование кода . Часто они используют версию популярных платформ тестирования XUnit.
Как только я закончил (и обычно в разные моменты, когда я работаю), я выполняю автоматическую сборку на своей машине разработки. Это берет исходный код в моей рабочей копии, компилирует и связывает его в исполняемый файл, и запускает автоматические тесты. Только если все сборки и тесты выполняются без ошибок, общая сборка считается хорошей.
При хорошей сборке я могу подумать о том, чтобы зафиксировать мои изменения в репозитории. Завихрение, конечно, состоит в том, что другие люди могут и, как правило, внесли изменения в магистраль, прежде чем я получу шанс совершить. Поэтому сначала я обновляю свою рабочую копию с их изменениями и перестраиваю. Если их изменения столкнутся с моими изменениями, это проявится как сбой в компиляции или в тестах. В этом случае моя ответственность исправить это и повторить, пока я не могу создать рабочую копию, которая должным образом синхронизируется с основной.
Как только я сделаю свою собственную сборку правильно синхронизированной рабочей копии, я смогу, наконец, зафиксировать мои изменения в магистрали, которая затем обновит репозиторий.
Однако моя фиксация не завершает мою работу. На этом этапе мы строим снова, но на этот раз на машине интеграции, основанной на коде mainline. Только когда эта сборка будет успешной, мы сможем сказать, что мои изменения сделаны. Всегда есть шанс, что я что-то пропустил на своей машине, и хранилище не было обновлено должным образом. Только когда мои совершенные изменения успешно строятся на интеграции, моя работа выполнена. Эта интеграционная сборка может быть выполнена вручную мной или автоматически сделана Cruise.
Если между двумя разработчиками происходит конфликт, его обычно задерживают, когда второй разработчик фиксирует свою обновленную рабочую копию. Если нет, сборка интеграции должна завершиться неудачно. В любом случае ошибка обнаруживается быстро. На данный момент самая важная задача – это исправить, и заставить сборку работать исправно. В среде Continuous Integration вам никогда не удастся потерпеть неудачу при сбое встроенной сборки надолго. Хорошая команда должна иметь много правильных сборок в день. Время от времени происходят плохие сбои, но их нужно быстро устранить.
Результатом этого является наличие стабильной части программного обеспечения, которая работает правильно и содержит несколько ошибок. Все развиваются за счет общей стабильной базы и никогда не отходят так далеко от этой базы, что требуется очень много времени, чтобы интегрироваться с ней. Меньше времени тратится на поиск ошибок, потому что они появляются быстро.
Практика непрерывной интеграции
История выше обзор CI и как он работает в повседневной жизни. Как все прошло гладко, это, вероятно , больше , чем это. Теперь я остановлюсь на ключевых практиках , которые составляют эффективную CI.
Ведение единого хранилища исходного кода.
Программные проекты включают множество файлов, которые необходимо объединить для создания продукта. Отслеживание всех этих действий – это серьезное усилие, особенно когда задействовано несколько человек. Поэтому неудивительно, что на протяжении многих лет команды разработчиков программного обеспечения создали инструменты для управления всем этим. Эти инструменты, называемые инструментами управления исходным кодом, управления конфигурацией, системами контроля версий, репозиториями или другими именами, являются неотъемлемой частью большинства проектов разработки. Печально и удивительно, что они не являются частью всех проектов. Это редкость, но я сталкиваюсь с проектами, которые не используют такую систему и используют беспорядочную комбинацию локальных и общих дисков.
Поэтому, как простая основа, убедитесь, что вы получаете достойную систему управления исходным кодом. Стоимость не является проблемой, поскольку доступны качественные инструменты с открытым исходным кодом. Текущий репозиторий с открытым исходным кодом по выбору – Subversion. (Старый инструмент с открытым исходным кодом CVS по-прежнему широко используется и намного лучше, чем ничего, но Subversion – это современный выбор.) Интересно, что, разговаривая с разработчиками, я знаю, что большинство коммерческих инструментов управления исходным кодом любят меньше, чем Subversion. Единственный инструмент, который я постоянно слышал от людей, заслуживающих оплаты, это Perforce.
Как только вы получите систему управления исходным кодом, убедитесь, что это известное место для всех, чтобы получить исходный код. Никто никогда не должен спрашивать: «Где файл foo-whiffle?» Все должно быть в репозитории.
Хотя многие команды используют хранилища общей ошибка , которую я вижу в том , что они не ставят все на хранении. Если люди используют , кто будет ставить код там, но все , что вам нужно сделать сборку должны быть там, в том числе: тестовые сценарии, файлы свойств, схемы базы данных, устанавливать скрипты и сторонние библиотеки. Я знаю , что проекты , которые обеспечивают , чтобы их составители в репозиторий (важно в первые дни компиляторы листа C + +). Основное правило заключается в том , что вы должны быть в состоянии идти в проект девственницы машины, полная проверка, и быть в состоянии в полной мере развивать систему. Только минимальное количество вещей , которые должны быть на машине дев – в основном вещи , которые являются большей, сложной установкой и стабильной. Операционная система, среда разработки Java, или базовая система управления базами данных являются типичными примерами.
Вы должны поместить все, что требуется для сборки в системе управления исходным кодом, однако вы также можете поместить другие вещи, с которыми обычно работают люди. Конфигурации IDE хороши, потому что так легко людям совместно использовать одни и те же настройки IDE.
Одной из особенностей систем управления версиями является то, что они позволяют создавать несколько филиалов для обработки различных потоков разработки. Это полезная, но очень важная функция, но она часто злоупотребляет и заставляет людей беспокоиться. Держите ваше использование ветвей к минимуму. В частности, имеется магистраль: одна ветвь проекта в настоящее время находится в стадии разработки. Практически все должны работать на этой магистрали большую часть времени. (Разумные ветви – это исправления ошибок предыдущих выпусков и временных экспериментов).
В общем, вы должны хранить в исходном контроле все, что вам нужно для сборки чего-либо, но ничего, что вы действительно строите. Некоторые люди хранят продукты сборки в системе управления версиями, но я считаю, что это запах – признак более глубокой проблемы, как правило, неспособность надежно воссоздать сборки.
Сборка автоматизации
Получение источников, превращенных в запущенную систему, часто может быть сложным процессом, включающим компиляцию, перемещение файлов, загрузку схем в базы данных и т. Д. Однако, как и большинство задач в этой части разработки программного обеспечения, он может быть автоматизирован – и в результате должен быть автоматизирован. Попросить людей набрать странные команды или щелкнуть по диалоговым окнам – это пустая трата времени и питательная среда для ошибок.
Автоматизированная среда для сборок – общая черта систем. Мир Unix существует уже несколько десятилетий, сообщество Java разработало Ant, сообщество .NET имеет Nant и теперь имеет MSBuild. Убедитесь, что вы можете создавать и запускать вашу систему, используя эти скрипты, используя одну команду.
Частая ошибка заключается не в том, чтобы включать все в автоматизированную сборку. Эта сборка должна включать извлечение схемы базы данных из репозитория и запуск ее в среде выполнения. Я разработаю более раннее эмпирическое правило: любой человек должен иметь возможность подключить девственную машину, проверить источники из репозитория, выпустить единую команду и запустить на своей машине работающую систему.
Скрипты сборки входят в различные вкусы и часто являются специфическими для платформы или сообщества, но они не обязательно должны быть. Хотя в большинстве наших Java-проектов используется Ant, некоторые использовали Ruby (система Ruby Rake – очень хороший скриптовый скрипт). Мы получили большую выгоду от автоматизации раннего проекта Microsoft COM с помощью Ant.
Большая сборка часто занимает много времени, вы не хотите делать все эти шаги, если только сделали небольшое изменение. Поэтому хороший инструмент построения анализирует, что нужно изменить как часть процесса. Обычный способ сделать это – проверить даты исходных и объектных файлов и только скомпилировать, если исходная дата позже. Зависимости затем становятся сложными: если изменяется один объектный файл, то, возможно, придется перестраивать и те, которые зависят от него. Компиляторы могут обрабатывать подобные вещи, а могут и нет.
В зависимости от того, что вам нужно, вам могут понадобиться различные виды вещей, которые нужно построить. Вы можете создать систему с тестовым кодом или без него или с различными наборами тестов. Некоторые компоненты могут быть построены автономно. Сценарий сборки позволяет вам создавать альтернативные цели для разных случаев.
Многие из нас используют IDE, и большинство IDE имеют в них какой-то процесс управления построением. Однако эти файлы всегда являются собственностью IDE и часто являются хрупкими. Кроме того, им нужна IDE для работы. Для пользователей IDE нормально создавать собственные файлы проектов и использовать их для индивидуального развития. Однако важно иметь мастер-сборку, которую можно использовать на сервере и запускать из других сценариев. Таким образом, в Java-проекте все в порядке, когда разработчики создают свою IDE, но главная сборка использует Ant, чтобы гарантировать, что она может быть запущена на сервере разработки.
Сделайте свою сборку самопроверкой
Традиционно сборка означает компиляцию, компоновку и все дополнительные материалы, необходимые для запуска программы. Программа может работать, но это не означает, что она делает правильные вещи. Современные статически типизированные языки могут перехватывать множество ошибок, но гораздо больше проскальзывают через эту сеть.
Хороший способ быстрее и эффективнее выявлять ошибки – включить автоматические тесты в процесс сборки. Конечно, тестирование не является совершенным, но оно может перехватить множество ошибок – достаточно, чтобы быть полезным. В частности, появление Extreme Programming (XP) и Test Driven Development (TDD) сделало много для популяризации кода самотестирования, и в результате многие люди увидели ценность этой техники.
Постоянные читатели моей работы будут знать, что я большой поклонник как TDD, так и XP, но я хочу подчеркнуть, что ни один из этих подходов не нужен для получения преимуществ кода самотестирования. Оба этих подхода делают точкой написания тестов перед тем, как вы напишете код, который их пропускает – в этом режиме тесты в равной мере касаются изучения дизайна системы, так как они касаются ловушки ошибок. Это хорошая вещь, но для целей Continuous Integration нет необходимости, когда у нас есть более слабое требование самотестирования кода. (Хотя TDD является моим предпочтительным способом создания кода самотестирования).
Для кода самотестирования вам понадобится набор автоматических тестов, которые могут проверять большую часть кода на наличие ошибок. Тесты должны иметь возможность запускаться из простой команды и быть самоконтролируемыми. Результат выполнения тестового набора должен указывать, не прошли ли какие-либо испытания. Для сборки, которая будет самотестироваться, отказ теста должен привести к сбою сборки.
За последние несколько лет, TDD рост популяризировал XUnit семьи с открытым исходным кодом, которые идеально подходят для такого рода испытаний. Инструменты XUnit оказались очень ценными для нас на ThoughtWorks, и я всегда предлагаю людям, чтобы они ими пользовались. Эти инструменты, разработанные Kent Beck, облегчают вам настройку среды самодиагностики.
Инструменты XUnit, безусловно, являются отправной точкой для самостоятельного тестирования вашего кода. Вам также следует обратить внимание на другие инструменты, которые фокусируются на более комплексном тестировании, в настоящее время существует целый ряд таких факторов, включая FIT, Selenium, Sahi, Watir, FITnesse и множество других, в которых я не являюсь Пытаясь всесторонне перечислить здесь.
Конечно, вы не можете рассчитывать на тесты, чтобы найти все. Как часто говорят: тесты не доказывают отсутствие ошибок. Однако совершенствование – это не единственный момент, когда вы получаете окупаемость для сборки самотестирования. Несовершенные тесты, выполняемые часто, намного лучше совершенных тестов, которые никогда не записываются вообще.
Все коммиты фиксируются в основную ветку
Интеграция в основном связана с коммуникацией. Интеграция позволяет разработчикам сообщать другим разработчикам об изменениях, которые они внесли. Частое общение позволяет людям быстро узнать, как развиваются изменения.
Одна из предпосылок для разработчика, выполняющего основную линию, заключается в том, что они могут правильно построить свой код. Это, конечно же, включает прохождение тестов сборки. Как и в любом цикле фиксации, разработчик сначала обновляет свою рабочую копию в соответствии с основной линией, разрешает любые конфликты с основной линией, а затем строит на своей локальной машине. Если сборка проходит, то они могут зафиксировать основную линию.
Делая это часто, разработчики быстро узнают, есть ли конфликт между двумя разработчиками. Ключ к быстрому устранению проблем – быстро найти их. Когда разработчики фиксируют каждые несколько часов, конфликт может быть обнаружен в течение нескольких часов после его возникновения, в этот момент не так много произошло, и его легко решить. Конфликты, которые остаются необнаруженными в течение нескольких недель, могут быть очень трудно разрешить.
Тот факт, что вы строите при обновлении рабочей копии, означает, что вы обнаруживаете конфликты компиляции, а также текстовые конфликты. Поскольку сборка является самотестированием, вы также обнаруживаете конфликты в работе кода. Последние конфликты – особенно неудобные ошибки, чтобы найти, долго ли они сидят в коде. Поскольку между фиксацией изменений всего несколько часов, есть только очень много мест, где проблема может скрываться. Кроме того, поскольку не так много изменилось, вы можете использовать diff-debugging, чтобы помочь вам найти ошибку.
Мое общее правило состоит в том, что каждый разработчик должен ежедневно фиксировать в репозитории. На практике часто бывает полезно, если разработчики совершают это чаще. Чем чаще вы выполняете фиксацию, тем меньше места вы должны искать для возникновения конфликтов, и тем быстрее вы исправляете конфликты.
Частые компромиссы побуждают разработчиков разбивать свою работу на небольшие куски по несколько часов каждый. Это помогает отслеживать прогресс и дает ощущение прогресса. Часто люди на первых порах считают, что они не могут сделать что-то значимое за несколько часов, но мы обнаружили, что наставничество и практика помогают им учиться.
Каждый Commit нужно построить магистраль на интеграционной машине
Используя ежедневные коммиты, команда часто тестирует билды. Это должно означать, что магистраль остается в здоровом состоянии. Однако на практике все по-прежнему идет не так. Одной из причин является дисциплина, люди, не делающие обновления и сборки, прежде чем они совершают. Другой – различия в окружающей среде между машинами разработчиков.
В результате вы должны убедиться, что регулярные сборки выполняются на машине интеграции, и только если эта интеграция будет успешной, если фиксация будет считаться выполненной. Поскольку ответственный за это разработчик, ответственный за это, этот разработчик должен следить за сборкой магистрали, чтобы они могли ее исправить, если она сломается. Следствием этого является то, что вы не должны идти домой до тех пор, пока построение магистрали не пройдет с любыми коммитами, которые вы добавили в конце дня.
Есть два основных способа убедиться в этом: использовать ручную сборку или сервер непрерывной интеграции.
Подход, основанный на ручном построении, является самым простым для описания. По существу, это похоже на локальную сборку, которую разработчик делает перед фиксацией в репозитории. Разработчик переходит на интеграционную машину, проверяет голову магистрали (в которой теперь размещается его последний коммит) и запускает сборку интеграции. Он следит за своим прогрессом, и если построение будет успешным, он выполнит свое поручение. (Также см. Описание Джима Шора.)
Сервер непрерывной интеграции выступает в роли монитора в репозитории. Каждый раз, когда коммит против репозитория заканчивается, сервер автоматически проверяет источники на машине интеграции, инициирует сборку и уведомляет коммиттера о результате построения. Коммиттер не будет сделан, пока она не получит уведомление – обычно это письмо.
На ThoughtWorks мы являемся большими поклонниками серверов с непрерывной интеграцией – действительно, мы возглавили оригинальную разработку CruiseControl и CruiseControl.NET, широко используемых CI-серверов с открытым исходным кодом. С тех пор мы также создали коммерческий Cruise CI сервер. Мы используем сервер CI почти в каждом проекте, который мы делаем, и были очень довольны результатами.
Не каждый предпочитает использовать сервер CI. Джим Шор дал хорошо аргументированное описание того, почему он предпочитает ручной подход. Я согласен с ним в том, что CI – это нечто большее, чем просто установка какого-то программного обеспечения. Все практики здесь должны быть в игре, чтобы эффективно выполнять Непрерывную Интеграцию. Но одинаково многие команды, которые делают CI, находят CI-сервер полезным инструментом.
Многие организации делают регулярные сборки по расписанию, например, каждую ночь. Это не то же самое, что непрерывная сборка и недостаточно для непрерывной интеграции. Весь смысл непрерывной интеграции – как можно скорее найти проблемы. Ночные сборки означают, что ошибки лежат незамеченными целый день, прежде чем кто-нибудь их обнаружит. Как только они в системе, что долго, это занимает много времени, чтобы найти и удалить их.
Исправить поврежденные сборки немедленно
Ключевой частью непрерывного построения является то, что если сборка основной магистрали не удалась, ее необходимо сразу же устранить. Весь смысл работы с CI заключается в том, что вы всегда развиваетесь на известной стабильной базе. Это не плохо для разрыва основной магистрали, хотя, если это происходит все время, это говорит о том, что люди недостаточно внимательны при обновлении и создании локально перед фиксацией. Однако, когда построение магистрали нарушается, важно, чтобы она быстро фиксировалась.
Фраза, которую я помню с использованием Кента Бека, заключалась в том, что «никто не имеет более приоритетной задачи, чем исправление сборки». Это не означает, что все в команде должны остановить то, что они делают, чтобы исправить сборку, обычно требуется всего лишь пара людей, чтобы заставить вещи работать снова. Это означает сознательную приоритизацию исправления сборки как неотложной и высокоприоритетной задачи.
Часто самый быстрый способ исправить сборку – это вернуть последний коммит из магистрали, возвращая систему к последней известной хорошей сборке. Разумеется, команда не должна пытаться делать отладку на сломанной магистрали. Если причина поломки сразу не очевидна, просто верните основную линию и отладьте проблему на рабочей станции разработчика.
Чтобы не повредить магистраль, вы можете использовать ожидающую голову.
Когда команды вводят CI, часто это одна из самых сложных задач. В начале команда может не привыкать к регулярному сбору основной сборки, особенно если она работает с существующей кодовой базой. Терпение и постоянное применение, похоже, регулярно делают трюк, поэтому не обескураживайте.
Быстрое создание сборки
Весь смысл непрерывной интеграции – обеспечить быструю обратную связь. Ничто не всасывает кровь деятельности CI больше, чем сборка, которая занимает много времени. Здесь я должен признать, что какой-то странный старый парень развлекается тем, что считается длинным. Большинство моих коллег считают сборку, которая занимает один час, чтобы быть абсолютно необоснованной. Я помню команды, мечтающие, чтобы они могли получить его так быстро – и иногда мы все равно сталкиваемся с случаями, когда очень сложно получить билд с такой скоростью.
Тем не менее, для большинства проектов руководство XP десятиминутной сборки вполне разумно. Большинство наших современных проектов достигают этого. Стоит прилагать целенаправленные усилия, чтобы это произошло, потому что каждую минуту вы сокращаете время сборки, минута сохраняется для каждого разработчика каждый раз, когда они фиксируются. Поскольку CI требует частых коммитов, это добавляет много времени.
Если вы уставились на время сборки одного часа, то переход к более быстрой сборке может показаться сложной перспективой. Может быть даже сложнее работать над новым проектом и думать о том, как удержать вещи быстро. Для корпоративных приложений, по крайней мере, мы обнаружили, что узким местом является тестирование – особенно тесты, которые включают внешние службы, такие как база данных.
Вероятно, самый важный шаг – начать работу по настройке конвейера развертывания. Идея конвейера развертывания (также известного как конвейер сборки или поэтапная сборка) состоит в том, что на самом деле выполняется несколько сборок последовательно. При фиксации к основной строке запускается первая сборка – то, что я называю сборкой фиксации. Фиксация коммита – это сборка, которая необходима, когда кто-то фиксирует основную линию. Компиляция коммита – это та, которая должна быть выполнена быстро, в результате потребуется несколько ярлыков, которые уменьшат возможность обнаружения ошибок. Трюк заключается в том, чтобы сбалансировать потребности в поиске ошибок и скорости, чтобы хорошая фиксация была достаточно стабильной, чтобы другие люди могли работать.
Как только сборка коммита хороша, другие люди могут с уверенностью работать над кодом. Однако есть и более медленные тесты, которые вы можете начать делать. Дополнительные машины могут запускать дополнительные процедуры тестирования на сборке, которые занимают больше времени.
Простым примером этого является двухэтапный конвейер развертывания. На первом этапе будут выполняться тесты компиляции и запуска, которые являются более локализованными модульными тестами с полной блокировкой базы данных. Такие тесты могут выполняться очень быстро, соблюдая десятиминутный ориентир. Однако любые ошибки, которые связаны с крупномасштабными взаимодействиями, особенно с реальными базами данных, не будут найдены. Второй этап сборки запускает другой набор тестов, которые попадают в реальную базу данных и предполагают более сквозное поведение. Этот пакет может занять пару часов.
В этом сценарии люди используют первый этап в качестве сборки фиксации и используют это как основной цикл CI. Последовательная сборка выполняется, когда это возможно, подбирая исполняемый файл из последней хорошей сборки фиксации для дальнейшего тестирования. Если эта вторичная сборка терпит неудачу, это может не иметь такого же качества «стоп все», но команда стремится исправить такие ошибки как можно быстрее, сохраняя при этом сборку фиксации. Как и в этом примере, более поздние сборки часто являются чистыми тестами, так как в наши дни тесты обычно вызывают медленность.
Если вторичная сборка обнаруживает ошибку, это признак того, что сборка фиксации может сделать с другим тестом. Насколько это возможно, вы хотите, чтобы любой последующий отказ привел к новым тестам в сборке фиксации, которые могли бы уловить ошибку, поэтому ошибка остается фиксированной в сборке фиксации. Таким образом, тесты фиксации усиливаются всякий раз, когда что-то проходит мимо них. Есть случаи, когда нет способа построить быстродействующий тест, предоставляющий ошибку, поэтому вы можете решить только проверить это условие во вторичной сборке. Большую часть времени, к счастью, вы можете добавить подходящие тесты в сборку фиксации.
Этот пример имеет двухступенчатый конвейер, но основной принцип может быть распространен на любое количество последующих стадий. Сборка после сборки фиксации также может выполняться параллельно, поэтому, если у вас есть два часа вторичных тестов, вы можете повысить быстроту реагирования, имея две машины, которые запускают по половине тестов каждый. Используя параллельные вторичные сборки, подобные этому, вы можете ввести все виды дальнейшего автоматизированного тестирования, включая тестирование производительности, в обычный процесс сборки.
Тестирование в клоне производственной среды
Целью тестирования является очистка в контролируемых условиях любой проблемы, которая будет иметься в производстве. Значительная часть этого – среда, в которой будет работать производственная система. Если вы тестируете в другой среде, каждая разница приводит к риску того, что происходящее при тестировании не произойдет в процессе производства.
В результате вы хотите настроить среду тестирования как можно точнее, имитируя вашу производственную среду. В одном и том же программном обеспечении баз данных с одинаковыми версиями используется одна и та же версия операционной системы. Поместите все соответствующие библиотеки, которые находятся в рабочей среде, в тестовую среду, даже если система фактически не использует их. Используйте одни и те же IP-адреса и порты, запустите его на том же оборудовании.
Ну, на самом деле есть пределы. Если вы пишете настольное программное обеспечение, то практически невозможно протестировать в клоне все возможные настольные компьютеры со всем сторонним программным обеспечением, которое работают разные люди. Аналогичным образом некоторые производственные среды могут быть чрезмерно дорогими для дублирования (хотя я часто сталкивался с ложными экономиками, не дублируя умеренно дорогие среды). Несмотря на эти ограничения, ваша цель должна заключаться в том, чтобы дублировать производственную среду настолько, насколько это возможно, и понимать риски, которые вы принимаете для каждой разницы между тестом и производством.
Если у вас есть довольно простая настройка без множества неудобных связей, вы можете запустить сборку фиксации в передразнивающей среде. Часто, однако, вам нужно использовать тестовые двойники, потому что системы реагируют медленно или с перерывами. В результате обычно бывает очень искусственная среда для тестов фиксации для скорости и использование клона производства для вторичного тестирования.
Я заметил растущий интерес к использованию виртуализации, чтобы упростить создание тестовой среды. Виртуализированные машины могут быть сохранены со всеми необходимыми элементами, запеченными в виртуализации. Затем относительно просто установить последние сборки и запустить тесты. Кроме того, это позволяет вам запускать несколько тестов на одной машине или моделировать несколько машин в сети на одной машине. По мере снижения производительности виртуализации этот параметр становится все более и более понятным.
Сделать так, чтобы каждый мог получить последний исполняемый файл
Одна из самых сложных составляющих разработки программного обеспечения – это то, что вы создаете правильное программное обеспечение. Мы обнаружили, что очень сложно заранее определить, что вы хотите, и быть верным; Людям гораздо легче увидеть что-то не совсем правильное и сказать, как его нужно изменить. Гибкие процессы разработки явно ожидают и используют в своих интересах эту часть человеческого поведения.
Чтобы помочь сделать эту работу, любой, кто занимается программным проектом, должен иметь возможность получить последний исполняемый файл и иметь возможность запускать его: для демонстраций, пробного тестирования или просто для просмотра изменений на этой неделе.
Сделать это довольно просто: убедитесь, что есть хорошо известное место, где люди могут найти последний исполняемый файл. Может быть полезно разместить несколько исполняемых файлов в таком хранилище. Для самых последних вы должны поместить последний исполняемый файл для прохождения тестов фиксации – такой исполняемый файл должен быть довольно стабильным, если набор фиксации достаточно прочен.
Если вы следуете за процессом с четко определенными итерациями, обычно целесообразно также положить конец итерационных сборок. Демонстрации, в частности, нуждаются в программном обеспечении, функции которого знакомы, поэтому обычно стоит пожертвовать самым последним для того, что демонстратор знает, как действовать.
Каждый может видеть, что происходит
Непрерывная интеграция – это общение, поэтому вы хотите, чтобы каждый мог легко увидеть состояние системы и изменения, внесенные в нее
Одной из важных вещей для общения является состояние сборки магистрали. Если вы используете Cruise, есть встроенный веб-сайт, который покажет вам, есть ли в процессе сборки и каково было состояние последней сборки mainline. Многим командам нравится делать это еще более очевидным, подключая непрерывный показ к системе сборки – свет, который светится зеленым, когда работает сборка, или красный, если он не работает, популярны. Особенно часто встречаются красные и зеленые лавовые лампы – не только они показывают состояние сборки, но и то, как долго она находится в этом состоянии. Пузыри на красной лампе указывают на то, что сборка была сломана слишком долго. Каждая команда делает свой собственный выбор на этих датчиках построения – хорошо быть игривым с вашим выбором (недавно я видел, что кто-то экспериментировал с танцующим кроликом).
Если вы используете ручной процесс CI, эта видимость по-прежнему важна. Монитор физического сборочного устройства может отображать состояние сборки основной магистрали. Часто у вас есть токен сборки, чтобы поставить на стол того, кто в настоящее время делает сборку (опять-таки глупый, как резиновый цыпленок, является хорошим выбором). Часто люди любят шуметь на хороших билдах, например, звон колокола.
Конечно, веб-страницы серверов CI могут содержать больше информации, чем это. Круиз служит показателем не только того, кто строит, но и каких изменений он совершил. Круиз также предоставляет историю изменений, позволяя членам команды получить представление о последних действиях в проекте. Я знаю руководителей команд, которые любят использовать это, чтобы понять, что люди делают, и постоянно следить за изменениями в системе.
Еще одно преимущество использования веб-сайта заключается в том, что те, которые не расположены в одном месте, могут получить представление о статусе проекта. В общем, я предпочитаю, чтобы все активно работали над проектом, сидя вместе, но часто есть периферийные люди, которые любят следить за вещами. Также полезно, чтобы группы собирали вместе информацию из нескольких проектов, обеспечивая простой и автоматизированный статус различных проектов.
Хорошие информационные дисплеи не только на экранах компьютеров. Один из моих любимых дисплеев был для проекта, который попадал в CI. Он долгое время не мог делать стабильные сборки. Мы поставили календарь на стене, который показывал полный год с небольшим квадратом на каждый день. Каждый день группа QA помещала зеленую наклейку в день, если они получили одну стабильную сборку, которая прошла тесты фиксации, в противном случае – красный квадрат. Со временем календарь показал состояние процесса сборки, показывая устойчивое улучшение, пока зеленые квадраты не были настолько распространены, что календарь исчез – его цель выполнена.
Автоматизация развертывания
Для непрерывной интеграции вам нужно несколько сред, один для запуска тестов фиксации, один или несколько для выполнения вторичных тестов. Поскольку вы перемещаете исполняемые файлы между этими средами несколько раз в день, вам нужно будет сделать это автоматически. Поэтому очень важно иметь сценарии, которые позволят вам легко развернуть приложение в любой среде.
Естественным следствием этого является то, что вы также должны иметь сценарии, которые позволят вам легко внедрить в производство. Вы не можете внедряться в производство каждый день (хотя я столкнулся с проектами, которые это делают), но автоматическое развертывание помогает как ускорить процесс, так и уменьшить количество ошибок. Это также дешевый вариант, поскольку он использует те же возможности, которые вы используете для развертывания в тестовых средах.
Если вы внедрите в производство еще одну автоматическую функцию, которую вы должны рассмотреть, это автоматический откат. Время от времени случаются плохие вещи, и если вонючие коричневые вещества попадают на вращающийся металл, хорошо иметь возможность быстро вернуться к последнему известному хорошему состоянию. Возможность автоматического возврата также уменьшает напряженность развертывания, побуждает людей к более частому развертыванию и, таким образом, быстро выводит новые функции пользователям. (Сообщество Ruby on Rails разработало инструмент под названием Capistrano, который является хорошим примером инструмента, который делает подобные вещи.)
В кластерных средах я видел развертывание развертываний, где новое программное обеспечение развертывается на одном узле одновременно, постепенно заменяя приложение в течение нескольких часов.
Особенно интересным вариантом этого, с которым я столкнулся с общедоступным веб-приложением, является идея развертывания пробной версии для подмножества пользователей. Затем команда видит, как используется пробная сборка, прежде чем решать, развертывать ли ее для полной пользовательской группы. Это позволяет тестировать новые функции и пользовательские интерфейсы, прежде чем перейти к окончательному выбору. Автоматизированное развертывание, связанное с хорошей дисциплиной CI, имеет важное значение для выполнения этой работы.
Преимущества непрерывной интеграции
В целом, я считаю, что наибольшая и самая широкая польза от непрерывной интеграции – снижение риска. Мой разум все еще возвращается к тому раннему программному проекту, о котором я упомянул в первом абзаце. Там они были в конце (они надеялись) на длинный проект, но не имели реального представления о том, как долго это будет продолжаться до их завершения.
Проблема с отложенной интеграцией заключается в том, что очень трудно предсказать, сколько времени потребуется на это, и что еще хуже, очень трудно понять, насколько вы продвинулись в этом процессе. В результате вы ставите себя в полное слепое пятно прямо в одной из самых напряженных частей проекта – даже если вы один из редких случаев, когда вы не опаздываете.
Непрерывная интеграция полностью устраняет эту проблему. Нет долгой интеграции, вы полностью устраняете слепое пятно. Во все времена вы знаете, где находитесь, что работает, а что нет, выдающиеся ошибки, которые у вас есть в вашей системе.
Ошибки – это неприятные вещи, которые разрушают доверие и запутывают расписания и репутации. Ошибки в развернутом программном обеспечении заставляют пользователей сердиться на вас. Ошибки в незавершенной работе мешают вам, что усложняет работу с остальной частью программного обеспечения.
Непрерывная интеграция не избавляет от ошибок, но значительно упрощает их поиск и удаление. В этом отношении он скорее похож на код самотестирования. Если вы введете ошибку и обнаружите ее быстро, от нее будет намного легче избавиться. Поскольку вы изменили только небольшую часть системы, вам не нужно далеко ходить. Поскольку этот бит системы – это тот бит, с которым вы только что работали, он свежий в вашей памяти, снова облегчая поиск ошибки. Вы также можете использовать отладку diff debugging – сравнивая текущую версию системы с более ранней версией, в которой не было ошибки.
Ошибки также являются кумулятивными. Чем больше ошибок у вас, тем труднее удалить их. Это отчасти потому, что вы получаете взаимодействия с ошибками, где сбои проявляются в результате множественных ошибок – что затрудняет поиск каждой ошибки. Это также психологично – у людей меньше энергии найти и избавиться от ошибок, когда их много – феномен, который Прагматические Программисты называют синдромом Broken Windows.
В результате проекты с непрерывной интеграцией, как правило, имеют значительно меньше ошибок, как в производстве, так и в процессе. Однако я должен подчеркнуть, что степень этого преимущества напрямую зависит от того, насколько хорош ваш набор тестов. Вы должны найти, что не так уж сложно создать тестовый набор, который имеет заметное значение. Обычно, однако, требуется некоторое время, прежде чем команда действительно добирается до низкого уровня ошибок, которых у них есть потенциал для достижения. Подход к этому означает постоянную работу над вашими тестами и их совершенствование.
Если у вас есть непрерывная интеграция, это устраняет один из самых больших препятствий для частого развертывания. Частое развертывание ценно, поскольку оно позволяет пользователям быстрее получать новые функции, предоставлять более быструю обратную связь с этими функциями и, как правило, более тесно сотрудничать в цикле разработки. Это помогает преодолеть барьеры между клиентами и развитием – барьеры, которые я считаю самыми серьезными препятствиями для успешного развития программного обеспечения.
Введение в непрерывной интеграции
Итак, вам интересно попробовать Continuous Integration – с чего начать? Полный набор методов, описанных выше, дает вам все преимущества, но вам не обязательно начинать со всех них.
Здесь нет фиксированного рецепта – многое зависит от характера вашей установки и команды. Но здесь есть несколько вещей, которые мы научились делать.
Одним из первых шагов является автоматизация сборки. Получите все, что вам нужно, в управление исходными кодами, чтобы он мог собрать всю систему с помощью одной команды. Для многих проектов это не второстепенное мероприятие, но для других важно работать. Вначале вы можете строить только по требованию или просто делать автоматическую ночную сборку. Хотя это не непрерывная интеграция, автоматизированная ночная сборка – прекрасный шаг на этом пути.
Внедрите в вашу сборку автоматическое тестирование. Постарайтесь определить основные области, где что-то идет не так, и получите автоматические тесты, чтобы выявить эти сбои. В частности, в рамках существующего проекта сложно получить действительно хороший набор тестов, которые идут быстро – требуется время для проведения тестов. Вы должны начать где-нибудь, хотя – все те клише о графике построения Рима применяются.
Попытайтесь ускорить сборку фиксации. Непрерывная интеграция на построение нескольких часов лучше, чем ничего, но приступить к этому волшебному десятиминутному числу намного лучше. Для этого обычно требуется довольно серьезная операция на вашем кодовом основании, чтобы делать это, когда вы нарушаете зависимости от медленных частей системы.
Если вы начинаете новый проект, начните с Непрерывной интеграции с самого начала. Следите за временем сборки и примите меры, как только вы начнете двигаться медленнее, чем правило десяти минут. Действуя быстро, вы выполните необходимую реструктуризацию до того, как кодовая база станет настолько большой, что она станет серьезной болью.
Прежде всего, получите некоторую помощь. Найдите того, кто сделал непрерывную интеграцию, чтобы помочь вам. Как и любой новый метод, его трудно представить, если вы не знаете, как выглядит конечный результат. Это может стоить денег, чтобы получить наставника, но вы также заплатите потерянное время и производительность, если вы этого не сделаете. (Отказ / Реклама – да, мы в ThoughtWorks делаем некоторые консультации в этой области. В конце концов мы сделали большую часть ошибок, которые мы можем сделать).
заключительные мысли
За годы, прошедшие с тех пор, как Мэтт и я написали оригинальную статью на этом сайте, Continuous Integration стала основным методом разработки программного обеспечения. Вряд ли любой проект ThoughtWorks обходится без него – и мы видим, что другие используют CI во всем мире. Вряд ли я когда-либо слышал негативные вещи об этом подходе – в отличие от некоторых из наиболее противоречивых методов экстремального программирования.
Если вы не используете непрерывную интеграцию, я настоятельно рекомендую вам попробовать. Если да, возможно, в этой статье есть идеи, которые помогут вам сделать это более эффективно. За последние несколько лет мы много узнали о непрерывной интеграции, я надеюсь, что еще есть чему поучиться и улучшить.
дальнейшее чтение
Эссе, подобное этому, может охватывать только много места, но это важная тема, поэтому я создал страницу руководства на своем веб-сайте, чтобы указать вам на дополнительную информацию.
Чтобы подробнее изучить непрерывную интеграцию, я предлагаю взглянуть на соответствующую книгу Пола Дюваля по этой теме (которая получила награду Jolt – больше, чем я когда-либо управлял). Чтобы узнать больше о более широком процессе Непрерывной поставки, взгляните на книгу Джеза Хамбла и Дэйва Фарли, которые также избили меня до награды Jolt.
Оригинальная версия: http://www.martinfowler.com/articles/continuousIntegration.html