Основная цель любого программного проекта — зарабатывать деньги за счет автоматизации бизнес-процессов. Чем быстрее вы можете выпускать новые версии для клиентов, тем лучше для вашей компании. Но как быстро реализовать процесс релизов? Что ж, вы можете делать все вручную. Например, можно подключиться к удаленному серверу через SSH. Затем клонировать репозиторий с новым кодом, собрать его и запустить с помощью командной строки. Хотя это действительно работает, это неэффективный подход. Итак, сегодня мы обсуждаем автоматизацию выпуска продуктов и самого процесса разработки.
CI/CD — это две аббревиатуры, обозначающие непрерывную интеграцию (Continuous Integration) и непрерывную доставку (Continuous Delivery).
Непрерывная интеграция (Continuous Integration, CI)
Непрерывная интеграция описывает процесс прохождения изменений d репозиториb. Давайте рассмотрим простую схему, которая дает пример командной разработки.
Группа людей может работать одновременно. Но со временем все изменения передаются в главную ветку. В любом случае, даже такая простая модель вызывает пару вопросов.
- Как мы можем узнать, что код, поступающий в главную ветку, компилируется?
- Мы хотим, чтобы разработчики писали тесты для кода. Как мы можем убедиться, что тестовое покрытие не уменьшается?
- Все члены команды должны отформатировать код в соответствии с указанным стилем кода. Как мы можем проверить возможные нарушения?
Конечно, все описанные требования можно проверить вручную. Хотя это довольно неорганизованный подход. Более того, когда команда растет, поддерживать его становится труднее.
CI был сделан для автоматизации проверки заявленных вопросов.
Начнем с первого пункта. Как убедиться, что предстоящие изменения не испортят сборку? Для этого нам понадобится еще один блок в нашей схеме.
В соответствии с этим алгоритмом можно описать большинство процессов CI.
- При каждом открытии пул реквеста (а также при отправке новых изменений) сервер Git отправляет уведомление серверу CI.
- CI-сервер клонирует репозиторий, извлекает из ветки исходники (например, bugfix/wrong-sorting) и объединяет с master.
- Затем запускается скрипт сборки. Например, ./gradlew build.
- Если команда возвращает код 0, то сборка успешна. В противном случае она считается неудачной.
- CI-сервер отправляет запрос с результатом сборки на Git-сервер.
- Если сборка прошла успешно, разрешается мердж Pull Request-а. В противном случае слияние блокируется.
Процесс гарантирует, что любой код, поступающий в основную ветку, не нарушит дальнейшие сборки.
Проверка тестового покрытия
Давайте усложним задачу. Предположим, мы хотим установить минимальную планку тестового покрытия. Так что в любой момент покрытие ветки master не должно быть ниже 50%. Плагин Jacoco может легко решить проблему. Вам просто нужно настроить его таким образом, чтобы сборка не проходила, если значение тестового покрытия меньше принятого.
Реализация подхода — проста. Но есть нюанс. Она может работать только в том случае, если плагин был настроен с момента запуска проекта.
Представьте, что вы работаете над продуктом, которому пять лет. С момента его первого коммита проверка тестового покрытия не проводилась. Разработчики добавляли тесты случайным образом без какого-либо порядка. Но однажды вы решили увеличить количество тестов. Вы настраиваете плагин Jacoco так, чтобы минимальное значение равнялось 60%. Через некоторое время разработчик открывает новый пул реквест. Затем он внезапно понимает, что тестовое покрытие составляет всего 30%. Таким образом, для успешного завершения задачи необходимо покрыть еще не менее 30% кода продукта. Как вы понимаете, для проекта пятилетней давности это практически неразрешимая задача.
Что, если бы мы проверили только предстоящие изменения кода, а не весь продукт? Если разработчик изменил 200 строк в пул реквесте, ему нужно было бы покрыть как минимум 120 из них (если уровень тестового покрытия равен 60%). Но нет необходимости обрабатывать сотни других модулей, которые не являются частью задачи. Это может решить проблему. Как мы можем применить это к проекту? К счастью, решение есть.
Отчет Jacoco отправляется на сервер тестового покрытия.
SonarCloud — одно из самых популярных решений.
Сервер хранит статистику предыдущих вычислений. Полезно рассчитать тестовое покрытие для предстоящих изменений, а также для всего кода. Затем результат анализа отправляется на сервер CI, который отправляет его обратно на сервер Git.
Такой рабочий процесс дает возможность применить культуру обязательного тестирования на любом этапе развития продукта. Потому что проверяются только новые изменения.
Говоря о стиле кода, здесь не так много различий. Вы можете попробовать плагин Checkstyle. Он автоматически завершает сборку, которая нарушает любое из заявленных требований. Например, в коде может быть неиспользованный импорт. Кроме того, вы можете использовать облачные сервисы, которые запускают анализ кода и показывают результат в виде набора диаграмм (SonarCloud также может это делать).
Непрерывная доставка (Continuous Delivery, CD)
Непрерывная доставка описывает процесс автоматического развертывания новой версии продукта.
Давайте внесем некоторые изменения в схему CI. Вот так процесс CI/CD может выглядеть в реальном проекте.
Во-первых, теперь CI-сервер называется CI/CD-сервером. Дело в том, что часто задачи CI и CD выполняются одним и тем же диспетчером задач. Итак, мы рассматриваем этот подход.
Хотя это не обязательно. Например, можно делегировать задания CI GitLab CI, а задания CD — Jenkins.
Правая часть схемы представляет собой CI. Мы уже обсуждали это выше. Левая яасть изображения — CD. Джоб CD создает проект (или повторно использует артефакты, созданные на этапе CI) и развертывает его на конечном сервере.
Стоит отметить, что в нашем случае сервер — это абстракция. Например, развертывание может продолжиться в кластере Kubernetes. Или серверов может быть несколько.
После завершения этапа развертывания обычно отправляются электронные письма. Например, CD-сервер может уведомить подписчиков об успешном или неудачном развертывании.
Во всяком случае, есть важный вопрос. Когда нам следует запускать задачи в CD? Триггеры могут отличаться.
- Развертывать после каждого мерджа Pull Request.
- Развертывание по расписанию.
- Деплой после каждого слияния пул реквеста с определенной веткой.
- Комбинированный вариант.
Первый вариант устанавливает процесс таким образом, чтобы задания CI и CD всегда выполнялись последовательно. Этот подход довольно популярен в разработке с открытым исходным кодом. Библиотека Semantic Release помогает настроить проект для прозрачной интеграции этого процесса.
Важно знать определение развертывания (deploy). Это не обязательно означает, что что-то где-то запускается. Если вы разрабатываете библиотеку, то запуска фактически нет. Вместо этого процесс развертывания означает выпуск новой версии библиотеки.
Второй вариант не зависит от процесса CI. Потому что проект деплоится по заранее определенному графику. Например, каждый день в 01:00.
Третий пункт аналогичен первому. Хотя есть отличия. Предположим, что у нас есть две основные ветки в нашем репозитории. Ветка develop и master. В разработке собраны самые актуальные изменения. А во второго только релизы. Если нам нужно развернуть только master ветвь, нет необходимости запускать джоб CD при слиянии с develop.
Последний пункт — совокупность всех подходов. Например, ветвь develop может быть развернута в соответствии с расписанием разработки. А master развертывается в производственной среде при каждом слиянии пул реквеста.
Инструменты
Рынок предлагает десятки решений для автоматизации процессов CI/CD. Давайте посмотрим на некоторые из них.
- Jenkins. Один из самых востребованных инструментов CI/CD в мире. Он стал настолько популярным из-за своей политики открытого исходного кода. Так что вам не нужно ничего платить. Jenkins позволяет в императивном порядке описывать конвейеры сборки с помощью Groovy. С одной стороны, это обеспечивает большую гибкость. Но с другой стороны, это требует более высокого уровня компетентности.
- GitHub Actions. Инструмент CI/CD включен в GitHub и GitHub Enterprise. В отличие от Jenkins, GitHub Actions предоставляет декларативные сборки с конфигурацией YAML. Кроме того, решение имеет множество интеграций с различными системами контроля качества (например, SonarCube). И сборки можно описать всего несколькими строками кода.
- GitLab CI. Он очень похож на GitHub Actions. Тем не менее, у него есть особенности. Например, GitLab CI может указать на конкретные тесты, которые не прошли сборку.
- Travis CI. Облачная служба CI/CD. Она предлагает множество возможностей, не требующих сложной настройки. Например, шифрование данных, которые должны быть скрыты в публичном репозитории. Кроме того, приятным бонусом является то, что Travis CI можно применять к общедоступным проектам с открытым исходным кодом GitHub, GitLab и BitBucket абсолютно бесплатно.
Заключение
Это все, что я хотел сказать об основах процессов CI/CD. Если у вас есть какие-либо вопросы или предложения, оставьте свои комментарии. Спасибо за прочтение!