Памятка по созданию Gitlab Pipelines

Шаблоны




В данном разделе напишем несколько готовых шаблонов, которые можно взять за основы при написании своего сценария.




1. Минимальный сценарий. Для небольших заданий, состоящих из пары заданий:




stages:
  - build

TASK_NAME:
  stage: build
  script:
    - ./build_script.sh




где:




  • stages — описание стадий нашего пайплайна. В данном примере, всего одна.



  • TASK_NAME — имя нашей задачи.

    • stage — стадия, к которой относится наше задание.



    • script — набор скриптов для выполнения.




2. Стандартный цикл при сборке. Обычно, процесс 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







Задаем имя образа, если наше задание выполняется в контейнере docker:




TASK_NAME:
  ...
  image: debian:11




Before_script







Данная опция определяет список команд, которые должны выполняться перед опцией script и после получения артефактов.




TASK_NAME:
  ...
  before_script:
    - echo "Run before_script"




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







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




TASK_NAME:
  ...
  script:
    ...
  after_script:
    - command1
    - command2




Artifacts







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




1. Например, так можно указать, какие файлы или каталоги будут артефактами:




TASK_NAME:
  ...
  artifacts:
    paths:
      - ${PKG_NAME}.deb
      - ${PKG_NAME}.rpm
      - *.txt
      - configs/




* в артефакты попадут все файлы, название которых заканчивается на txt, файлы ${PKG_NAME}.deb и ${PKG_NAME}.rpm, а также каталог configs. Где ${PKG_NAME} — переменная (подробнее о переменных ниже).




В других заданиях, которые будут выполняться после, можно использовать артефакты, обращаясь к ним по именам, например:




TASK_NAME_2:
  ...
  script:
    - cat *.txt
    - yum -y localinstall ${PKG_NAME}.rpm
    - apt -y install ./${PKG_NAME}.deb




2. Также мы можем передать системные переменные, которые были нами переданы в файл:




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, в котором, как правило, хранятся метаданные репозитория.
** обратите внимание, что в отличие от pathsexclude не берет файлы и каталоги рекурсивно и нужно явно указывать объекты.




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




Что произошло:







Environment







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




TASK_NAME:
  ...
  environment:
    RSYNC_PASSWORD: rpass
    EDITOR: vi




Release







Публикует релиз на портале Gitlab для нашего проекта.




TASK_NAME:
  ...
  release:
    name: 'Release $CI_COMMIT_TAG'
    tag_name: '$CI_COMMIT_TAG'
    description: 'Created using the release-cli'
    assets:
      links:
        - name: "program-${VERSION}"
          url: "https://gitlab.com/master/project/-/jobs/${CI_JOB_ID}/artifacts/raw/program-${VERSION}.tar.gz"
  rules:
    - if: $CI_COMMIT_TAG




Директивы правил и ограничений




Для управления поведением выполнения задач могут использоваться директивы с правилами и условиями. Подробнее мы их рассмотрим ниже, а в данном разделе просто перечислим их.




Rules







Правила, при соблюдении которых наше задание может быть запущено. Примеры работы с директивой rules приведены ниже.




When







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




Needs







Тоже набор правил, требующий определенных условий для запуска задачи. Все директивы условий и правил описаны ниже.




Правила и условия




Мы можем выполнять или пропускать задания, в зависимости от выполнения определенных условий. Для этого существует несколько полезных директив, которые мы рассмотрим в данном разделе.




Rules




Регламентирует различные правила, определенные с помощью:







1. Оператор if. Позволяем проверить условие, например, если переменная равна определенному значению:




TASK_NAME:
  ...
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'




* в этом примере commit должен быть выполнен в branch по умолчанию.




2. Изменения затронули определенные файлы. Проверка выполняется с помощью опции changes.




В данном примере, файл script.sql:




TASK_NAME:
  ...
  rules:
    - changes:
        - script.sql




3. Несколько условий. Условий для начала выполнения задания может быть несколько. Рассмотрим несколько примеров.




а) При условии, что commit выполнен в branch по умолчанию И изменения затронули файл script.sql:




TASK_NAME:
  ...
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      changes:
        - script.sql




б) При условии, что commit выполнен в branch по умолчанию ИЛИ изменения затронули файл 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




Определяет условия запуска задания. Возможные значения:







Разберем примеры.




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




Дополнительные параметры




В данном разделе мы рассмотрим различные опции, которые не вошли в другие разделы.




1. Workflow. Позволяем задать общие правила запуска для всего конвейера. Рассмотрим пример с официального сайта:




workflow:
  rules:
    - if: $CI_COMMIT_TITLE =~ /-draft$/
      when: never
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH




В данном примере конвейер:







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'




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]




* давайте разберемся, что происходит в нашем примере.







Источник: https://kuzevanov.ru/devops/памятка-по-созданию-gitlab-pipelines.html