Перейти к основному содержимому

7.04. Как работает CI/CD

Разработчику Архитектору Инженеру

1. Как работает CI/CD

1.1. Как работает CI

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

Технически процесс CI запускается триггером — чаще всего это push в определённую ветку репозитория (например, в main, develop или feature-ветку). Система CI (например, Jenkins, GitLab CI, GitHub Actions, Azure DevOps) перехватывает этот триггер, клонирует репозиторий, устанавливает необходимые зависимости, компилирует код (если требуется), и последовательно выполняет:

  • Сборку (build): преобразование исходного кода в исполняемый артефакт;
  • Статический анализ кода: проверка соответствия кода стандартам, поиск потенциальных уязвимостей или антишаблонов;
  • Запуск автоматизированных тестов: unit-, интеграционные, иногда end-to-end тесты;
  • Формирование отчётов: о покрытии кода тестами, времени выполнения, количестве найденных ошибок.

Если все этапы завершены успешно, интеграция считается пройденной. В противном случае — система CI сигнализирует о сбое и часто блокирует дальнейшее продвижение изменений (например, не позволяет смёрджить pull request).

1.2. Как организовать CI

Организация CI требует как инфраструктурных, так и процессных решений:

  • Централизованный репозиторий исходного кода с чёткой политикой ветвления;
  • Система CI, способная выполнять сборку и тестирование в изолированной среде;
  • Единая система зависимостей и воспроизводимая сборка (с помощью lock-файлов, Dockerfile, Makefile и т.п.);
  • Минимальный набор автоматизированных тестов, покрывающих критические участки логики;
  • Культура частых и небольших коммитов — чтобы уменьшить риск конфликтов и упростить отладку неудачных сборок.

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


2. Непрерывная доставка и непрерывное развёртывание (Continuous Delivery / Continuous Deployment)

2.1. Как работает CD

Непрерывная доставка (Continuous Delivery) — это расширение практики CI, при котором система проверяет изменения и подготавливает их к развёртыванию в рабочую среду. Иными словами, после прохождения CI каждый успешный билд становится готовым к развёртыванию в production — в любой момент и с минимальными усилиями.

Непрерывное развёртывание (Continuous Deployment) — это дальнейшее развитие идеи: каждое изменение, успешно прошедшее CI и все необходимые проверки, автоматически развёртывается в production, без вмешательства человека.

Таким образом, отличие между Delivery и Deployment заключается в наличии или отсутствии ручного подтверждения перед попаданием в production. Continuous Delivery предполагает, что развёртывание может быть выполнено вручную по решению команды; Continuous Deployment делает это решение автоматическим.

Процесс CD включает:

  • Создание артефакта (например, Docker-образа, JAR-файла, ZIP-архива), идентифицируемого по хешу коммита;
  • Тестирование на staging-среде (предпродакшене), максимально приближённой к production;
  • Управление конфигурациями и переменными окружения отдельно от кода;
  • Плавное развёртывание (blue-green, canary, rolling update) для минимизации простоя и рисков;
  • Механизмы мониторинга и автоматического отката при обнаружении сбоев.

2.2. Как организовать CD

Для организации CD требуется более зрелая инфраструктура и культура, чем для CI:

  • Единый артефакт, проходящий все стадии без пересборки;
  • Идемпотентные скрипты развёртывания, не зависящие от состояния целевой системы;
  • Среды, идентичные друг другу по конфигурации (с возможным различием только в данных);
  • Автоматизированные проверки качества развёртывания: smoke-тесты, health-check, метрики доступности;
  • Политики безопасности: управление секретами, ограничение прав на развёртывание, аудит изменений.

Ключевое правило CD: если что-то нельзя автоматизировать — это узкое место, которое необходимо устранить. CD требует доверия к автоматике, а доверие строится на стабильности и прозрачности.


3. Среды разработки и эксплуатации

Эффективная реализация CI/CD невозможна без чёткого разграничения и управления средами — изолированными экземплярами системы, предназначенными для разных целей в жизненном цикле разработки.

3.1. Локальная разработка

Каждый разработчик работает на собственной машине, используя локально установленные инструменты или контейнеры. Цель — написать и протестировать небольшой функционал в условиях, приближённых к реальным. Однако локальная среда по определению неполна: она не включает зависимости, характерные для распределённой системы (например, очереди сообщений, внешние API, балансировщики).

3.2. Тестовая среда (test/staging)

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

  • Запуска интеграционных и end-to-end тестов;
  • Проверки совместимости между сервисами;
  • Валидации миграций баз данных.

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

3.3. Предпродакшен (pre-production)

Предпродакшен — это точное зеркало production, включая конфигурации, версии зависимостей, топологию сети и объёмы данных (или их анонимизированные копии). Его цель — финальная валидация перед выходом в продакшен. Здесь запускаются:

  • Performance-тесты;
  • Security-сканирования;
  • Business-валидация (QA, заказчик, аналитики).

Если система работает корректно в предпроде, она считается готовой к развёртыванию в production.

3.4. Продакшен (production)

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

  • Доступность (availability);
  • Надёжность (reliability);
  • Восстанавливаемость (recoverability).

Любое вмешательство в продакшен должно быть минимальным, контролируемым и обратимым.


4. Варианты развёртывания: от физических серверов до контейнеров

Развёртывание программного обеспечения — это процесс установки, конфигурации и активации системы в целевой среде. Выбор архитектуры развёртывания напрямую влияет на сложность реализации CI/CD, скорость доставки и надёжность эксплуатации.

4.1. Физические серверы с прямым доступом

Исторически первым способом размещения ПО были выделенные физические серверы, на которых вручную устанавливались операционная система, зависимости и само приложение. Управление осуществлялось через SSH или консольные сессии.

Преимущества:

  • Полный контроль над «железом»;
  • Минимальные накладные расходы на виртуализацию.

Недостатки:

  • Высокая сложность масштабирования;
  • Слабая воспроизводимость сред («на моей машине работает»);
  • Длительное время развёртывания;
  • Сложность автоматизации без использования систем конфигурации (Ansible, Puppet, Chef).

В контексте CI/CD физические серверы требуют тщательной автоматизации доставки кода и настройки среды. Без этого практики непрерывной доставки становятся нестабильными и подвержены человеческим ошибкам.

4.2. Виртуальные машины (ВМ)

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

Преимущества:

  • Полная изоляция окружения;
  • Возможность клонирования и быстрого восстановления;
  • Поддержка мультиплатформенности (Linux/Windows на одном хосте).

Недостатки:

  • Значительные накладные расходы на память и CPU;
  • Длительное время запуска по сравнению с контейнерами;
  • Сложность оркестрации при большом числе ВМ.

В CI/CD-конвейерах виртуальные машины часто используются как тестовые или staging-среды, однако в production-развёртываниях их постепенно вытесняют более лёгкие и гибкие решения.

4.3. Контейнеризация

Контейнеризация — это парадигма, при которой приложение и все его зависимости упаковываются в изолированный, переносимый образ, выполняемый в общей ОС хоста с использованием механизмов ядра (cgroups, namespaces). Наиболее распространённая реализация — Docker.

Контейнеры стали де-факто стандартом в CI/CD благодаря следующим свойствам:

  • Иммутабельность: образ создаётся один раз и используется на всех стадиях;
  • Идемпотентность: запуск одного и того же образа всегда даёт одинаковый результат;
  • Лёгкость: контейнеры запускаются за секунды и потребляют минимальные ресурсы;
  • Переносимость: один и тот же образ может работать локально, в облаке, на bare metal.

В сочетании с системами оркестрации (Kubernetes, Docker Swarm) контейнеризация позволяет реализовать масштабируемые, самовосстанавливающиеся и легко управляемые платформы. Именно с контейнерами наиболее органично сочетаются практики CI/CD:

  1. После прохождения CI создаётся Docker-образ с уникальным тегом (например, по хешу коммита);
  2. Образ помещается в registry (Docker Hub, Harbor, ECR и т.п.);
  3. На staging или production система оркестрации подтягивает новый образ и запускает его в соответствии с политикой развёртывания.

Такой подход обеспечивает единый артефакт, проходящий все этапы без пересборки, что исключает так называемый «эффект дивана» («работает в тесте — не работает в проде»).


5. Откат и вывод из эксплуатации

Несмотря на все меры предосторожности, не все изменения оказываются стабильными в production. Поэтому CI/CD-конвейеры должны включать механизмы отката и процедуры вывода из эксплуатации.

5.1. Откат (rollback)

Откат — это процесс возвращения системы к предыдущему известному рабочему состоянию. Он может быть:

  • Автоматическим: при срабатывании триггеров мониторинга (например, рост ошибок 5xx, падение метрики liveness);
  • Ручным: по решению инженера или дежурного.

Для эффективного отката необходимы:

  • Хранение предыдущих версий артефактов (образов, пакетов);
  • Идемпотентные и обратимые миграции баз данных (или стратегия «только вперёд» с двойной совместимостью);
  • Версионирование конфигураций (через GitOps, например);
  • Минимальное время восстановления (MTTR).

Важно: откат не всегда возможен. Например, при изменении структуры данных без поддержки обратной совместимости. Поэтому в зрелых системах часто применяются стратегии развёртывания без отката, такие как feature toggles и постепенное включение функционала.

5.2. Вывод из эксплуатации (decommissioning)

Вывод из эксплуатации — это процесс полного или частичного прекращения работы компонента системы. Это может быть связано с:

  • Заменой сервиса на новый;
  • Устареванием функционала;
  • Снижением нагрузки до нуля.

Процедура включает:

  • Уведомление зависимых систем;
  • Миграция или архивирование данных;
  • Удаление маршрутов и DNS-записей;
  • Остановку и удаление контейнеров/виртуальных машин;
  • Документирование и аудит.

В CI/CD-контексте важно, чтобы процесс вывода тоже был автоматизирован и проверяем. Например, в GitOps-подходе удаление манифеста из репозитория приводит к автоматическому уничтожению ресурса в кластере.