DevOps

GitLab CI: альтернативный подход, .gitlab-ci.yml

1 просмотров

Введение

GitLab CI — альтернативная, широко используемая платформа CI/CD, особенно распространённая в организациях, использующих самостоятельно развёрнутый (self-hosted) GitLab — в статье разберём её ключевые отличия от GitHub Actions и базовый синтаксис .gitlab-ci.yml.

Концепция

GitLab CI определяется единственным файлом .gitlab-ci.yml в корне репозитория (в отличие от множества отдельных workflow-файлов GitHub Actions), организованным через «стадии» (stages, например build, test, deploy), выполняемые последовательно, и «задачи» (jobs) внутри каждой стадии, способные выполняться параллельно друг с другом в рамках одной стадии. GitLab Runner — агент, выполняющий реальную работу задач CI, может быть как предоставляемым самим GitLab.com (shared runners), так и самостоятельно развёрнутым (self-hosted runners) на собственной инфраструктуре организации, что особенно ценно для задач, требующих специфичного оборудования (например, физическая Raspberry Pi/STM32-плата для реального, а не эмулированного embedded-тестирования) или доступа к внутренней, не публичной сети организации.

Пример кода

# .gitlab-ci.yml — структура со стадиями build/test/deploy
stages:
  - build
  - test
  - deploy

build_job:
  stage: build
  image: ubuntu:22.04
  before_script:
    - apt-get update && apt-get install -y cmake qt6-base-dev
  script:
    - cmake -B build -S .
    - cmake --build build
  artifacts:
    paths:
      - build/MyQtApp # передача результата сборки в следующие стадии (test, deploy)

test_job:
  stage: test
  image: ubuntu:22.04
  script:
    - ctest --test-dir build --output-on-failure
  dependencies:
    - build_job # явное указание зависимости от результата предыдущей стадии

deploy_job:
  stage: deploy
  script:
    - echo "Публикация релиза..."
  only:
    - main # выполняется ТОЛЬКО для основной ветки, не для каждого pull request
# Использование self-hosted runner со специфичными возможностями (например, доступ к реальной
# Raspberry Pi-плате для embedded-тестирования, продолжение Raspberry Pi-цикла)
embedded_test_job:
  stage: test
  tags:
    - raspberry-pi # этот job выполнится ТОЛЬКО на runner'е с этим конкретным тегом,
                    # зарегистрированном на машине с реально подключённой Raspberry Pi
  script:
    - ./run_hardware_tests.sh

Пояснения к коду

Структура stages/stage явно показывает последовательную организацию GitLab CI — test_job выполняется только после успешного завершения всех задач стадии build, а deploy_job — только после успешного завершения test, и artifacts обеспечивают передачу конкретных файлов (результата сборки) между разными, изолированными друг от друга задачами разных стадий, выполняющимися на потенциально разных runner’ах. tags: [raspberry-pi] показывает практичный механизм направления конкретной задачи именно на runner с нужными специфичными возможностями — это особенно ценно для проектов с embedded-составляющей (Raspberry Pi/STM32-циклы), где часть тестирования реально требует физического, специфичного оборудования, недоступного на обычных, общих runner’ах облачного CI-сервиса.

Подводные камни

  • Отсутствие явного указания only/rules (более новый, гибкий синтаксис) для задач, которые не должны выполняться для каждого изменения (например, deploy_job, которая должна срабатывать только для основной ветки, а не для каждой ветки функции или pull request) — без такого ограничения задача деплоя могла бы непреднамеренно сработать для незавершённой, экспериментальной ветки разработки, что особенно опасно для задач с реальными, видимыми пользователю побочными эффектами (публикация релиза).
  • Использование self-hosted runner без должного понимания его операционной нагрузки и обслуживания — в отличие от полностью управляемых, облачных shared runners GitHub Actions/GitLab.com, self-hosted runner требует самостоятельного администрирования (обновления, мониторинг доступности, безопасность машины, на которой он работает), и эта операционная ответственность должна быть осознанно принята командой, разворачивающей такой runner, особенно для критичных задач (например, embedded-тестирование на реальном оборудовании), зависящих от непрерывной доступности именно этого конкретного, специфично настроенного runner’а.
  • Забытое указание artifacts для передачи результата сборки между стадиями, что приводит к ошибке на следующей стадии (например, test_job не находит скомпилированный исполняемый файл, поскольку каждая задача GitLab CI по умолчанию выполняется в полностью чистом, изолированном окружении, не сохраняющем автоматически результаты предыдущих задач) — явное указание artifacts (и соответствующих dependencies для последующих задач, нуждающихся в этих артефактах) обязательно для любых данных, которые должны «пережить» границу между отдельными задачами разных стадий.
  • Слепое копирование конфигурации .gitlab-ci.yml из примера/документации без адаптации к конкретному проекту, особенно версии используемых образов (image: ubuntu:22.04) и реальных команд установки зависимостей, специфичных для конкретного, возможно отличного от примера набора зависимостей конкретного Qt-проекта — конфигурация CI должна быть протестирована и адаптирована именно для реальных, конкретных потребностей текущего проекта, а не скопирована механически из чужого, возможно не полностью соответствующего примера.