DevOps

CI/CD в GitLab: руководство для начинающих

BorisBoris
|
7 января 2026 г.
|
10 мин чтения
|
125 просмотров
CI/CD в GitLab: руководство для начинающих

Что такое CI/CD?

CI (Continuous Integration) — непрерывная интеграция. Это практика, при которой разработчики регулярно (несколько раз в день) объединяют свой код в общий репозиторий. Каждое такое объединение автоматически проверяется: запускаются тесты, линтеры, сборка проекта.

CD (Continuous Delivery/Deployment) — непрерывная доставка или развёртывание. Это автоматическая доставка кода на тестовые или продакшен-серверы после успешного прохождения всех проверок.

Зачем это нужно?

Без CI/CD:

  • Разработчик пишет код → вручную тестирует → вручную заливает на сервер
  • Ошибки обнаруживаются поздно
  • Деплой занимает часы
  • «У меня на компьютере работает» — классическая проблема

С CI/CD:

  • Код автоматически проверяется при каждом коммите
  • Ошибки обнаруживаются сразу
  • Деплой занимает минуты
  • Все работают в одинаковом окружении

Как устроен CI/CD в GitLab

Основные понятия

ТерминОписание
PipelineНабор задач, которые выполняются для вашего кода. Запускается при каждом push
StageЭтап пайплайна (например: build, test, deploy). Этапы выполняются последовательно
JobКонкретная задача внутри этапа. Джобы одного этапа выполняются параллельно
RunnerСервер, который выполняет джобы. Может быть shared (общий) или собственный
ArtifactФайлы, созданные во время джоба и сохранённые для следующих этапов

Визуальное представление

Pipeline
├── Stage: build
│   └── Job: compile
├── Stage: test
│   ├── Job: unit-tests
│   └── Job: integration-tests
└── Stage: deploy
    └── Job: deploy-production

Файл .gitlab-ci.yml

Всё начинается с файла .gitlab-ci.yml в корне репозитория. Это конфигурация вашего пайплайна.

Минимальный пример

# Определяем этапы (порядок важен!)
stages:
  - build
  - test
  - deploy

# Джоб сборки
build-job:
  stage: build
  script:
    - echo "Собираем проект..."
    - npm install
    - npm run build

# Джоб тестирования
test-job:
  stage: test
  script:
    - echo "Запускаем тесты..."
    - npm test

# Джоб деплоя
deploy-job:
  stage: deploy
  script:
    - echo "Деплоим на сервер..."

Разбор синтаксиса

имя-джоба:           # Произвольное имя
  stage: test        # К какому этапу относится
  script:            # Команды для выполнения (обязательно)
    - команда 1
    - команда 2

Подробный разбор возможностей

1. Образы Docker (image)

GitLab Runner выполняет джобы в Docker-контейнерах. Вы можете указать, какой образ использовать:

# Глобальный образ для всех джобов
image: node:20

# Или для конкретного джоба
build-job:
  image: node:20-alpine
  script:
    - npm install
    - npm run build

Популярные образы:

  • node:20 — для Node.js проектов
  • python:3.11 — для Python
  • php:8.2 — для PHP
  • docker:latest — для работы с Docker
  • alpine:latest — минимальный Linux

2. Переменные (variables)

# Глобальные переменные
variables:
  NODE_ENV: production
  API_URL: https://api.example.com

build-job:
  # Переменные для конкретного джоба
  variables:
    DEBUG: "true"
  script:
    - echo $NODE_ENV
    - echo $API_URL

Секретные переменные (пароли, токены) задаются в настройках GitLab: Settings → CI/CD → Variables

deploy-job:
  script:
    # $SSH_PRIVATE_KEY задана в настройках GitLab
    - echo "$SSH_PRIVATE_KEY" > key.pem
    - chmod 600 key.pem
    - ssh -i key.pem user@server "deploy.sh"

3. Кэширование (cache)

Кэш сохраняет файлы между запусками пайплайна (например, node_modules):

build-job:
  cache:
    key: ${CI_COMMIT_REF_SLUG}  # Ключ кэша (по имени ветки)
    paths:
      - node_modules/
      - .npm/
  script:
    - npm ci
    - npm run build

Варианты ключей кэша:

cache:
  key: fixed-key                    # Один кэш на весь проект
  key: ${CI_COMMIT_REF_SLUG}        # Кэш для каждой ветки
  key: ${CI_JOB_NAME}               # Кэш для каждого джоба
  key:
    files:
      - package-lock.json           # Кэш меняется при изменении файла

4. Артефакты (artifacts)

Артефакты — файлы, которые передаются между джобами или сохраняются для скачивания:

build-job:
  stage: build
  script:
    - npm run build
  artifacts:
    paths:
      - dist/                # Папка для сохранения
    expire_in: 1 week        # Срок хранения

deploy-job:
  stage: deploy
  script:
    - ls dist/               # Артефакты доступны в следующих этапах
    - rsync -av dist/ server:/var/www/

5. Условия выполнения (rules/only/except)

Современный способ — rules:

deploy-production:
  stage: deploy
  script:
    - ./deploy.sh production
  rules:
    # Только для main ветки
    - if: $CI_COMMIT_BRANCH == "main"

    # Только при наличии тега
    - if: $CI_COMMIT_TAG

    # Только при изменении определённых файлов
    - changes:
        - src/**/*
        - package.json

deploy-staging:
  stage: deploy
  script:
    - ./deploy.sh staging
  rules:
    # Для всех веток, кроме main
    - if: $CI_COMMIT_BRANCH != "main"

Ручной запуск:

deploy-production:
  stage: deploy
  script:
    - ./deploy.sh
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual  # Требует ручного подтверждения в интерфейсе

6. Зависимости между джобами (needs/dependencies)

По умолчанию джобы ждут завершения всего предыдущего этапа. С needs можно это изменить:

stages:
  - build
  - test
  - deploy

build-frontend:
  stage: build
  script: npm run build:frontend
  artifacts:
    paths: [dist/frontend/]

build-backend:
  stage: build
  script: npm run build:backend
  artifacts:
    paths: [dist/backend/]

test-frontend:
  stage: test
  needs: [build-frontend]  # Не ждёт build-backend
  script: npm run test:frontend

test-backend:
  stage: test
  needs: [build-backend]   # Не ждёт build-frontend
  script: npm run test:backend

deploy:
  stage: deploy
  needs: [test-frontend, test-backend]
  script: ./deploy.sh

7. Сервисы (services)

Сервисы — дополнительные контейнеры, которые запускаются вместе с джобом:

test-job:
  image: node:20
  services:
    - postgres:15
    - redis:7
  variables:
    POSTGRES_DB: test_db
    POSTGRES_USER: test
    POSTGRES_PASSWORD: test
    DATABASE_URL: postgres://test:test@postgres:5432/test_db
    REDIS_URL: redis://redis:6379
  script:
    - npm test

8. Before/After script

default:
  before_script:
    - echo "Выполняется перед каждым джобом"
    - npm ci
  after_script:
    - echo "Выполняется после каждого джоба"
    - rm -rf node_modules

test-job:
  script:
    - npm test

9. Шаблоны и наследование (extends)

# Базовый шаблон (начинается с точки — не выполняется)
.node-template:
  image: node:20
  cache:
    paths:
      - node_modules/
  before_script:
    - npm ci

# Джобы наследуют шаблон
test-unit:
  extends: .node-template
  script:
    - npm run test:unit

test-e2e:
  extends: .node-template
  script:
    - npm run test:e2e

10. Include (подключение внешних файлов)

# Локальный файл
include:
  - local: '/.gitlab/ci/build.yml'
  - local: '/.gitlab/ci/test.yml'
  - local: '/.gitlab/ci/deploy.yml'

# Файл из другого репозитория
include:
  - project: 'company/ci-templates'
    file: '/templates/node.yml'

# Удалённый файл
include:
  - remote: 'https://example.com/ci-template.yml'

Практический пример: Node.js приложение

Полный пример для реального проекта:

image: node:20

stages:
  - install
  - lint
  - test
  - build
  - deploy

variables:
  npm_config_cache: "$CI_PROJECT_DIR/.npm"

# Кэширование для всех джобов
cache:
  key:
    files:
      - package-lock.json
  paths:
    - .npm/
    - node_modules/

# Установка зависимостей
install:
  stage: install
  script:
    - npm ci
  artifacts:
    paths:
      - node_modules/
    expire_in: 1 hour

# Проверка кода линтером
lint:
  stage: lint
  needs: [install]
  script:
    - npm run lint
  allow_failure: true  # Пайплайн продолжится даже при ошибке

# Юнит-тесты
unit-tests:
  stage: test
  needs: [install]
  script:
    - npm run test:unit -- --coverage
  coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'  # Парсинг покрытия
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

# Интеграционные тесты
integration-tests:
  stage: test
  needs: [install]
  services:
    - mongo:6
  variables:
    MONGODB_URI: mongodb://mongo:27017/test
  script:
    - npm run test:integration

# Сборка
build:
  stage: build
  needs: [install]
  script:
    - npm run build
  artifacts:
    paths:
      - .output/
      - dist/
    expire_in: 1 day

# Деплой на staging (автоматически для develop)
deploy-staging:
  stage: deploy
  needs: [lint, unit-tests, integration-tests, build]
  environment:
    name: staging
    url: https://staging.example.com
  script:
    - apt-get update && apt-get install -y rsync
    - rsync -avz --delete .output/ $STAGING_USER@$STAGING_HOST:/var/www/staging/
  rules:
    - if: $CI_COMMIT_BRANCH == "develop"

# Деплой на production (вручную для main)
deploy-production:
  stage: deploy
  needs: [lint, unit-tests, integration-tests, build]
  environment:
    name: production
    url: https://example.com
  script:
    - apt-get update && apt-get install -y rsync
    - rsync -avz --delete .output/ $PROD_USER@$PROD_HOST:/var/www/production/
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual
  allow_failure: false

Environments (Окружения)

GitLab позволяет отслеживать деплои через окружения:

deploy-staging:
  stage: deploy
  environment:
    name: staging
    url: https://staging.example.com
    on_stop: stop-staging  # Джоб для остановки окружения
  script:
    - ./deploy.sh staging

stop-staging:
  stage: deploy
  environment:
    name: staging
    action: stop
  script:
    - ./stop.sh staging
  when: manual

В интерфейсе GitLab: Operations → Environments — список всех окружений с историей деплоев.


GitLab Runner

Типы Runner'ов

  1. Shared Runners — предоставляются GitLab.com, общие для всех
  2. Group Runners — для группы проектов
  3. Project Runners — для конкретного проекта

Установка своего Runner

# На Ubuntu/Debian
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
sudo apt install gitlab-runner

# Регистрация runner'а
sudo gitlab-runner register

При регистрации понадобится:

  • URL GitLab (например, https://gitlab.com)
  • Registration token (Settings → CI/CD → Runners)
  • Executor (docker, shell, kubernetes и др.)

Теги Runner'ов

deploy-job:
  tags:
    - docker
    - production  # Только runner'ы с этими тегами
  script:
    - ./deploy.sh

Предопределённые переменные

GitLab автоматически создаёт множество переменных:

ПеременнаяОписание
CI_COMMIT_SHAПолный хэш коммита
CI_COMMIT_SHORT_SHAКороткий хэш (8 символов)
CI_COMMIT_BRANCHИмя ветки
CI_COMMIT_TAGИмя тега (если есть)
CI_COMMIT_MESSAGEСообщение коммита
CI_PIPELINE_IDID пайплайна
CI_JOB_IDID джоба
CI_PROJECT_NAMEИмя проекта
CI_PROJECT_DIRПуть к проекту
CI_REGISTRYURL Docker registry
CI_REGISTRY_IMAGEПуть к образу в registry
GITLAB_USER_EMAILEmail автора коммита

Полный список: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html


Docker в CI/CD

Сборка Docker-образа

build-docker:
  image: docker:24
  services:
    - docker:24-dind  # Docker-in-Docker
  variables:
    DOCKER_TLS_CERTDIR: "/certs"
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

Использование Kaniko (без privileged mode)

build-docker:
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n $CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD | base64)\"}}}" > /kaniko/.docker/config.json
    - /kaniko/executor
      --context $CI_PROJECT_DIR
      --dockerfile $CI_PROJECT_DIR/Dockerfile
      --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

Полезные паттерны

1. Merge Request пайплайны

workflow:
  rules:
    # Пайплайн для MR
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    # Пайплайн для веток main и develop
    - if: $CI_COMMIT_BRANCH =~ /^(main|develop)$/

2. Пропуск CI

Добавьте в сообщение коммита:

  • [ci skip] или [skip ci] — пропустить пайплайн
  • [skip ci] в заголовке MR — пропустить для всех коммитов MR

3. Retry при падении

test-flaky:
  script:
    - npm run test:e2e
  retry:
    max: 2
    when:
      - runner_system_failure
      - stuck_or_timeout_failure

4. Timeout

long-running-job:
  script:
    - ./long-script.sh
  timeout: 3 hours  # Максимальное время выполнения

5. Параллельное выполнение

test:
  parallel: 5  # Запустит 5 экземпляров джоба
  script:
    - npm run test -- --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL

Отладка пайплайнов

1. Локальный запуск

# Установка gitlab-runner локально
gitlab-runner exec docker job-name

2. CI Lint

В GitLab: CI/CD → Editor → Validate

Или через API:

curl --header "PRIVATE-TOKEN: $TOKEN" \
  "https://gitlab.com/api/v4/ci/lint" \
  --form "content=@.gitlab-ci.yml"

3. Debug mode

test-job:
  variables:
    CI_DEBUG_TRACE: "true"  # Выводит все команды
  script:
    - npm test

Best Practices

  1. Быстрый feedback — ставьте быстрые проверки (lint) в начало
  2. Fail fast — если тесты падают, не продолжайте деплой
  3. Кэшируйте зависимости — экономит время и трафик
  4. Используйте артефакты — не собирайте код дважды
  5. Секреты в переменных — никогда не храните пароли в .gitlab-ci.yml
  6. Маленькие образы — используйте alpine версии
  7. Шаблоны — выносите общую логику в extends
  8. Документация — комментируйте сложные места в конфиге

Заключение

CI/CD в GitLab — мощный инструмент, который автоматизирует рутину и повышает качество кода. Начните с простого пайплайна и постепенно добавляйте новые возможности.

Полезные ссылки

Комментарии

Войдите, чтобы оставить комментарий

Пока нет комментариев. Будьте первым!