Основы CI/CD

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

Как ускорить процесс выпуска? Можно делать это вручную. Например, подключиться к удаленному серверу через SSH, клонировать репозиторий с новым кодом, собрать его и запустить из командной строки. Этот подход работает, но он неэффективен. Поэтому сегодня мы обсудим автоматизацию самого процесса выпуска и разработки продукта.

CI и CD  —  это две аббревиатуры, обозначающие непрерывную интеграцию и непрерывную доставку.

CI

Непрерывная интеграция (Continuous Integration) описывает процесс поступления изменений в репозиторий. Взглянем на простую схему, которая показывает пример командной разработки.

Группа людей может работать одновременно, но все изменения в конечном итоге переносятся в мастер-ветку. И даже такая простая модель вызывает вопросы.

  1. Как узнать, что код, который отправляется в главную ветку, скомпилируется?
  2. Нужно, чтобы разработчики писали тесты для кода. Как убедиться, что покрытие кода не уменьшается?
  3. Все члены команды должны форматировать код в соответствии с предписанным стилем программирования. Как проверять возможные нарушения?

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

Начнем с первого пункта. Как убедиться, что предстоящие изменения не испортят сборку? Для этого нужно добавить в схему еще один блок.

Большинство процессов CI описываются в соответствии с этим алгоритмом.

  1. При каждом открытии пул-реквеста (а также при внесении новых изменений) сервер Git отправляет уведомление на сервер CI.
  2. Сервер CI клонирует репозиторий, проверяет исходную ветку (например, bugfix/wrong-sorting) и сливает ее с основной веткой.
  3. Затем запускается сценарий сборки. Например, ./gradlew build.
  4. Если команда возвращает код 0, то сборка выполнена успешно. В противном случае она рассматривается как неудачная.
  5. Сервер CI отправляет запрос с результатом сборки на сервер Git.
  6. Если сборка прошла успешно, то пул-реквесту разрешается слияние. В противном случае слияние блокируется.

Этот процесс гарантирует, что любой код, который отправляется в master, не нарушает дальнейшие сборки.

Проверка тестового покрытия

Усложним задачу. Предположим, что нужно установить минимальную планку тестового покрытия. Таким образом, в любой момент охват главной ветки тестами не должен быть ниже 50%. Плагин Jacoco способен с легкостью решить эту проблему. Достаточно настроить его так, чтобы сборка завершалась неудачно, если значение тестового покрытия меньше установленного.

Реализовать данный подход  —  проще простого. Но есть одно “но”: он сработает только в том случае, если плагин был настроен с момента запуска проекта.

Представьте, что вы работаете над продуктом с пятилетней историей. С момента первого коммита проверка тестового покрытия не проводилась. Разработчики добавляли тесты случайным образом, без какой-либо дисциплины. Но однажды вы решили увеличить количество тестов. Вы настраиваете плагин Jacoco таким образом, чтобы минимальная планка равнялась 60%.

Через некоторое время разработчик открывает новый пул-реквест. А затем внезапно понимает, что тестовое покрытие составляет всего 30%. Итак, для успешного завершения задачи необходимо дополнительно покрыть тестами не менее 30% кода продукта. Как вы можете догадаться, это почти неразрешимая проблема для пятилетнего проекта.

Что если проверять только предстоящие изменения кода, а не весь продукт? Если разработчик изменил 200 строк в пул-реквесте, ему потребуется покрыть не менее половины из них (120), если граница тестового покрытия равна 60%. В таком случае не будет необходимости проходить через тонны модулей, которые не являются частью задачи. Это может решить проблему. Как применить это к проекту? К счастью, решение есть.

Здесь показано, как отчет Jacoco отправляется на сервер тестового покрытия.

SonarCloud  —  одно из самых популярных решений.

Сервер хранит статистику предыдущих вычислений. Это полезно для расчета тестового покрытия предстоящих изменений, а также всего кода. Затем результат анализа отправляется на сервер CI, который отправляет его обратно на сервер Git.

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

Примерно то же применимо и к стилю кода. Можно попробовать плагин Checkstyle. Он автоматически завершает сборку с ошибкой, если в ней нарушается любое из заявленных требований, например код содержит неиспользуемый импорт. Кроме того, попробуйте посмотреть на облачные сервисы, которые выполняют анализ кода и показывают результат в виде набора диаграмм (SonarCloud тоже так умеет).

CD

Непрерывная поставка (Continuous Delivery) описывает процесс автоматического развертывания новой версии продукта.

Внесем некоторые изменения в схему CI. Вот как может выглядеть процесс CI/CD в реальном проекте.

Во-первых, сервер CI теперь называется сервером CI/CD. Дело в том, что задачи CI и CD часто выполняются с помощью одного и того же диспетчера задач. Поэтому мы рассматриваем именно этот подход.

Тем не менее это не обязательно . Например, можно делегировать задачи CI в GitLab CI, а задачи CD  —  в Jenkins.

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

Стоит отметить, что в данном случае сервер  —  это абстракция. Например, развертывание может быть продолжено в кластере Kubernetes. Таким образом, может быть несколько серверов.

После завершения этапа развертывания обычно отправляются электронные письма. Например, сервер CD может уведомлять подписчиков об успешном или неудачном развертывании.

В любом случае, остается важный вопрос. Когда надо запускать задачи CD? Триггеры могут быть разными.

  1. Развертывание после каждого слияния пул-реквестов.
  2. Развертывание в соответствии с графиком.
  3. Развертывание после того, как каждый пул-реквест сливается с конкретной веткой.
  4. Комбинированный вариант.

Первый вариант задает процесс таким образом, чтобы задачи CI и CD всегда выполнялись последовательно. Этот подход довольно популярен в разработке с открытым исходным кодом. Библиотека Semantic Release помогает настроить проект для прозрачной интеграции этого процесса.

Важно быть в курсе определения deploy (развертывания). Это не обязательно означает, что где-то что-то запускается. Если вы разрабатываете библиотеку, то никакого запуска не происходит. В таком случае процесс развертывания означает выпуск новой версии библиотеки.

Второй вариант не зависит от процесса CI. Потому что проект развертывается в соответствии с некоторым предопределенным графиком. Например, каждый день в час утра.

Третий вариант аналогичен первому, но есть и отличия. Предположим, что у нас в репозитории есть две основные ветки: ветка разработки develop и главная ветка master. В ветке разработки содержатся наиболее актуальные изменения, а в мастер-ветке  —  только релизы. Если нужно развернуть только главную ветку, нет необходимости запускать CD при слиянии с develop.

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

Инструменты

Рынок предлагает десятки решений для автоматизации процессов CI/CD. Взглянем на некоторые из них.

  1. Jenkins  —  один из самых востребованных инструментов CI/CD в мире. Его популярность связана с политикой открытого исходного кода  —  пользоваться им можно бесплатно. Jenkins позволяет императивно описывать конвейеры сборки с помощью Groovy. Это обеспечивает большую гибкость, но требует более высокого уровня компетентности.
  2. GitHub Actions  —  инструмент CI/CD, который включен в GitHub и GitHub Enterprise. В отличие от Jenkins, GitHub Actions предоставляет декларативные сборки с конфигурацией YAML. Кроме того, решение имеет множество интеграций с различными системами обеспечения качества (например, SonarCube). Благодаря этому сборка может быть описана всего в нескольких строках.
  3. GitLab CI  —  инструмент, напоминающий GitHub Actions, но со своими особенностями. Например, он может указывать на конкретные тесты, из-за которых сборка завершилась неудачно.
  4. Travis CI  —  облачный сервис CI/CD. Он предлагает различные возможности, которые не требуют сложной настройки. Например, шифрование данных, которые должны быть скрыты в общедоступном хранилище. Кроме того, приятным бонусом является то, что Travis CI можно абсолютно бесплатно применять к общедоступным проектам с открытым исходным кодом на GitHub, GitLab и BitBucket.

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Semyon Kirekov: Basics of CI/CD

Предыдущая статьяКак сократить ошибки в базе кода React
Следующая статьяЭкспериментируем с пользовательскими функциями JavaScript на Trino