GitLab CI: .gitlab-ci.yml, stages, jobs, rules, runners, кэш

↑ Этап 3 · CI-CD

Обязательно~10 мин чтенияУровень: middle

Зачем это на собесе

GitLab CI — основной CI/CD в вашем стеке. Нужно уметь писать .gitlab-ci.yml: stages, rules, кэш, артефакты, окружения, раннеры.

Структура

Файл .gitlab-ci.yml в корне репозитория. Pipeline состоит из stages, внутри — jobs; jobs выполняются на runners.

stages: [build, test, package, deploy]
 
variables:
  DOTNET_CLI_TELEMETRY_OPTOUT: "1"
  NUGET_PACKAGES: $CI_PROJECT_DIR/.nuget
  IMAGE: $CI_REGISTRY_IMAGE/api
 
default:
  image: mcr.microsoft.com/dotnet/sdk:9.0
  interruptible: true
  retry: { max: 2, when: [runner_system_failure, stuck_or_timeout_failure] }
 
build:
  stage: build
  script:
    - dotnet restore --locked-mode
    - dotnet build -c Release --no-restore
  cache:
    key: { files: [packages.lock.json] }
    paths: [.nuget/]
  artifacts:
    paths: [src/**/bin/Release/]
    expire_in: 1 day
 
unit-tests:
  stage: test
  needs: [build]
  script:
    - dotnet test -c Release --no-build --logger "junit;LogFilePath=report.xml" --collect:"XPlat Code Coverage"
  coverage: '/Total\s+\|\s+(\d+\.?\d*)%/'
  artifacts:
    when: always
    reports:
      junit: "**/report.xml"
      coverage_report: { coverage_format: cobertura, path: "**/coverage.cobertura.xml" }
 
docker-image:
  stage: package
  image: docker:27
  services: [docker:27-dind]
  variables: { DOCKER_TLS_CERTDIR: "/certs" }
  before_script:
    - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
  script:
    - docker build --pull -t $IMAGE:$CI_COMMIT_SHORT_SHA -t $IMAGE:$CI_COMMIT_REF_SLUG .
    - docker push --all-tags $IMAGE
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_COMMIT_TAG
 
deploy-staging:
  stage: deploy
  environment: { name: staging, url: https://staging.example.com }
  script: ./scripts/deploy.sh staging $CI_COMMIT_SHORT_SHA
  rules: [{ if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH }]
 
deploy-prod:
  stage: deploy
  environment: { name: production, url: https://example.com }
  script: ./scripts/deploy.sh production $CI_COMMIT_TAG
  rules: [{ if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/, when: manual }]
  resource_group: production        # не более одного деплоя одновременно

Основные ключевые слова

СловоНазначение
stagesпорядок стадий
image, servicesконтейнер для выполнения задания и сопутствующие сервисы (БД для тестов)
script, before_script, after_scriptкоманды
variablesпеременные
rulesусловия включения задания (if, changes, exists, when)
needsзависимости между заданиями (DAG) — не ждать всей стадии
artifactsфайлы между заданиями и для скачивания; reports (junit, coverage, sast, dotenv)
cacheкэш между пайплайнами (key, paths, policy)
environmentокружение деплоя, URL, откат, on_stop
whenon_success, manual, always, delayed
allow_failureне блокирует пайплайн
parallel, parallel:matrixразмножение заданий
extends, !reference, include, .hidden шаблоныпереиспользование
triggerдочерние и мультипроектные пайплайны
resource_groupсериализация деплоев
interruptibleотменять устаревшие пайплайны
retry, timeoutповторы и таймауты

rules и workflow

workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
      when: never                   # не дублировать ветка+MR-пайплайн
    - if: $CI_COMMIT_BRANCH
    - if: $CI_COMMIT_TAG
 
lint-frontend:
  rules:
    - changes: [web/**/*]           # только если менялся фронтенд

only/except — устаревшее; используйте rules.

Переиспользование

include:
  - project: platform/ci-templates
    ref: v2.1.0
    file: /templates/dotnet.yml
  - template: Security/SAST.gitlab-ci.yml
 
.deploy-base:
  image: registry.example.com/tools/kubectl:1.31
  before_script: [kubectl config use-context $KUBE_CONTEXT]
 
deploy-dev:
  extends: .deploy-base
  script: [helm upgrade --install api ./chart --set image.tag=$CI_COMMIT_SHORT_SHA]

CI/CD components (каталог) и шаблоны в отдельном репозитории — единые стандарты для всех сервисов.

Runners

Runner — агент, выполняющий задания. Executors: docker (типовой), kubernetes (каждое задание — под), shell, docker+machine/autoscaler (облачные ВМ).

  • shared (общие) и project/group (выделенные), теги (tags: [docker, gpu]) для выбора раннера;
  • protected runners: только для защищённых веток/тегов (прод-деплой);
  • регистрация: токен аутентификации, config.toml (concurrent, [runners.docker] privileged, volumes);
  • Docker-in-Docker (privileged = true) — риск безопасности; альтернативы: Kaniko, BuildKit rootless, Buildah, socket binding (осторожно);
  • автомасштабирование и эфемерные раннеры; ограничение ресурсов.

Кэш и артефакты

  • cache: ускорение (зависимости), «best effort», может отсутствовать; key по lock-файлу; policy: pull-push / pull;
  • artifacts: гарантированная передача результатов между заданиями; expire_in; reports;
  • кэш Docker-слоёв: --cache-from из реестра или BuildKit cache.

Переменные и секреты

  • предопределённые: CI_COMMIT_SHA, CI_COMMIT_SHORT_SHA, CI_COMMIT_REF_SLUG, CI_COMMIT_TAG, CI_PIPELINE_ID, CI_REGISTRY*, CI_JOB_TOKEN, CI_ENVIRONMENT_NAME, CI_MERGE_REQUEST_IID, CI_DEFAULT_BRANCH;
  • CI/CD Variables (Settings → CI/CD): Masked (скрыть в логах), Protected (только для защищённых веток/тегов), File-тип, scope по окружению;
  • секреты — из HashiCorp Vault/облачного хранилища (id_tokens + OIDC) вместо долгоживущих паролей;
  • не печатать секреты (set +x); CI_JOB_TOKEN с минимальными правами.

Окружения и review apps

environment: name: review/$CI_COMMIT_REF_SLUG + on_stop создаёт временное окружение под MR и удаляет его по закрытию; страница Environments показывает деплои и позволяет откат (повторный запуск прошлого деплоя). Protected environments — ручное подтверждение и список допустимых пользователей.

Практики

  • короткий MR-пайплайн, interruptible для отмены устаревших;
  • needs для DAG: ускорение;
  • parallel:matrix: тесты на нескольких версиях;
  • сохранение отчётов (JUnit, покрытие, SAST, контейнерное сканирование) — виджеты в MR;
  • Merge Trains и merge request pipelines — проверка результата слияния;
  • needs:project, trigger для мультирепо;
  • линтинг CI-файла (CI Lint, glab ci lint).

Вопросы с ответами