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— для Pythonphp:8.2— для PHPdocker:latest— для работы с Dockeralpine: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'ов
- Shared Runners — предоставляются GitLab.com, общие для всех
- Group Runners — для группы проектов
- 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_ID | ID пайплайна |
CI_JOB_ID | ID джоба |
CI_PROJECT_NAME | Имя проекта |
CI_PROJECT_DIR | Путь к проекту |
CI_REGISTRY | URL Docker registry |
CI_REGISTRY_IMAGE | Путь к образу в registry |
GITLAB_USER_EMAIL | Email автора коммита |
Полный список: 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
- Быстрый feedback — ставьте быстрые проверки (lint) в начало
- Fail fast — если тесты падают, не продолжайте деплой
- Кэшируйте зависимости — экономит время и трафик
- Используйте артефакты — не собирайте код дважды
- Секреты в переменных — никогда не храните пароли в .gitlab-ci.yml
- Маленькие образы — используйте alpine версии
- Шаблоны — выносите общую логику в extends
- Документация — комментируйте сложные места в конфиге
Заключение
CI/CD в GitLab — мощный инструмент, который автоматизирует рутину и повышает качество кода. Начните с простого пайплайна и постепенно добавляйте новые возможности.
Комментарии
Войдите, чтобы оставить комментарий
Пока нет комментариев. Будьте первым!