Внедряем CI/CD в разработку с помощью Gitlab CI
CI/CD — это практика DevOps, ориентированная на автоматизацию развертывания приложений. Она позволяет разработчикам сосредоточиться на решении задач, не тратя время на рутинные действия, связанные с деплоем нового функционала или правок. Это становится возможным благодаря обеспечению автоматизированной системы доставки кода в необходимое окружение, чаще — на продакшн-площадку.
CI/CD пытается устранить человеческий фактор и убрать необходимость публиковать изменения вручную разработчикам.
Почему GitLab?
В своей работе мы используем self-hosted версию Gitlab, и поскольку эта платформа дает мощные возможности для организации CI/CD из коробки, мы используем именно его. Тем более, Gitlab CI очень прост для понимания и легко настраивается.
Таким образом, добавить CI/CD на новый проект занимает не более часа, а трудозатраты на публикации и шансы, что что-то сломается, сокращаются многократно.
Как это выглядит
Чтобы начать использовать GitLab CI, нужно разместить в корне проекта специальный файл — .gitlab-ci.yml. В этом файле указываются этапы (stages), задания (jobs) и скрипты, которые будут выполняться во время выполнения задания.
Также Gitlab CI позволяет прописать условия срабатывания заданий, например, отложенное выполнение (scheduled jobs) или пуш в указанную ветку.
Набор этапов называется пайплайном. Пайплайны могут представлять из себя что угодно, но чаще это стандартный набор: сборка, тесты, развертывание на площадке. Именно пайплайны вы будете описывать в .gitlab-ci.yml, если захотите использовать Gitlab CI.
Gitlab Runner
Пайплайны выполняются специальными демонами — раннерами.
Раннер может быть размещен как на физическом сервере, так и на виртуальной машине. Обычно раннер загружает код или артефакт и запускает задания — либо локально, либо в контейнере.
Если вы также используете self-hosted решение, вы можете зарегистрировать новый раннер на сервере или использовать уже зарегистрированный. В публичной версии Gitlab должны быть доступны раннеры для Linux, Windows и Mac.
Реализуем CI/CD
Приблизительно .gitlab-ci.yml может выглядеть так:
stages:
- deploy
- error
.branch_stay_actual:
stage: deploy
rules:
- if: ($CI_COMMIT_BRANCH == $BRANCH_NAME)
when: on_success
script:
- make check_vars --makefile=MakefilePipeline -j1
- make notify_start --makefile=MakefilePipeline -j1
- make build --makefile=MakefilePipeline -j1
- make shift_build_versions --makefile=MakefilePipeline -j1
- make notify_success --makefile=MakefilePipeline -j1
- make clear_tmp --makefile=MakefilePipeline -j1
variables:
GIT_STRATEGY: clone
dev-stay-actual:
rules:
- if: ($CI_PIPELINE_SOURCE == "schedule")
when: never
- if: ($CI_COMMIT_BRANCH == $BRANCH_NAME)
when: on_success
extends: .branch_stay_actual
variables:
COMPOSE_NAME: docker-compose-dev.yml
FPM_CONTAINER_NAME: backend-dev-fpm
CICD_DIR: /var/www/html/project/path/
environment:
name: "backend-dev"
tags:
- development
dev-react_error:
stage: error
tags:
- development
environment:
name: "backend-dev"
script:
- sh $TELEGRAM_SCRIPT "❌ FAILURE"
rules:
- if: ($CI_PIPELINE_SOURCE == "schedule")
when: never
- if: ($CI_COMMIT_BRANCH != $BRANCH_NAME)
when: never
- when: on_failure
prod-stay-actual:
rules:
- if: ($CI_PIPELINE_SOURCE == "schedule")
when: never
- if: ($CI_COMMIT_BRANCH == $BRANCH_NAME)
when: on_success
extends: .branch_stay_actual
variables:
COMPOSE_NAME: docker-compose-prod.yml
FPM_CONTAINER_NAME: backend-prod-fpm
CICD_DIR: /var/www/html/project/path/
environment:
name: "backend-prod"
tags:
- production
prod-error:
stage: error
tags:
- production
environment:
name: "backend-prod"
script:
- sh $TELEGRAM_SCRIPT "❌ FAILURE"
rules:
- if: ($CI_PIPELINE_SOURCE == "schedule")
when: never
- if: ($CI_COMMIT_BRANCH != $BRANCH_NAME)
when: never
- when: on_failure
Разберем файл по порядку.
Блок stages описывает стадии пайплайна. В данном случае, имеем всего две стадии: deploy, в котором выполняются необходимые действия для деплоя проекта на сервере, и error, который выполняется в случае ошибки.
Далее идут задания.
Задание .branch_stay_actual является своеобразным прототипом, который не выполняется напрямую и наследуется в настоящих заданиях ниже (в dev-stay-actual и prod-stay-actual для деплоя на dev-площадке и prod-площадке соответственно).
Для каждого задания определены правила, при которых задание будет выполнено. Так, задание dev-stay-actual не выполняется при отложенном выполнении, а запускается лишь когда значение специальной предопределенной Gitlab переменной CI_COMMIT_BRANCH будет равно значению переменной BRANCH_NAME, которая указывается вручную. Переменная CI_COMMIT_BRANCH инициализируется автоматически самим Gitlab при пуше в ветку.
Далее идет специальный блок environment, грубо говоря, отвечающий за то, какие переменные стоит передавать в пайплайн. Environments создаются в настройках Operate вашего проекта в Gitlab, после чего в настройках CI/CD проекта можно будет указать переменные для выбранного environment.
Скрипт отвечает за то, какие команды выполнять. Содержимое скрипта зависит от ваших целей.
Мы применяем Makefile, чтобы уменьшить размер .gitlab-ci.yml и сохранить читаемость. Внутри этого мейкфайла, как и внутри скрипта в целом, как и говорилось, может быть что угодно — главное, чтобы команды были доступны на машине с раннером.
Пример задания Makefile, проверяющего, что все необходимые переменные заданы:
check_vars:
ifeq ($(BRANCH_NAME),)
$(error BRANCH_NAME var is required!)
endif
ifeq ($(CICD_DIR),)
$(error CICD_DIR var is required!)
endif
ifeq ($(TELEGRAM_SCRIPT),)
$(error TELEGRAM_SCRIPT var is required!)
endif
ifeq ($(DOTENV), )
$(error DOTENV var is required)
endif
Таким образом, переменные, указанные в блоке variables задания будут переданы в окружение выполняемого скрипта. Список предопределенных переменных CI/CD Gitlab можно найти здесь.
Немаловажной деталью пайплайна являются теги заданий. Они необходимы, чтобы раннер понимал, нужно ли выполнять данное задание или нет.
Теги настраиваются для каждого раннера индивидуально и указываются у заданий. У заданий и у раннеров может быть ноль или более тегов.
Например, вы можете описать задания, которые подхватываются только раннером, работающим с заданиями помеченными как testing для периодического тестирования системы. Или отметить задания, которые выполняются только на dev-, test- или prod-площадках.
Также, можно наследовать целые пайплайны. Выглядит это примерно так:
reusable-pipeline.yml:
variables:
DB_USER: user
DB_PASSWORD: password
production:
stage: production
script:
- build
- deploy
environment:
name: production
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
.gitlab-ci.yml:
include:
- project: "project-name"
ref: 0.1.0
file: reusable-pipeline.yml
variables:
DB_USER: root
DB_PASSWORD: real_password
stages:
- build
- test
- production
production:
environment:
name: production
В данном случае .gitlab-ci.yml называется reusable-pipeline.yml и находит в другом репозитории — project-name. Так можно описать один общий пайплайн для всех похожих проектов и наследовать при необходимости.
Заключение
CI/CD помогает разработчикам сократить затраты на развертывание и настройку проектов, позволяя им сконцентрироваться на решении бизнес-задач. Gitlab — чрезвычайно мощная платформа, и мы рекомендуем присмотреться к использованию средств CI/CD, которые она представляет.
В этой статье мы рассмотрели один из самых простых сценариев использования Gitlab CI, но его возможности — куда шире.