Шпаргалка по Pipelines Gitlab
Шаблоны
В данном разделе напишем несколько готовых шаблонов, которые можно взять за основы при написании своего сценария.
Минимальный сценарий
Для небольших заданий, состоящих из пары заданий:
stages:
- build
TASK_NAME:
stage: build
script:
- ./build_script.sh
где:
stages — описание стадий нашего пайплайна. В данном примере, всего одна. TASK_NAME — имя нашей задачи. stage — стадия, к которой относится наше задание. script — набор скриптов для выполнения.
Стандартный цикл при сборке
Обычно, процесс CI/CD включает следующие шаги:
- Сборка пакета.
- Тестирование.
- Доставка.
- Развертывание.
Для написания такого сценария за основу можно взять шаблон:
stages:
- build
- test
- delivery
- deploy
build-job:
stage: build
script:
- echo "Start build job"
- build-script.sh
test-job:
stage: test
script:
- echo "Start test job"
- test-script.sh
delivery-job:
stage: delivery
script:
- echo "Start delivery job"
- delivery-script.sh
deploy-job:
stage: deploy
script:
- echo "Start deploy job"
- deploy-script.sh
Задания
Опции, которые могут применяться при описании задания. Общий синтаксис:
<TASK_NAME>:
<OPTION1>: ...
<OPTION2>: ...
Мы перечислим лишь часто встречаемые опции. Полный список можно найти в официальной документации.
Stage
https://docs.gitlab.com/ee/ci/yaml/#stages
Опция указывает, к какой стадии относится задание. Например:
stages:
- build
- test
TASK_NAME:
...
stage: build
TASK_NAME:
...
stage: test
Сами же стадии описываются в общей директиве stages.
Также есть две стадии, которые не требуют предварительного определения в stages:
.pre — запускает до выполнения основных заданий конвейера.
.post — выполняется в конце, после выполнения основных заданий нашего пайплайна.
Например:
stages:
- build
- test
getVersion:
stage: .pre
script:
- VERSION=$(cat VERSION_FILE")
- echo "VERSION=${VERSION}" > variables.env
artifacts:
reports:
dotenv: variables.env
в данном примере мы до того, как начнем проходить по всем заданиям сборки, определим переменную с версией, прочивав ее из файла, и передадим артефактом в качестве системной переменной VERSION.
Image
https://docs.gitlab.com/ee/ci/yaml/#image
Задаем имя образа, если наше задание выполняется в контейнере docker:
TASK_NAME:
...
image: debian:11
Services
https://docs.gitlab.com/ci/services/
Позволяет поднять дополнительный контейнер, который будет доступен первому (указанному в Image):
TASK_NAME:
...
image: debian:12
services:
- name: postgres:15
alias: pgdb
в данном примере сборка будет выполняться в контейнере debian. Но дополнительно будет поднят контейнер postgres, который будет доступен по значению директивы alias. Таким образом, если для нашей процедуры CI/CD нужна база данных, к ней можно обратиться по имени pgdb.
Before_script
https://docs.gitlab.com/ee/ci/yaml/#before_script
Данная опция определяет список команд, которые должны выполняться перед опцией script и после получения артефактов.
TASK_NAME:
...
before_script:
- echo "Run before_script"
Script
https://docs.gitlab.com/ee/ci/yaml/#script
Основная часть, где выполняются задания сценария, описана в опции script. Рассмотрим ее подробнее.
1. Описание массива команд. Мы просто перечисляем списком те команды, которые необходимо последовательно выполнить нашему сценарию в конкретной задаче
TASK_NAME:
...
script:
- command1
- command2
2. Длинные команды, разбитые на несколько строк. Нам может потребоваться выполнить команды в виде скрипта (с операторами сравнения, например). В этом случае будет удобна многострочная запись. Ее можно указать разными способами
- Индикатор | :
TASK_NAME:
...
script:
- |
command_line1
command_line2
для него каждая новая строка является переходом к новой команде или части оператора. По сути, это близко к логике скрипта shell.
- Индикатор > :
TASK_NAME:
...
script:
- >
command_line1
command_line1_continue
command_line2
command_line2_continue
данный индикатор интерпретирует новую команду после пустой строки.
After_script
https://docs.gitlab.com/ee/ci/yaml/#after_script
Набор команд, которые запускаются после scripts, даже, при неудачном завершении последнего.
TASK_NAME:
...
script:
...
after_script:
- command1
- command2
Artifacts
https://docs.gitlab.com/ee/ci/yaml/#artifacts
Артефакты представляют из себя промежуточные сборки или файлы, которые могут передаваться от одного этапа — другому.
1. Например, мы можем передать системные переменные, которые были нами переданы в файл
TASK_NAME:
...
script:
- echo -e "VERSION=1.1.1" > variables.env
...
artifacts:
reports:
dotenv: variables.env
в данном примере мы передадим системную переменную VERSION со значением 1.1.1 через файл variables.env.
3. При необходимости, мы можем исключить из списка определенные файлы по названию или маске
TASK_NAME:
...
artifacts:
paths:
...
exclude:
- ./.git/**/*
в данном примере мы исключаем каталог .git, в котором, как правило, хранятся метаданные репозитория.
Обратите внимание, что в отличие от paths, exclude не берет файлы и каталоги рекурсивно и нужно явно указывать объекты.
Extends
https://docs.gitlab.com/ee/ci/yaml/#extends.
Позволяет вынести часть сценария в отдельный блок и объединить его с заданием. Чтобы это лучше понять, рассмотрим конкретный пример:
.my_extend:
stage: build
variables:
USERNAME: my_user
script:
- extend script
TASK_NAME:
extends: .my_extend
variables:
VERSION: 123
PASSWORD: my_pwd
script:
- task script
И так, в нашем задании TASK_NAME мы используем extends. В результате, задание примет такой вид:
TASK_NAME:
stage: build
variables:
VERSION: 123
PASSWORD: my_pwd
USERNAME: my_user
script:
- task script
Что произошло:
stage: build попало в задание из my_extend. произошло объединение variables, итого в задание попали VERSION, PASSWORD и USERNAME. script используется из задания (значения ключей не объединяются, приоритет за значением основного задания).
Timeout
https://docs.gitlab.com/ee/ci/runners/configure_runners.html#set-script-and-after_script-timeouts.
Позволяет ограничить время выполнения задания, или увеличить время, заложенное по умолчанию (1 час).
Можно применить глобально для задания:
TASK_NAME:
...
timeout: 2h
Дополнительно можно задать лимит для скриптов и/или постскриптов с помощью переменных:
TASK_NAME:
variables:
RUNNER_SCRIPT_TIMEOUT: 30m
RUNNER_AFTER_SCRIPT_TIMEOUT: 5m
Если мы, описывая задачу, применяем и timeout и переменные RUNNER_SCRIPT_TIMEOUT/RUNNER_AFTER_SCRIPT_TIMEOUT, то значение последних не должны превышать значения timeout.
Release
https://docs.gitlab.com/ee/ci/yaml/#release.
Публикует релиз на портале Gitlab для нашего проекта.
TASK_NAME:
...
release:
name: 'Release $CI_COMMIT_TAG'
tag_name: '$CI_COMMIT_TAG'
description: 'Created using the release-cli'
assets:
links:
- name: "myprogram-${VERSION}"
url: "https://gitlab.com/master.dmosk/project/-/jobs/${CI_JOB_ID}/artifacts/raw/myprogram-${VERSION}.tar.gz"
rules:
- if: $CI_COMMIT_TAG
Обратите внимание, что мы применяем правило if (о нем ниже).
Директивы правил и ограничений
Для управления поведением выполнения задач могут использоваться директивы с правилами и условиями. Подробнее мы их рассмотрим ниже, а в данном разделе просто перечислим их.
- Rules
https://docs.gitlab.com/ee/ci/yaml/#rules.
Правила, при соблюдении которых наше задание может быть запущено. Примеры работы с директивой rules приведены ниже.
- When
https://docs.gitlab.com/ee/ci/yaml/#when.
Определяет условия запуска задания, например, только ручной запуск или через определенный интервал времени. Примеры работы приведены ниже.
- Needs
https://docs.gitlab.com/ee/ci/yaml/#needs.
Тоже набор правил, требующий определенных условий для запуска задачи. Все директивы условий и правил описаны ниже.
- Мартицы (параллельная сборка)
https://docs.gitlab.com/ee/ci/yaml/#parallelmatrix
Позволяет запустить несколько сборок для одного задания, подставляя разные значения переменных.
buildApp:
stage: build
image: ${IMAGE}
...
script:
- build command 1
- build command 2
- build command with args pg_ver=${POSTGRESQL_VERSION}
...
parallel:
matrix:
- IMAGE: ['ubuntu22.04']
POSTGRESQL_VERSION: ['13','14']
- IMAGE: ['rocky9']
POSTGRESQL_VERSION: ['15']
в результате такого описания, данное задание запустит 3 сборки — две для ubuntu22.04 + postgresql версий 13 и 14, а также одна сборка на rocky9 + postgresql 15.
В matrix мы задаем переменные, которые используются нами при описании задачи сборки.
Правила и условия
Мы можем выполнять или пропускать задания, в зависимости от выполнения определенных условий. Для этого существует несколько полезных директив, которые мы рассмотрим в данном разделе.
Регламентирует различные правила, определенные с помощью:
if
changes
exists
allow_failure
variables
1. Оператор if позволяет проверить условие, например, равенство определенному значению
TASK_NAME:
...
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
в данном примере коммит должен быть выполнен в бранче по умолчанию.
2. Изменения затронули определенные файлы. Проверка выполняется с помощью опции changes
В данном примере, файл script.sql:
TASK_NAME:
...
rules:
- changes:
- script.sql
3. Несколько условий. Условий для начала выполнения задания может быть несколько. Рассмотрим несколько примеров
а) При условии, что коммит выполнен в бранче по умолчанию И изменения затронули файл script.sql:
TASK_NAME:
...
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
changes:
- script.sql
б) При условии, что коммит выполнен в бранче по умолчанию ИЛИ изменения затронули файл script.sql
TASK_NAME:
...
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
- changes:
- script.sql
4. Проверка на существование файла. Определяется с помощью exists
TASK_NAME:
...
rules:
- exists:
- script.sql
задание будет выполняться только в случае присутствия файла script.sql.
5. Разрешить сбой задания. Задается с помощью allow_failure
TASK_NAME:
...
rules:
- allow_failure: true
в данном примере наш конвейер продолжит работу, если задание TASK_NAME будет выполнено с ошибкой.
6. Определение переменной в зависимости от условия. Для этого используются вместе if и variables
TASK_NAME:
...
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
variables:
DEPLOY_VARIABLE: "production"
- if: '$CI_COMMIT_BRANCH =~ demo'
variables:
DEPLOY_VARIABLE: "demo"
When
определяет условия запуска задания. Возможные значения:
- on_success (по умолчанию) — если все задания на более ранних этапах завершились успешно или имеют allow_failure: true.
- manual — запуск вручную (в панели pipeline появится кнопка запуска).
- always — запускать всегда, независимо от предыдущих результатов.
- on_failure — только в случае сбоя, хотя бы, одного задания на более раннем этапе.
- delayed — отложить запуск задания. Контролировать срок можно с помощью директивы start_in.
- never — никогда не запускать задание.
Разберем примеры.
1. Manual
TASK_NAME:
...
when: manual
задание не начнет выполняться, пока мы не нажмем кнопку запуска в GitLab CI/CD.
2. Always
TASK_NAME:
...
when: always
задание будет выполняться всегда. Удобно, например, если нужно сформировать отчетный файл, независимо от результатов сборки.
3. On_failure
TASK_NAME:
...
on_failure: always
задание будет выполняться при наличии ошибки на ранних этапах. Можно использовать для отправки уведомления.
4. Delayed
TASK_NAME:
...
when: delayed
start_in: 30 minutes
запуск задания будет отложен на 30 минут.
5. Never
TASK_NAME:
...
when: never
задание не будет выполняться.
6. Использование вместе с if
TASK_NAME:
...
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
when: manual
в данном примере задание будет выполняться, если коммит прошел в основной ветке и если администратор подтвердил запуск.
Needs
Позволяет задавать условия выполнения заданий только при наличии определенного артефакта или выполненного задания. С помощью правил данного типа можно контролировать порядок выполнения задач.
Рассмотрим примеры.
1. Artifacts. Принимает значение true (по умолчанию) и false. Для запуска задания нужно, чтобы на предыдущих этапах были загружены артефакты. Запись
TASK_NAME:
...
needs:
- artifacts: false
позволяет начать выполнение задания без этого.
2. Job. Позволяет начать выполнение задачи только после завершения другого задания
TASK_NAME:
...
needs:
- job: createBuild
в нашем примере задача начнет выполняться не раньше завершения задачи с названием createBuild.
Переменные
В данном разделе будут рассмотрены пользовательские переменные, которые мы можем использовать в сценарии на свое усмотрение, а также некоторые встроенные переменные, которые могут менять процесс работы конвейера.
Пользовательские переменные
Задаются с помощью директивы variables.
Можно определить глобально для всех заданий:
variables:
PKG_VER: "1.1.1"
Или для конкретного задания:
TASK_NAME:
...
variables:
PKG_VER: "1.1.1"
Теперь можно использовать нашу переменную в сценарии. Для этого ставим перед ее названием знак доллара, а также стоит заключить его в фигурные скобки, например:
script:
- echo ${PKG_VER}
Переменные Gitlab
Данный тип переменных поможет нам управлять процессом сборки. Перечислим данные переменные с описанием их свойств:
- LOG_LEVEL Определяет уровень журнала. Варианты: debug, info, warn, error, fatal, и panic. Имеет более низкий приоритет, по сравнению с аргументами командной строки --debug, --log-level. LOG_LEVEL: warning CI_DEBUG_TRACE Включение или отключение ведения журнала отладки. Принимает значения true или false. CI_DEBUG_TRACE: true CONCURRENT Ограничивает количество заданий, которые могут выполняться одновременно. CONCURRENT: 5 GIT_STRATEGY Способ загрузки файлов из репозитория. Возможны варианты: clone, fetch, и none (не загружать). GIT_STRATEGY: none FF_ENABLE_JOB_CLEANUP Определяет будет ли каталог проекта очищен в конце сборки. FF_ENABLE_JOB_CLEANUP: true
Дополнительные параметры
В данном разделе мы рассмотрим различные опции, которые не вошли в другие разделы.
- Workflow. Позволяем задать общие правила запуска для всего конвейера. Рассмотрим пример с официального сайта:
workflow:
rules:
- if: $CI_COMMIT_TITLE =~ /-draft$/
when: never
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_PIPELINE_SOURCE == "web"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
В данном примере конвейер:
Не будет запущен, если название коммита содержит текст draft. Будет запущен, если пайплайн сработал после merge request. Будет запущен, если пайплайн запускается вручную из веб-интерфейса gitlab. Будет запущен, если изменения внесены в основную ветку репозитория.
2. Значения по умолчанию. Задаются в директиве default. Опции с данными значениями будут использоваться во всех заданиях или могут быть переопределены на уровне конкретного задания
Пример:
default:
image: centos:7
tags:
- ci-test
мы определили опцию с именем образа (например, образа docker) и тег (теги могут быть необходимы для запуска некоторых runner).
3. Импорт конфигурации из другого файла yml. Может быть полезным, например, для написания общей части сценария, которую мы захотим применять ко всем pipeline или для разделения громоздкого сценария на составные части. Выполняется с помощью опции include. Имеет различные варианты подгрузки файлов. Рассмотрим их подробнее
а) подключение локального файла (local):
include:
- local: .gitlab/ci/build.yml
б) коллекции шаблонов (template):
include:
- template: Custom/.gitlab-ci.yml
в данном примере мы подключим к нашему сценарию содержимое файла Custom/.gitlab-ci.yml.
в) внешний файл доступный по HTTP (remote):
include:
- remote: 'https://gitlab.dmosk.ru/main/build.yml'
г) другой проект:
include:
- project: 'public-project'
ref: main
file: 'build.yml'
д) несколько файлов:
include:
- project: 'public-project'
ref: master
file:
- 'build.yml'
- 'include/main.yml'
4. !reference tags. Позволяет описать сценарий и повторно его использовать для разных стадий и задач нашего CI/CD
Например:
.apt-cache:
script:
- apt update
after_script:
- apt clean all
install:
stage: install
script:
- !reference [.apt-cache, script]
- apt install nginx
update:
stage: update
script:
- !reference [.apt-cache, script]
- apt upgrade
- !reference [.apt-cache, after_script]
давайте разберемся, что происходит в нашем примере.
Мы создали задачу apt-cache. Точка в начале названия говорит системе, что данную задачу не нужно стартовать автоматически при запуске pipeline. Созданная задача состоит из двух секций script и after_script. Секций может быть больше. Мы выполняем стадию install. В одной из строк выполнения мы вызываем apt-cache (только команды из раздела script). При выполнении стадии update мы вызываем apt-cache два раза — первый выполняет команды раздела script, второй — after_script. Примеры действий Рассмотрим некоторые готовые наборы команд для выполнения конкретных задач.
Подключение к хосту по SSH с использованием ключа Предположим, что у нас в переменной SSH_REMHOST_KEY записан приватный ключ, который мы будем использовать для подключения по SSH к хосту remote_host. В этом случае, сценарий будет выглядеть так:
...
script:
- mkdir -p ~/.ssh
- eval $(ssh-agent -s)
- ssh-add <(echo "$SSH_REMHOST_KEY")
- ssh-keyscan -H remote_host >> ~/.ssh/known_hosts
- ssh root@remote_host "ls -la /var/"