Часть3: CI/CD – ветки, условия, секреты и окружения
Введение
Если вы дошли до этой части в серии, то вы уже знакомы с базовыми принципами CI/CD и даже настроили свой первый простой пайплайн. Поздравляю! Вы сделали важный шаг в мир автоматизации разработки. Но как и в программировании, где после "Hello World" начинается настоящее обучение, в CI/CD после базовой настройки открывается целый мир возможностей.
Если вы попали сюда случайно, или не видели первых двух частей, то рекомендую ознакомиться:
Часть 1: Основы CI/CD – что это и зачем нужно; GitHub Actions и GitLab CI
Часть 2: Настройка GitHub Actions и GitLab CI – первый workflow и деплой
Часть3: CI/CD – ветки, условия, секреты и окружения
^ Вы сейчас здесь ^
В этой статье мы поговорим о том, как сделать ваш CI/CD по-настоящему гибким и мощным. Мы рассмотрим:
Ветвление и условия — как заставить пайплайн вести себя по-разному в зависимости от ветки или других условий
Секреты и переменные окружения — как безопасно хранить и использовать чувствительные данные
Окружения и этапы — как организовать многоступенчатый процесс с разными средами выполнения
Отладку и разбор ошибок — как понять, что пошло не так, и быстро это исправить
Эти темы критически важны для любого реального проекта. Согласитесь, в продакшн вы хотите деплоить только проверенный код из основной ветки, а не каждый экспериментальный коммит. Для доступа к серверам вам нужны пароли и токены, которые нельзя хранить в открытом виде. А для полноценного процесса вам нужны разные окружения — разработка, тестирование, продакшн.
Не волнуйтесь, если некоторые концепции кажутся сложными. Мы разберём всё пошагово, с примерами и объяснениями. К концу статьи вы будете уверенно настраивать продвинутые CI/CD-конфигурации и сможете автоматизировать практически любой процесс разработки.
Итак, пристегните ремни — мы отправляемся в путешествие по миру продвинутого CI/CD, где ваш код сам себя проверяет, тестирует и деплоит именно так, как вам нужно. И помните: мы превращаем наш "Hello World" workflow во взрослую программу, которая умеет принимать решения, хранить секреты и работать в разных окружениях.
Ветвление и условия в workflow
Концепция ветвления в Git и CI/CD
Если вы когда-нибудь работали с Git, то знаете, что ветки — это одна из его самых мощных возможностей. Они позволяют разработчикам работать параллельно над разными задачами, не мешая друг другу. Типичный проект может иметь десятки активных веток одновременно:
main
(илиmaster
) — стабильная версия, готовая к использованиюdevelop
— ветка разработки, куда сливаются все новые функцииfeature/*
— ветки для отдельных функцийhotfix/*
— срочные исправления критических ошибокrelease/*
— подготовка к выпуску новой версии
Но что это значит для CI/CD? А то, что не все ветки равны! Представьте, что у вас есть магазин одежды. Вы же не будете выставлять на продажу каждый незавершённый эскиз от дизайнера? Точно так же не стоит деплоить в продакшн каждый коммит из каждой ветки.
Вот где на сцену выходит условное выполнение в CI/CD. Оно позволяет настроить разное поведение пайплайна в зависимости от ветки или других условий:
Для веток
feature/*
— только сборка и базовые тестыДля ветки
develop
— полное тестирование и деплой на тестовый серверДля ветки
main
— полное тестирование и деплой на продакшн
Это как если бы у вас был умный конвейер на фабрике, который автоматически определяет, что делать с каждым изделием в зависимости от его маркировки.
Реализация в GitHub Actions
В GitHub Actions условное выполнение можно настроить на двух уровнях:
На уровне workflow — когда запускать весь пайплайн
На уровне job или step — какие задачи выполнять внутри запущенного пайплайна
Фильтрация на уровне workflow
Чтобы запускать workflow только для определённых веток, используйте секцию on
:
name: CI Pipeline
on:
push:
branches: [ main, develop, 'feature/**' ]
pull_request:
branches: [ main, develop ]
Этот код говорит: "Запускай пайплайн при пуше в ветки main, develop или любую ветку, начинающуюся с feature/, а также при создании PR в main или develop".
Обратите внимание на синтаксис 'feature/**'
— это шаблон (pattern), который соответствует всем веткам, начинающимся с feature/
. Звёздочки здесь работают как подстановочные знаки.
Условия внутри workflow
Для более гибкого контроля используйте условие if
на уровне job или step:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: npm test
deploy-to-staging:
runs-on: ubuntu-latest
needs: test
if: github.ref == 'refs/heads/develop'
steps:
- uses: actions/checkout@v3
- name: Deploy to staging
run: ./deploy.sh staging
deploy-to-production:
runs-on: ubuntu-latest
needs: test
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Deploy to production
run: ./deploy.sh production
В этом примере тесты запускаются для всех веток, но деплой на staging происходит только для ветки develop
, а деплой на production — только для ветки main
.
Выражение github.ref == 'refs/heads/main'
проверяет, является ли текущая ветка main. GitHub Actions предоставляет множество контекстных переменных, которые можно использовать в условиях:
github.ref
— полное имя ссылки (например,refs/heads/main
илиrefs/tags/v1.0.0
)github.event_name
— тип события (например,push
,pull_request
)github.actor
— пользователь, инициировавший событиеи многие другие
Вы можете комбинировать условия с помощью операторов &&
(И) и ||
(ИЛИ):
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
Это условие будет истинным только для пушей в ветку main.
Реализация в GitLab CI
GitLab CI предлагает похожие, но немного отличающиеся механизмы для условного выполнения.
Простой способ: only/except
Традиционный способ фильтрации в GitLab CI — использование ключевых слов only
и except
:
stages:
- test
- deploy
test:
stage: test
script:
- npm test
deploy-staging:
stage: deploy
script:
- ./deploy.sh staging
only:
- develop
deploy-production:
stage: deploy
script:
- ./deploy.sh production
only:
- main
Здесь only: - develop
означает "выполнять только для ветки develop". Аналогично, except
указывает, для каких веток задачу выполнять НЕ нужно.
Продвинутый способ: rules
Более новый и гибкий подход — использование rules
:
deploy-staging:
stage: deploy
script:
- ./deploy.sh staging
rules:
- if: $CI_COMMIT_BRANCH == "develop"
when: on_success
- when: never
Секция rules
содержит список правил, которые проверяются по порядку. Первое совпавшее правило определяет, будет ли выполняться задача. В этом примере задача выполнится, если ветка — develop
и предыдущие шаги успешны. В противном случае сработает правило when: never
, и задача будет пропущена.
GitLab CI предоставляет множество предопределённых переменных:
$CI_COMMIT_BRANCH
— имя ветки$CI_PIPELINE_SOURCE
— источник запуска пайплайна (например,push
,merge_request_event
)$CI_COMMIT_TAG
— имя тега (если есть)и многие другие
Вы можете создавать сложные условия, комбинируя эти переменные:
rules:
- if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"
when: on_success
- if: $CI_COMMIT_BRANCH =~ /^feature\/.*/
when: manual
- when: never
Это правило говорит: "Выполнять автоматически для пушей в main, требовать ручного запуска для веток feature/*
, и пропускать для всех остальных случаев".
Практический пример полного workflow
Давайте рассмотрим более полный пример, который демонстрирует мощь условного выполнения. Представим, что у нас есть веб-приложение, и мы хотим настроить следующий процесс:
Для всех веток — сборка и базовые тесты
Для веток
feature/*
— дополнительно линтинг и проверка типовДля ветки
develop
— всё вышеперечисленное + e2e-тесты + деплой на тестовый серверДля ветки
main
— всё вышеперечисленное + деплой на продакшн (с ручным подтверждением)
GitHub Actions
name: CI/CD Pipeline
on:
push:
branches: [ main, develop, 'feature/**' ]
pull_request:
branches: [ main, develop ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Run basic tests
run: npm test
lint-and-typecheck:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/heads/feature/')
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npm run typecheck
e2e-tests:
runs-on: ubuntu-latest
needs: [build]
if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Run E2E tests
run: npm run test:e2e
deploy-staging:
runs-on: ubuntu-latest
needs: [build, e2e-tests]
if: github.ref == 'refs/heads/develop'
steps:
- uses: actions/checkout@v3
- name: Deploy to staging
run: |
echo "Deploying to staging environment..."
# Здесь команды для деплоя на тестовый сервер
deploy-production:
runs-on: ubuntu-latest
needs: [build, e2e-tests]
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v3
- name: Deploy to production
run: |
echo "Deploying to production environment..."
# Здесь команды для деплоя на продакшн
GitLab CI
stages:
- build
- test
- deploy
build:
stage: build
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
basic-tests:
stage: test
script:
- npm test
lint-and-typecheck:
stage: test
script:
- npm run lint
- npm run typecheck
rules:
- if: $CI_COMMIT_BRANCH =~ /^feature\/.*/
when: on_success
- when: never
e2e-tests:
stage: test
script:
- npm run test:e2e
rules:
- if: $CI_COMMIT_BRANCH == "develop" || $CI_COMMIT_BRANCH == "main"
when: on_success
- when: never
deploy-staging:
stage: deploy
script:
- echo "Deploying to staging environment..."
# Здесь команды для деплоя на тестовый сервер
environment:
name: staging
url: https://staging.example.com
rules:
- if: $CI_COMMIT_BRANCH == "develop"
when: on_success
- when: never
deploy-production:
stage: deploy
script:
- echo "Deploying to production environment..."
# Здесь команды для деплоя на продакшн
environment:
name: production
url: https://example.com
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
- when: never
Обратите внимание, как в GitLab CI мы используем when: manual
для деплоя на продакшн, что требует ручного подтверждения.
Визуализация процесса принятия решений
Чтобы лучше понять, как работает условное выполнение, представьте его как блок-схему:
Событие (пуш, PR) → Запуск workflow
-
Проверка ветки:
Если
feature/*
→ Сборка + Базовые тесты + ЛинтингЕсли
develop
→ Сборка + Все тесты + Деплой на тестовый серверЕсли
main
→ Сборка + Все тесты + Деплой на продакшн (с подтверждением)
Такой подход позволяет автоматизировать процесс разработки, при этом обеспечивая необходимый уровень контроля и безопасности.
Веток в Git-репозитории может быть больше, чем веток на новогодней ёлке — CI/CD помогает упорядочить их и обеспечить правильную обработку каждой из них, позволяет точно определить, какие ветки и изменения можно запускать, а какие — должны пройти ручную проверку.
Секреты и переменные окружения
Безопасность в CI/CD
Представьте, что вы строите дом. Вы же не оставите ключи от входной двери под ковриком, где их может найти любой прохожий? Точно так же в мире CI/CD есть данные, которые нельзя хранить в открытом виде: пароли от серверов, токены доступа к API, приватные ключи для подписи кода и многое другое.
Проблема в том, что конфигурационные файлы CI/CD (.github/workflows/*.yml
или .gitlab-ci.yml
) хранятся в репозитории, который может быть публичным или к которому имеет доступ множество людей. Если вы напрямую запишете пароль в такой файл, он станет доступен всем, кто имеет доступ к репозиторию. Это всё равно что написать свой PIN-код на банковской карте.
Вот типичные виды чувствительных данных, которые требуют защиты:
Пароли — для доступа к серверам, базам данных и т.д.
API-токены — для взаимодействия с внешними сервисами
SSH-ключи — для безопасного подключения к серверам
Сертификаты — для подписи кода или установки защищённых соединений
Учётные данные облачных провайдеров — для управления ресурсами в AWS, Azure, GCP и т.д.
К счастью, и GitHub Actions, и GitLab CI предоставляют механизмы для безопасного хранения и использования таких данных — секреты и переменные окружения.
Управление секретами в GitHub Actions
GitHub Actions позволяет хранить секреты на трёх уровнях:
Уровень репозитория — секреты доступны только в конкретном репозитории
Уровень организации — секреты доступны во всех репозиториях организации
Уровень окружения — секреты доступны только в определённом окружении (например, production)
Добавление секрета на уровне репозитория
Перейдите в свой репозиторий на GitHub
Нажмите на вкладку "Settings"
В боковом меню выберите "Secrets and variables" → "Actions"
Нажмите "New repository secret"
Введите имя секрета (например,
DATABASE_PASSWORD
) и его значениеНажмите "Add secret"
Использование секретов в workflow
После добавления секрета вы можете использовать его в своём workflow через синтаксис ${{ secrets.SECRET_NAME }}
:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to server
run: |
sshpass -p ${{ secrets.SERVER_PASSWORD }} ssh user@example.com "cd /var/www && git pull"
Важно понимать, что GitHub автоматически маскирует секреты в логах. Если секрет случайно попадёт в вывод команды, GitHub заменит его звёздочками. Однако это не работает, если вы намеренно выводите секрет или его часть, например, через echo ${{ secrets.API_TOKEN }}
.
Секреты на уровне организации
Если у вас есть организация на GitHub и несколько репозиториев используют одни и те же секреты, удобнее настроить их на уровне организации:
Перейдите на страницу вашей организации
Нажмите на вкладку "Settings"
В боковом меню выберите "Secrets and variables" → "Actions"
Нажмите "New organization secret"
Введите имя и значение секрета
Выберите, к каким репозиториям применить этот секрет
Нажмите "Add secret"
Секреты на уровне окружения
Для особо чувствительных данных, таких как ключи от продакшн-серверов, лучше использовать секреты на уровне окружения:
В репозитории перейдите в "Settings" → "Environments"
Создайте новое окружение или выберите существующее
Нажмите "Add secret" и добавьте секрет
В workflow укажите окружение для job-а:
jobs:
deploy-production:
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy to production
run: ./deploy.sh ${{ secrets.PRODUCTION_API_KEY }}
Преимущество этого подхода в том, что вы можете настроить дополнительные защитные меры для окружения, например, требовать ручное одобрение перед выполнением.
Управление секретами в GitLab CI
GitLab CI использует термин "переменные" (variables) вместо "секреты", но концепция та же. Переменные могут быть защищёнными (protected) и/или маскированными (masked).
Защищённые переменные доступны только в защищённых ветках и тегах
Маскированные переменные автоматически скрываются в логах
Добавление переменных на уровне проекта
Перейдите в свой проект на GitLab
Нажмите на "Settings" → "CI/CD"
Разверните секцию "Variables"
Нажмите "Add variable"
Введите ключ (например,
DATABASE_PASSWORD
) и значениеПри необходимости отметьте "Protect variable" и/или "Mask variable"
Нажмите "Add variable"
Использование переменных в pipeline
После добавления переменной вы можете использовать её в своём .gitlab-ci.yml
:
deploy:
stage: deploy
script:
- sshpass -p $DATABASE_PASSWORD ssh user@example.com "cd /var/www && git pull"
Обратите внимание, что в GitLab CI переменные используются напрямую через $VARIABLE_NAME
, без дополнительного синтаксиса.
Переменные на уровне группы
Если у вас есть группа проектов в GitLab, вы можете настроить переменные на уровне группы:
Перейдите на страницу вашей группы
Нажмите на "Settings" → "CI/CD"
Разверните секцию "Variables"
Добавьте переменные так же, как на уровне проекта
Переменные группы будут доступны во всех проектах этой группы, но их можно переопределить на уровне проекта.
Типы переменных в GitLab CI
GitLab CI поддерживает два типа переменных:
Переменные среды (environment variables) — доступны как переменные окружения в скриптах
Файловые переменные (file variables) — сохраняются как файлы в директории сборки
Файловые переменные особенно полезны для хранения сертификатов, ключей SSH и других данных, которые обычно хранятся в файлах:
deploy:
stage: deploy
script:
- ssh -i $SSH_PRIVATE_KEY_FILE user@example.com "cd /var/www && git pull"
Практические примеры использования секретов
Давайте рассмотрим несколько реальных сценариев использования секретов в CI/CD.
Пример 1: Деплой на сервер через SSH
GitHub Actions:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install SSH key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.SSH_PRIVATE_KEY }}
known_hosts: ${{ secrets.KNOWN_HOSTS }}
- name: Deploy to server
run: |
ssh user@example.com "cd /var/www && git pull && npm ci && npm run build"
GitLab CI:
deploy:
stage: deploy
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- ssh user@example.com "cd /var/www && git pull && npm ci && npm run build"
Пример 2: Публикация пакета в npm
GitHub Actions:
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
GitLab CI:
publish:
stage: deploy
script:
- echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
- npm ci
- npm publish
Пример 3: Деплой в облачный сервис (AWS)
GitHub Actions:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy to S3
run: aws s3 sync ./dist s3://my-bucket/
GitLab CI:
deploy:
stage: deploy
image: amazon/aws-cli
script:
- aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
- aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
- aws configure set region us-east-1
- aws s3 sync ./dist s3://my-bucket/
Лучшие практики работы с секретами
Минимизируйте доступ — предоставляйте минимально необходимые права. Например, для деплоя на сервер используйте отдельного пользователя с ограниченными правами.
Регулярно обновляйте секреты — периодически меняйте пароли и токены, особенно если в команде были кадровые изменения.
Используйте временные токены — по возможности используйте токены с ограниченным сроком действия.
Проверяйте логи — убедитесь, что секреты не попадают в логи. Даже с автоматической маскировкой могут быть утечки.
Не хардкодьте секреты — никогда не записывайте секреты напрямую в код или конфигурационные файлы.
Используйте разные секреты для разных окружений — не используйте один и тот же токен для тестового и продакшн-окружений.
Ограничьте видимость секретов — используйте секреты на уровне окружений или с ограничением доступа к определённым веткам.
Секреты в CI/CD требуют такой же осторожности, как пароли или банковские данные — используйте механизмы защиты и избегайте попадания в открытый код. Правильная работа с секретами — это не просто вопрос удобства, это вопрос безопасности вашего проекта и данных ваших пользователей.
Окружения и этапы (стейджи)
Концепция окружений в разработке
Представьте, что вы шеф-повар, разрабатывающий новое блюдо. Вы же не будете сразу подавать его посетителям ресторана, верно? Сначала вы экспериментируете на кухне, затем даёте попробовать коллегам, и только после всех корректировок и одобрений блюдо попадает в меню.
В разработке программного обеспечения работает тот же принцип. Окружения (environments) — это различные среды, через которые проходит ваш код на пути от разработчика к конечному пользователю:
Development (разработка) — здесь разработчики экспериментируют и создают новые функции
Testing/QA (тестирование) — здесь тестировщики проверяют функциональность и ищут ошибки
Staging (предпродакшн) — максимально приближенная к продакшну среда для финальных проверок
Production (продакшн) — реальная среда, с которой взаимодействуют пользователи
Каждое окружение имеет свои особенности:
Разные серверы или облачные ресурсы
Разные базы данных (часто с тестовыми данными в непродакшн-окружениях)
Разные уровни доступа и безопасности
Разные конфигурации (например, более подробное логирование в dev)
Этапы (stages) — это последовательные шаги в процессе CI/CD, которые выполняются в определённом порядке:
Build (сборка) — компиляция кода, сборка артефактов
Test (тестирование) — запуск автоматических тестов
Deploy (развёртывание) — выкладка кода на серверы
Этапы и окружения тесно связаны: на этапе деплоя код может быть развёрнут в разные окружения в зависимости от условий.
Зачем это нужно? Представьте, что вы обнаружили критическую ошибку в продакшне. Что лучше:
Исправить её напрямую на продакшн-сервере, рискуя внести новые проблемы?
Исправить в коде, проверить на тестовом окружении, и только потом выкатить на продакшн?
Очевидно, второй вариант безопаснее. Правильно настроенные окружения и этапы в CI/CD помогают минимизировать риски и обеспечивают плавный процесс доставки кода от разработчика к пользователю.
Настройка окружений в GitHub Actions
GitHub Actions предлагает встроенную поддержку окружений, которая позволяет не только организовать деплои, но и добавить защитные правила.
Создание окружений
Окружения создаются в настройках репозитория:
Перейдите в репозиторий на GitHub
Нажмите на вкладку "Settings"
В боковом меню выберите "Environments"
Нажмите "New environment"
Введите имя окружения (например, "production")
Настройте защитные правила (при необходимости)
Нажмите "Configure environment"
Защитные правила для окружений
GitHub позволяет настроить несколько типов защиты:
Required reviewers (обязательные проверяющие) — люди, которые должны одобрить деплой
Wait timer (таймер ожидания) — задержка перед деплоем (например, 10 минут)
Deployment branches (ветки для деплоя) — ограничение веток, из которых можно деплоить
Environment secrets (секреты окружения) — секреты, доступные только в этом окружении
Использование окружений в workflow
После создания окружений вы можете использовать их в своём workflow:
name: Deploy
on:
push:
branches: [ main ]
jobs:
deploy-to-staging:
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v3
- name: Deploy to Staging
run: ./deploy.sh staging
deploy-to-production:
needs: deploy-to-staging
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v3
- name: Deploy to Production
run: ./deploy.sh production
В этом примере:
Деплой сначала идёт на staging, а затем на production (благодаря
needs: deploy-to-staging
)Для каждого окружения указан URL, который будет отображаться в интерфейсе GitHub
Если для production настроены защитные правила (например, обязательное одобрение), workflow будет ждать этого одобрения перед деплоем
Переменные окружения
Помимо секретов, вы можете использовать обычные переменные окружения:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
env:
NODE_ENV: production
API_URL: https://api.example.com
steps:
- uses: actions/checkout@v3
- name: Deploy
run: |
echo "Deploying with NODE_ENV=$NODE_ENV"
./deploy.sh
Переменные env
доступны всем шагам в job-е как обычные переменные окружения.
Настройка окружений в GitLab CI
GitLab CI имеет мощную поддержку как этапов (stages), так и окружений (environments).
Определение этапов
Этапы в GitLab CI определяются в начале файла .gitlab-ci.yml
:
stages:
- build
- test
- deploy
Это задаёт порядок выполнения: сначала все job-ы этапа build, затем test, и наконец deploy.
Определение окружений
Окружения определяются внутри job-ов:
deploy-staging:
stage: deploy
script:
- ./deploy.sh staging
environment:
name: staging
url: https://staging.example.com
Ключ environment
указывает, что этот job связан с окружением staging и предоставляет URL для доступа к нему.
Динамические окружения
Одна из мощных возможностей GitLab CI — динамические окружения, которые создаются автоматически, например, для каждой ветки или merge request:
deploy-review:
stage: deploy
script:
- ./deploy.sh review/$CI_COMMIT_REF_SLUG
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_COMMIT_REF_SLUG.review.example.com
on_stop: stop-review
only:
- branches
except:
- main
stop-review:
stage: deploy
script:
- ./cleanup.sh review/$CI_COMMIT_REF_SLUG
environment:
name: review/$CI_COMMIT_REF_SLUG
action: stop
when: manual
only:
- branches
except:
- main
В этом примере:
Для каждой ветки создаётся отдельное окружение
review/имя-ветки
URL формируется динамически на основе имени ветки
Добавлен job
stop-review
для удаления окружения, когда оно больше не нужно
Защита окружений
GitLab позволяет защитить окружения, ограничив, кто может деплоить в них:
Перейдите в проект на GitLab
Нажмите на "Settings" → "CI/CD"
Разверните секцию "Protected environments"
Нажмите "Protect an environment"
Выберите окружение и укажите, кто может деплоить в него
Ручное одобрение
Для критических окружений, таких как production, часто требуется ручное одобрение:
deploy-production:
stage: deploy
script:
- ./deploy.sh production
environment:
name: production
url: https://example.com
when: manual
only:
- main
Параметр when: manual
означает, что job не будет запущен автоматически — требуется ручной запуск через интерфейс GitLab.
Практический пример многоэтапного pipeline
Давайте рассмотрим более полный пример, который демонстрирует использование этапов и окружений в реальном проекте.
GitHub Actions
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build-files
path: dist/
test:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-files
path: dist/
- name: Run tests
run: npm test
deploy-dev:
if: github.ref == 'refs/heads/develop'
needs: test
runs-on: ubuntu-latest
environment:
name: development
url: https://dev.example.com
steps:
- uses: actions/checkout@v3
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-files
path: dist/
- name: Deploy to development
run: |
echo "Deploying to development environment..."
# Здесь команды для деплоя
deploy-staging:
if: github.ref == 'refs/heads/main'
needs: test
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v3
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-files
path: dist/
- name: Deploy to staging
run: |
echo "Deploying to staging environment..."
# Здесь команды для деплоя
deploy-production:
if: github.ref == 'refs/heads/main'
needs: deploy-staging
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v3
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-files
path: dist/
- name: Deploy to production
run: |
echo "Deploying to production environment..."
# Здесь команды для деплоя
GitLab CI
stages:
- build
- test
- deploy-dev
- deploy-staging
- deploy-production
variables:
NODE_ENV: production
build:
stage: build
image: node:16
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
test:
stage: test
image: node:16
script:
- npm ci
- npm test
dependencies:
- build
deploy-dev:
stage: deploy-dev
script:
- echo "Deploying to development environment..."
# Здесь команды для деплоя
environment:
name: development
url: https://dev.example.com
only:
- develop
deploy-staging:
stage: deploy-staging
script:
- echo "Deploying to staging environment..."
# Здесь команды для деплоя
environment:
name: staging
url: https://staging.example.com
only:
- main
deploy-production:
stage: deploy-production
script:
- echo "Deploying to production environment..."
# Здесь команды для деплоя
environment:
name: production
url: https://example.com
when: manual
only:
- main
Визуализация процесса и зависимостей
Чтобы лучше понять, как работают этапы и окружения, представьте их как конвейер на фабрике:
Сборка (Build) — сырьё превращается в полуфабрикат
Тестирование (Test) — полуфабрикат проходит контроль качества
Деплой в Dev — первичная проверка в "лабораторных условиях"
Деплой в Staging — проверка в условиях, приближенных к реальным
Деплой в Production — выпуск готового продукта для потребителей
Каждый этап зависит от успешного завершения предыдущего. Если на любом этапе возникает проблема, конвейер останавливается, предотвращая попадание дефектного продукта к пользователю.
Обработка ошибок и восстановление
Что делать, если на каком-то этапе возникла ошибка? В CI/CD есть несколько стратегий:
Автоматический откат (rollback) — возврат к предыдущей стабильной версии
Ручное вмешательство — остановка пайплайна и ожидание действий от разработчика
Условное продолжение — игнорирование некритичных ошибок (например, в тестах производительности)
В GitHub Actions можно использовать условия для обработки ошибок:
deploy-with-rollback:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy
id: deploy
continue-on-error: true
run: ./deploy.sh
- name: Rollback if deploy failed
if: steps.deploy.outcome == 'failure'
run: ./rollback.sh
В GitLab CI можно использовать when: on_failure
:
deploy:
stage: deploy
script:
- ./deploy.sh
rollback:
stage: deploy
script:
- ./rollback.sh
when: on_failure
Окружения и этапы — это не просто технические термины, это философия разработки, которая помогает доставлять качественный продукт пользователям с минимальными рисками. Правильно настроенный процесс CI/CD с множеством окружений похож на систему шлюзов на реке: он позволяет контролировать поток изменений и предотвращать "наводнения" в виде критических ошибок в продакшне.
Отладка и разбор ошибок
Общие принципы отладки CI/CD
Даже самые тщательно настроенные CI/CD-пайплайны иногда дают сбои. Это нормально — в конце концов, мы имеем дело со сложными системами, где множество компонентов должны работать вместе. Важно уметь быстро находить и устранять проблемы.
Отладка CI/CD во многом похожа на отладку обычного кода, но имеет свои особенности. Вот основные принципы:
Изоляция проблемы — определите, на каком именно этапе и в каком job-е возникает ошибка
Анализ логов — внимательно изучите вывод команд, которые завершились с ошибкой
Воспроизведение локально — попробуйте выполнить те же команды на своей машине
Пошаговое упрощение — временно отключите сложные части конфигурации, чтобы локализовать проблему
Инкрементальные изменения — вносите и тестируйте изменения постепенно
Типичные причины ошибок в CI/CD:
Синтаксические ошибки в YAML-файлах (неправильные отступы, пропущенные кавычки)
Отсутствующие зависимости (пакеты, библиотеки, инструменты)
Проблемы с правами доступа (к репозиториям, серверам, API)
Несовместимость версий (например, код требует Node.js 16, а в CI используется Node.js 14)
Проблемы с сетью (недоступность внешних сервисов, таймауты)
Различия между окружениями (код работает локально, но не в CI)
Отладка в GitHub Actions
GitHub Actions предоставляет удобный интерфейс для просмотра логов и отладки workflow.
Где искать логи
Перейдите на вкладку "Actions" в вашем репозитории
Выберите конкретный запуск workflow (обычно последний)
В левой части экрана вы увидите список job-ов
Нажмите на job, чтобы увидеть его шаги
Нажмите на шаг, чтобы развернуть его лог
GitHub Actions группирует вывод по шагам, что упрощает поиск проблемы. Если шаг завершился с ошибкой, он будет отмечен красным крестиком.
Расширенное логирование
Для более подробной отладки можно включить расширенное логирование:
jobs:
debug-job:
runs-on: ubuntu-latest
steps:
- name: Enable debug logging
run: echo "ACTIONS_RUNNER_DEBUG=true" >> $GITHUB_ENV
- name: Your step
run: your-command
Также можно включить отладку на уровне всего workflow, добавив секрет ACTIONS_RUNNER_DEBUG
со значением true
в настройках репозитория.
Использование debug-действий
Существуют специальные actions для отладки:
jobs:
debug:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Dump job context
env:
JOB_CONTEXT: ${{ toJson(job) }}
run: echo "$JOB_CONTEXT"
- name: Dump steps context
env:
STEPS_CONTEXT: ${{ toJson(steps) }}
run: echo "$STEPS_CONTEXT"
- name: Dump runner context
env:
RUNNER_CONTEXT: ${{ toJson(runner) }}
run: echo "$RUNNER_CONTEXT"
Этот job выведет в лог все доступные контексты, что поможет понять, какие переменные доступны и какие значения они имеют.
Локальное тестирование с act
Инструмент act позволяет запускать GitHub Actions локально:
# Установка на macOS
brew install act
# Установка на Linux
curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
# Запуск всех workflow
act
# Запуск конкретного workflow
act -W .github/workflows/specific-workflow.yml
# Запуск с конкретным событием
act push
Это особенно полезно для быстрой итерации при разработке workflow, без необходимости коммитить и пушить изменения.
Отладка в GitLab CI
GitLab CI также предоставляет инструменты для отладки pipeline.
Где искать логи
Перейдите в раздел "CI/CD" → "Pipelines" вашего проекта
Выберите конкретный pipeline
Нажмите на job, который вас интересует
Вы увидите вкладку "Job log" с полным выводом команд
GitLab показывает вывод в реальном времени, что удобно для отслеживания длительных операций.
Использование CI_DEBUG_TRACE
Для более подробного логирования можно включить переменную CI_DEBUG_TRACE
:
job-name:
stage: test
variables:
CI_DEBUG_TRACE: "true"
script:
- your-command
Это выведет в лог все выполняемые команды и их вывод, включая команды, которые обычно скрыты.
Локальное тестирование с gitlab-runner
GitLab Runner можно установить локально и использовать для тестирования:
# Установка на macOS
brew install gitlab-runner
# Установка на Ubuntu
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
sudo apt-get install gitlab-runner
# Запуск job-а локально
gitlab-runner exec docker job-name
Это позволяет отлаживать конфигурацию без коммитов в репозиторий.
Отладка с использованием services
Если ваш pipeline использует сервисы (например, базу данных), вы можете отладить их взаимодействие:
job:
services:
- name: postgres:13
alias: db
variables:
POSTGRES_PASSWORD: password
script:
- apt-get update && apt-get install -y postgresql-client
- psql -h db -U postgres -c "SELECT 1"
- echo "Database connection successful"
В случае проблем с подключением к сервису, вы можете добавить дополнительные команды для проверки сетевого соединения:
script:
- apt-get update && apt-get install -y postgresql-client iputils-ping net-tools
- ping -c 3 db
- netstat -tuln
- psql -h db -U postgres -c "SELECT 1"
Типичные проблемы и их решения
1. Ошибки синтаксиса YAML
Проблема: Workflow не запускается или завершается с ошибкой из-за неправильного синтаксиса YAML.
Решение:
Используйте онлайн-валидаторы YAML (например, YAML Lint)
Установите расширение для вашего редактора, которое подсвечивает синтаксис YAML
Обратите внимание на отступы (в YAML они имеют значение)
Проверьте кавычки вокруг строк, содержащих специальные символы
Пример ошибки:
# Неправильно
job:
steps:
- name: Step with error
run: echo "Hello
World"
# Правильно
job:
steps:
- name: Step without error
run: echo "Hello World"
2. Проблемы с переменными и секретами
Проблема: Скрипты не могут получить доступ к переменным или секретам.
Решение:
Проверьте, правильно ли настроены секреты в настройках репозитория/проекта
Убедитесь, что вы используете правильный синтаксис для доступа к секретам
Добавьте отладочный вывод (но не выводите сами секреты!)
Пример отладки:
# GitHub Actions
steps:
- name: Debug API key
run: |
if [ -n "${{ secrets.API_KEY }}" ]; then
echo "API key is set"
else
echo "API key is NOT set"
fi
# GitLab CI
script:
- |
if [ -n "$API_KEY" ]; then
echo "API key is set"
else
echo "API key is NOT set"
fi
3. Проблемы с зависимостями
Проблема: Сборка или тесты падают из-за отсутствующих зависимостей.
Решение:
Убедитесь, что все зависимости явно указаны
Зафиксируйте версии зависимостей (например,
npm ci
вместоnpm install
)Используйте кеширование для ускорения установки зависимостей
Пример с кешированием:
# GitHub Actions
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- run: npm ci
- run: npm test
# GitLab CI
job:
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
script:
- npm ci
- npm test
4. Проблемы с сетевым доступом
Проблема: CI не может подключиться к внешним сервисам.
Решение:
Проверьте доступность сервисов из CI (добавьте команды ping, curl и т.д.)
Убедитесь, что у вас есть необходимые учётные данные
Рассмотрите возможность использования моков или стабов для тестов
Пример проверки:
steps:
- name: Check connectivity
run: |
curl -Is https://api.example.com | head -1
ping -c 3 api.example.com
5. Временные ограничения и таймауты
Проблема: Job превышает максимальное время выполнения.
Решение:
Оптимизируйте длительные операции
Разделите большие job-ы на несколько меньших
Увеличьте таймаут, если это возможно
Пример настройки таймаута:
# GitHub Actions
jobs:
long-job:
timeout-minutes: 120
runs-on: ubuntu-latest
steps:
- name: Long running task
run: ./long-task.sh
# GitLab CI
long-job:
script:
- ./long-task.sh
timeout: 2h
Лучшие практики отладки
Добавляйте информативные сообщения — используйте
echo
илиprintf
для вывода промежуточных результатовПроверяйте окружение — выводите переменные окружения с помощью
env
илиprintenv
Разделяйте сложные команды — вместо одной длинной команды используйте несколько простых с промежуточными проверками
Используйте флаги подробного вывода — многие инструменты имеют флаги вроде
-v
или--verbose
Сохраняйте артефакты — файлы логов, отчёты и другие результаты могут помочь в отладке
Тестируйте изменения в отдельной ветке — не меняйте основную конфигурацию, пока не убедитесь, что всё работает
Отладка CI/CD может быть сложной, но с правильным подходом вы сможете быстро находить и устранять проблемы. Помните: даже когда CI падает — всегда есть лог, позволяющий найти проблему и быстро её исправить. Это как расследование детектива — у вас есть все улики, нужно только правильно их интерпретировать.
Хорошие практики настройки CI/CD
Структурирование pipeline
Хорошо структурированный CI/CD pipeline похож на хорошо написанный код — он должен быть понятным, поддерживаемым и эффективным. Вот несколько принципов, которые помогут вам создать качественные конфигурации:
Модульность и переиспользование кода
Как и в программировании, в CI/CD стоит избегать дублирования кода. Повторяющиеся блоки лучше выносить в отдельные компоненты.
В GitHub Actions для этого используются:
Composite actions — собственные переиспользуемые действия:
# .github/actions/setup-project/action.yml
name: 'Setup Project'
description: 'Sets up Node.js and installs dependencies'
runs:
using: "composite"
steps:
- uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- run: npm ci
shell: bash
Использование:
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup-project
- run: npm test
Reusable workflows — переиспользуемые workflow:
# .github/workflows/reusable-test.yml
name: Reusable test workflow
on:
workflow_call:
inputs:
node-version:
required: false
default: '16'
type: string
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ inputs.node-version }}
- run: npm ci
- run: npm test
Использование:
jobs:
call-test-workflow:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: '18'
В GitLab CI для переиспользования используются:
Includes — включение внешних файлов:
include:
- local: 'ci/build.yml'
- local: 'ci/test.yml'
- project: 'my-group/my-project'
file: '/templates/deploy.yml'
Extends — наследование конфигурации:
# ci/templates.yml
.base-job:
image: node:16
before_script:
- npm ci
# .gitlab-ci.yml
include:
- local: 'ci/templates.yml'
test:
extends: .base-job
script:
- npm test
Anchors — якоря для повторяющихся блоков:
.setup: &setup
- npm ci
- npm run build
test:
script:
- *setup
- npm test
lint:
script:
- *setup
- npm run lint
Именование job-ов и шагов
Хорошие имена делают конфигурацию более понятной и упрощают отладку:
# Плохо
job1:
steps:
- run: npm ci
- run: npm test
# Хорошо
install-and-test:
steps:
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm test
Придерживайтесь единого стиля именования и используйте имена, которые отражают суть операции.
Документирование конфигурации
Комментарии в YAML-файлах помогают объяснить неочевидные моменты:
jobs:
deploy:
# Этот job деплоит только на staging, для production используйте deploy-prod
environment: staging
steps:
# Используем --force, потому что иногда возникают конфликты с существующими файлами
- name: Deploy to server
run: rsync --force -avz ./dist/ user@server:/var/www/
Также полезно создать файл CONTRIBUTING.md
с описанием CI/CD-процесса для новых участников проекта.
Версионирование используемых actions/images
Всегда указывайте конкретные версии используемых actions и Docker-образов:
# Плохо - может сломаться при обновлении action
- uses: actions/checkout@master
# Хорошо - фиксированная версия
- uses: actions/checkout@v3
# Плохо - может сломаться при обновлении образа
image: node
# Хорошо - фиксированная версия
image: node:16.14.2
Это делает ваши сборки более предсказуемыми и защищает от неожиданных изменений в зависимостях.
Оптимизация производительности
Быстрый CI/CD — это не просто удобство, это экономия времени и ресурсов. Вот несколько способов ускорить ваши пайплайны:
Кеширование зависимостей и артефактов
В GitHub Actions:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Cache build output
uses: actions/cache@v3
with:
path: dist
key: ${{ runner.os }}-build-${{ github.sha }}
В GitLab CI:
build:
stage: build
script:
- npm ci
- npm run build
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
artifacts:
paths:
- dist/
Параллельное выполнение задач
Независимые задачи можно выполнять параллельно:
В GitHub Actions:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm test
# Этот job запустится только после успешного завершения lint и test
build:
needs: [lint, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run build
В GitLab CI:
stages:
- validate
- build
lint:
stage: validate
script:
- npm ci
- npm run lint
test:
stage: validate
script:
- npm ci
- npm test
build:
stage: build
script:
- npm ci
- npm run build
Условное выполнение для экономии ресурсов
Не все задачи нужно выполнять при каждом коммите:
jobs:
e2e-tests:
runs-on: ubuntu-latest
# Запускаем e2e-тесты только для PR в main или при пуше в main
if: github.event_name == 'pull_request' && github.base_ref == 'main' || github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run test:e2e
Матрицы для тестирования в разных средах
Матрицы позволяют запускать один и тот же job с разными параметрами:
В GitHub Actions:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [14, 16, 18]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
В GitLab CI:
test:
parallel:
matrix:
- NODE_VERSION: [14, 16, 18]
OS: [linux, windows]
script:
- npm ci
- npm test
Интеграция с инструментами разработки
CI/CD становится ещё полезнее, когда интегрируется с другими инструментами разработки.
Бейджи статуса в README
Бейджи показывают текущий статус вашего проекта:
GitHub Actions:

GitLab CI:
[](https://gitlab.com/username/repo/-/commits/main)
Интеграция с системами отслеживания задач
CI/CD может автоматически обновлять статус задач:
GitHub Actions с Jira:
jobs:
update-jira:
runs-on: ubuntu-latest
steps:
- name: Jira Login
uses: atlassian/gajira-login@v3
env:
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
- name: Update Jira issue
uses: atlassian/gajira-transition@v3
with:
issue: ${{ github.event.pull_request.title | regex_match('^([A-Z]+-[0-9]+)') }}
transition: "In Review"
Автоматические комментарии в PR/MR
CI/CD может оставлять комментарии с результатами проверок:
GitHub Actions:
jobs:
comment:
runs-on: ubuntu-latest
steps:
- name: Comment PR
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '✅ Tests passed successfully!'
})
Уведомления в мессенджеры
CI/CD может отправлять уведомления о статусе сборки:
GitHub Actions с Slack:
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_CHANNEL: ci-notifications
SLACK_TITLE: Build Result
SLACK_MESSAGE: 'Build ${{ job.status }}'
SLACK_COLOR: ${{ job.status == 'success' && 'good' || 'danger' }}
Масштабирование и поддержка
С ростом проекта растёт и сложность CI/CD. Вот как сделать его более масштабируемым:
Шаблоны для повторяющихся конфигураций
Создавайте шаблоны для типовых задач:
GitHub Actions:
# .github/workflow-templates/node-test.yml
name: Node.js Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
- run: npm ci
- run: npm test
GitLab CI:
# gitlab-ci-templates/node-test.yml
.node-test:
image: node:16
script:
- npm ci
- npm test
Самодокументируемые workflow
Используйте говорящие имена и комментарии:
name: Deploy to Production
# Этот workflow деплоит приложение на продакшн-сервер
# Запускается только при создании тега с префиксом 'v'
# Требует ручного одобрения от команды DevOps
on:
push:
tags:
- 'v*'
jobs:
# Проверяем, что все тесты проходят
test:
runs-on: ubuntu-latest
steps:
# ...
# Собираем приложение для продакшна
build:
needs: test
runs-on: ubuntu-latest
steps:
# ...
# Деплоим на продакшн (требуется одобрение)
deploy:
needs: build
environment:
name: production
url: https://example.com
runs-on: ubuntu-latest
steps:
# ...
Мониторинг производительности CI/CD
Отслеживайте время выполнения пайплайнов и оптимизируйте узкие места:
GitHub Actions:
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- name: Start timer
run: echo "start_time=$(date +%s)" >> $GITHUB_ENV
- name: Run your task
run: npm run build
- name: Calculate duration
run: |
end_time=$(date +%s)
duration=$((end_time - ${{ env.start_time }}))
echo "Task took $duration seconds"
GitLab CI предоставляет встроенную метрику времени выполнения для каждого job-а.
Стратегии обновления конфигураций
При обновлении CI/CD-конфигураций:
Создавайте отдельную ветку для изменений
Тестируйте изменения на этой ветке
Используйте PR/MR для обсуждения изменений с командой
После слияния внимательно следите за первыми запусками
Хорошо настроенный CI/CD — это инвестиция, которая окупается экономией времени и повышением качества продукта. Как говорил Авраам Линкольн: "Дайте мне шесть часов на то, чтобы срубить дерево, и я потрачу первые четыре на заточку топора". Время, потраченное на настройку CI/CD, — это и есть заточка вашего инструмента разработки.
Расширенные возможности и интеграции
Матрицы для тестирования
Представьте, что вы разрабатываете библиотеку, которая должна работать в разных окружениях: на разных операционных системах, с разными версиями языка программирования или с разными версиями зависимостей. Проверять каждую комбинацию вручную было бы крайне утомительно. Здесь на помощь приходят матрицы — мощный инструмент CI/CD для параллельного тестирования в разных условиях.
Концепция матриц в CI/CD
Матрица — это способ определить несколько вариаций одного и того же job-а с разными параметрами. Вместо того чтобы писать отдельный job для каждой комбинации параметров, вы определяете одну матрицу, и система CI/CD автоматически создаёт и запускает все необходимые комбинации.
Типичные параметры для матриц:
Версии языка программирования (Node.js 14, 16, 18)
Операционные системы (Linux, Windows, macOS)
Версии браузеров (Chrome, Firefox, Safari)
Версии зависимостей (React 16, 17, 18)
Настройка матриц в GitHub Actions
В GitHub Actions матрицы определяются с помощью ключа strategy.matrix
:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [14, 16, 18]
# Можно добавить исключения
exclude:
# Не тестируем Node.js 14 на macOS
- os: macos-latest
node-version: 14
# Можно добавить дополнительные комбинации
include:
# Дополнительно тестируем Node.js 12 только на Ubuntu
- os: ubuntu-latest
node-version: 12
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
В этом примере будет создано 8 job-ов:
Ubuntu + Node.js 14
Ubuntu + Node.js 16
Ubuntu + Node.js 18
Ubuntu + Node.js 12 (из include)
Windows + Node.js 14
Windows + Node.js 16
Windows + Node.js 18
macOS + Node.js 16
macOS + Node.js 18
Обратите внимание, что комбинация macOS + Node.js 14 исключена с помощью exclude
.
Настройка матриц в GitLab CI
В GitLab CI матрицы реализуются с помощью ключа parallel.matrix
:
test:
parallel:
matrix:
- OS: [ubuntu, windows]
NODE_VERSION: [14, 16, 18]
script:
- echo "Testing on $OS with Node.js $NODE_VERSION"
- npm ci
- npm test
Этот пример создаст 6 job-ов для всех комбинаций OS и NODE_VERSION.
Оптимизация матричных сборок
Матрицы могут быстро увеличить количество job-ов, что приведёт к увеличению времени сборки и потреблению ресурсов. Вот несколько советов по оптимизации:
Используйте исключения для пропуска ненужных комбинаций
Добавьте условия для раннего завершения — например, если тесты падают на одной ОС, нет смысла продолжать на других
Кешируйте зависимости для ускорения установки
Группируйте связанные параметры — например, тестируйте только LTS-версии Node.js на всех ОС, а промежуточные версии — только на одной ОС
Работа с артефактами
В процессе CI/CD часто создаются файлы, которые нужно сохранить для последующего использования: скомпилированный код, отчёты о тестировании, документация и т.д. Такие файлы называются артефактами.
Что такое артефакты и зачем они нужны
Артефакты — это файлы, созданные во время выполнения pipeline, которые нужно сохранить после завершения job-а. Они могут быть использованы:
Для передачи результатов сборки между job-ами
Для скачивания и анализа (например, отчёты о покрытии кода)
Для деплоя (например, скомпилированные файлы)
Для архивации (например, логи или снимки экрана при падении тестов)
Сохранение и передача артефактов между job-ами
В GitHub Actions:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build-files
path: dist/
retention-days: 7 # Хранить 7 дней
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-files
path: dist/
- name: Deploy
run: ./deploy.sh
В GitLab CI:
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week # Хранить 1 неделю
deploy:
stage: deploy
script:
- ./deploy.sh
dependencies:
- build # Автоматически скачивает артефакты из job-а build
Публикация артефактов для скачивания
Артефакты можно скачать через веб-интерфейс GitHub или GitLab после завершения workflow/pipeline. Это удобно для анализа результатов или для ручного деплоя.
В GitHub Actions артефакты доступны на странице конкретного запуска workflow.
В GitLab CI артефакты доступны на странице job-а или pipeline.
Управление жизненным циклом артефактов
Артефакты занимают место в хранилище, поэтому важно управлять их жизненным циклом:
Устанавливайте срок хранения — не храните артефакты дольше, чем нужно
Ограничивайте размер — сохраняйте только необходимые файлы
Используйте шаблоны исключения — например, не сохраняйте node_modules
# GitHub Actions
- uses: actions/upload-artifact@v3
with:
name: build-files
path: |
dist/
!dist/**/*.map # Исключаем source maps
retention-days: 3
# GitLab CI
artifacts:
paths:
- dist/
exclude:
- dist/**/*.map # Исключаем source maps
expire_in: 3 days
Интеграция с облачными платформами
Современные приложения часто развёртываются в облачных платформах, таких как AWS, Azure или Google Cloud. CI/CD может автоматизировать этот процесс.
Деплой в AWS, Azure, GCP
Деплой в AWS с GitHub Actions:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy to S3
run: aws s3 sync ./dist s3://my-bucket/ --delete
- name: Invalidate CloudFront cache
run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_DISTRIBUTION_ID }} --paths "/*"
Деплой в Azure с GitLab CI:
deploy:
stage: deploy
image: mcr.microsoft.com/azure-cli
script:
- az login --service-principal -u $AZURE_SP_CLIENT_ID -p $AZURE_SP_CLIENT_SECRET --tenant $AZURE_TENANT_ID
- az webapp deployment source config-zip --resource-group myResourceGroup --name myWebApp --src dist.zip
Использование облачных CLI и SDK
Большинство облачных провайдеров предоставляют CLI-инструменты и SDK, которые можно использовать в CI/CD:
AWS: aws-cli, AWS SDK
Azure: azure-cli, Azure SDK
Google Cloud: gcloud, Google Cloud SDK
Эти инструменты можно использовать напрямую в скриптах или через специальные actions/images.
Управление инфраструктурой через CI/CD
CI/CD может не только деплоить приложения, но и управлять инфраструктурой с помощью инструментов Infrastructure as Code (IaC):
Terraform с GitHub Actions:
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Terraform Init
run: terraform init
- name: Terraform Plan
run: terraform plan -out=tfplan
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -auto-approve tfplan
Безопасная работа с облачными учетными данными
При работе с облачными платформами критически важно безопасно хранить учётные данные:
Используйте секреты для хранения ключей и токенов
Ограничивайте права — предоставляйте минимально необходимые права
Используйте временные учётные данные — например, AWS STS для получения временных токенов
Настройте ротацию ключей — регулярно обновляйте ключи доступа
Контейнеризация и CI/CD
Контейнеры стали стандартом для упаковки и развёртывания приложений. CI/CD может автоматизировать работу с контейнерами.
Сборка и публикация Docker-образов
GitHub Actions:
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: username/app:latest,username/app:${{ github.sha }}
cache-from: type=registry,ref=username/app:buildcache
cache-to: type=registry,ref=username/app:buildcache,mode=max
GitLab CI:
build:
image: docker:20.10.16
services:
- docker:20.10.16-dind
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
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest
Тестирование в контейнерах
Контейнеры обеспечивают изолированную и воспроизводимую среду для тестирования:
# GitHub Actions
jobs:
test:
runs-on: ubuntu-latest
container:
image: node:16
services:
postgres:
image: postgres:13
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm test
# GitLab CI
test:
image: node:16
services:
- postgres:13
variables:
POSTGRES_PASSWORD: postgres
script:
- npm ci
- npm test
Интеграция с реестрами контейнеров
CI/CD может автоматически публиковать образы в реестры контейнеров:
Docker Hub
GitHub Container Registry
GitLab Container Registry
AWS ECR
Google Container Registry
Azure Container Registry
Оркестрация контейнеров через CI/CD
CI/CD может автоматизировать развёртывание контейнеров в оркестраторы, такие как Kubernetes:
GitHub Actions с Kubernetes:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up kubeconfig
uses: azure/k8s-set-context@v3
with:
kubeconfig: ${{ secrets.KUBE_CONFIG }}
- name: Deploy to Kubernetes
run: |
kubectl apply -f k8s/deployment.yaml
kubectl rollout status deployment/my-app
GitLab CI с Kubernetes:
deploy:
image: bitnami/kubectl:latest
script:
- kubectl config set-cluster k8s --server="$KUBE_URL" --insecure-skip-tls-verify=true
- kubectl config set-credentials admin --token="$KUBE_TOKEN"
- kubectl config set-context default --cluster=k8s --user=admin
- kubectl config use-context default
- kubectl apply -f k8s/deployment.yaml
- kubectl rollout status deployment/my-app
Расширенные возможности CI/CD позволяют автоматизировать практически любой аспект разработки, тестирования и развёртывания приложений. Это как швейцарский нож для DevOps-инженера — множество инструментов для решения разных задач в одном месте.
Терминология и глоссарий
Для удобства навигации по миру CI/CD, давайте разберёмся с основными терминами, которые вы будете встречать при настройке и использовании этих инструментов. Хорошее понимание терминологии поможет вам быстрее освоиться и эффективнее общаться с коллегами.
Branch (ветка)
Определение: Параллельная версия кода в репозитории Git, которая позволяет разработчикам работать независимо друг от друга.
Практическое применение: В CI/CD ветки часто определяют, какие действия должны быть выполнены. Например, пуш в ветку main
может запускать полный цикл тестирования и деплоя, а пуш в ветку feature/*
— только базовые тесты.
Пример использования:
on:
push:
branches: [ main, develop ]
Tag (метка)
Определение: Специальная ссылка в Git, указывающая на конкретный коммит, обычно используется для маркировки релизов.
Практическое применение: Теги часто используются для запуска процессов деплоя или публикации релизов.
Пример использования:
on:
push:
tags:
- 'v*' # Любой тег, начинающийся с 'v'
Environment (окружение)
Определение: Среда, в которой выполняется код — например, разработка, тестирование, продакшн.
Практическое применение: Разные окружения могут иметь разные конфигурации, переменные и требования безопасности.
Пример использования:
deploy:
environment:
name: production
url: https://example.com
Secret (секрет)
Определение: Защищённая переменная, содержащая чувствительные данные, такие как пароли, токены API или ключи SSH.
Практическое применение: Секреты используются для безопасного хранения и использования чувствительной информации в CI/CD-процессах.
Пример использования:
steps:
- name: Deploy
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
Artifact (артефакт)
Определение: Файл или набор файлов, созданных во время выполнения CI/CD-процесса, которые нужно сохранить для последующего использования.
Практическое применение: Артефакты используются для передачи результатов между job-ами или для скачивания и анализа.
Пример использования:
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: build-files
path: dist/
Cache (кеш)
Определение: Временное хранилище данных, которые могут быть повторно использованы между запусками CI/CD для ускорения процесса.
Практическое применение: Кеширование часто используется для зависимостей (node_modules, vendor и т.д.), чтобы не устанавливать их заново при каждом запуске.
Пример использования:
- name: Cache dependencies
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
Matrix (матрица)
Определение: Стратегия в CI/CD, которая позволяет запускать один и тот же job с разными параметрами (например, на разных ОС или с разными версиями языка).
Практическое применение: Матрицы используются для тестирования кода в различных окружениях без необходимости дублировать конфигурацию.
Пример использования:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node-version: [14, 16, 18]
Manual job (ручной запуск)
Определение: Job, который не запускается автоматически, а требует ручного запуска через интерфейс CI/CD-системы.
Практическое применение: Ручные job-ы часто используются для деплоя в продакшн или для других критических операций, требующих человеческого контроля.
Пример использования:
# GitHub Actions
jobs:
deploy:
environment:
name: production
# Требует ручного одобрения через интерфейс GitHub
# GitLab CI
deploy:
when: manual
Runner (раннер)
Определение: Сервер или агент, который выполняет задачи CI/CD.
Практическое применение: Раннеры могут быть общими (предоставляемыми GitHub/GitLab) или самостоятельно размещёнными (self-hosted).
Пример использования:
jobs:
build:
runs-on: ubuntu-latest # Использование общего раннера GitHub
Workflow (рабочий процесс)
Определение: В GitHub Actions — настраиваемый автоматизированный процесс, который выполняет одну или несколько задач.
Практическое применение: Workflow определяется в файле YAML и может быть запущен различными событиями.
Пример использования:
name: CI
on: [push, pull_request]
jobs:
# ...
Pipeline (пайплайн)
Определение: В GitLab CI — набор job-ов, сгруппированных по стадиям, которые выполняются последовательно или параллельно.
Практическое применение: Pipeline определяется в файле .gitlab-ci.yml
и запускается при определённых событиях.
Пример использования:
stages:
- build
- test
- deploy
Job (задача)
Определение: Набор шагов, которые выполняются на одном раннере.
Практическое применение: Job-ы могут выполняться параллельно или последовательно, в зависимости от настроек.
Пример использования:
jobs:
build:
# ...
test:
# ...
Step (шаг)
Определение: В GitHub Actions — отдельная команда или action внутри job-а.
Практическое применение: Шаги выполняются последовательно и могут использовать результаты предыдущих шагов.
Пример использования:
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Build
run: npm run build
Action (действие)
Определение: В GitHub Actions — повторно используемый блок кода, который выполняет определённую задачу.
Практическое применение: Actions могут быть официальными (от GitHub), сторонними или собственными.
Пример использования:
- uses: actions/setup-node@v3
with:
node-version: '16'
Stage (стадия)
Определение: В GitLab CI — группа job-ов, которые выполняются параллельно.
Практическое применение: Стадии выполняются последовательно, что позволяет организовать pipeline в логические этапы.
Пример использования:
stages:
- build
- test
- deploy
build-job:
stage: build
# ...
Trigger (триггер)
Определение: Событие, которое запускает workflow или pipeline.
Практическое применение: Триггерами могут быть пуши, pull request-ы, создание тегов, расписание и т.д.
Пример использования:
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * *' # Ежедневно в полночь
Self-hosted runner (самостоятельно размещённый раннер)
Определение: Раннер, который вы устанавливаете и управляете самостоятельно, а не используете предоставляемые GitHub/GitLab.
Практическое применение: Self-hosted runner-ы полезны, когда вам нужен доступ к специфическому оборудованию или внутренним ресурсам.
Пример использования:
jobs:
build:
runs-on: self-hosted
Dependency (зависимость)
Определение: В контексте CI/CD — job, который должен быть успешно выполнен перед запуском другого job-а.
Практическое применение: Зависимости позволяют создавать последовательности job-ов.
Пример использования:
# GitHub Actions
jobs:
test:
# ...
deploy:
needs: test
# ...
# GitLab CI
deploy:
dependencies:
- test
Понимание этих терминов поможет вам лучше ориентироваться в документации и обсуждениях, связанных с CI/CD. Не стесняйтесь возвращаться к этому глоссарию, когда встречаете незнакомый термин — со временем вся эта терминология станет для вас второй натурой.
Заключение
Вот мы и подошли к концу нашего путешествия по миру продвинутого CI/CD. Мы начали с простых конфигураций, а теперь умеем настраивать сложные пайплайны с ветвлением, секретами, окружениями и множеством других возможностей. Давайте подведём итоги того, что мы узнали.
Что мы изучили
Мы научились:
Настраивать условное выполнение в зависимости от ветки или других параметров — теперь ваш CI/CD может принимать умные решения о том, что и когда запускать
Безопасно работать с секретами — больше никаких паролей в открытом виде в репозитории
Управлять разными окружениями — от разработки до продакшна, с разными уровнями защиты и автоматизации
Отлаживать и решать проблемы в CI/CD — теперь вы знаете, где искать логи и как анализировать ошибки
Применять лучшие практики для структурирования и оптимизации пайплайнов
Использовать продвинутые возможности — матрицы, артефакты, интеграции с облачными платформами и контейнеризацию
Всё это делает ваш CI/CD не просто набором скриптов, а полноценным инструментом, который помогает доставлять качественный код пользователям быстро и безопасно.
Практические рекомендации по внедрению
Если вы только начинаете внедрять CI/CD или хотите улучшить существующие процессы, вот несколько рекомендаций:
Начинайте с малого — не пытайтесь сразу настроить идеальный пайплайн со всеми возможностями. Начните с базовых тестов и постепенно добавляйте новые функции.
Автоматизируйте рутину в первую очередь — определите, какие задачи отнимают больше всего времени у команды, и автоматизируйте именно их.
Инвестируйте в тесты — CI/CD без хороших тестов — это быстрый способ доставки багов пользователям. Чем лучше ваши тесты, тем больше пользы от автоматизации.
Документируйте процессы — убедитесь, что все члены команды понимают, как работает ваш CI/CD и что делать в случае проблем.
Регулярно пересматривайте конфигурацию — CI/CD не высечен в камне. По мере роста проекта и команды пересматривайте и улучшайте процессы.
Перспективы развития навыков CI/CD
Мир CI/CD постоянно развивается, и есть множество направлений для дальнейшего изучения:
GitOps — подход к управлению инфраструктурой и приложениями через Git
Непрерывный мониторинг — интеграция мониторинга и оповещений в процесс CI/CD
Канареечные релизы — постепенное развёртывание новых версий для ограниченной группы пользователей
Автоматизированное тестирование безопасности — интеграция инструментов безопасности в пайплайн
Машинное обучение в CI/CD — использование ML для оптимизации процессов и предсказания проблем
Призыв к действию
Теперь, когда у вас есть знания и инструменты, самое время применить их на практике. Начните с небольших изменений в своих существующих workflow или создайте новый с нуля. Экспериментируйте, не бойтесь ошибок — CI/CD как раз и нужен для того, чтобы ловить их до того, как они попадут к пользователям.
Помните, что хороший CI/CD — это не цель, а путь. Он должен постоянно эволюционировать вместе с вашим проектом и командой. То, что работает сегодня, может потребовать изменений завтра, и это нормально.
И напоследок: поздравляем, вы прокачали свой CI/CD до продвинутого уровня! Теперь ваш код сам себя проверяет и разворачивает — а вы можете расслабиться и заняться более интересными задачами. Ну, почти расслабиться — роботы пока не захватили мир, и кто-то должен писать сам код ?
Удачи в ваших CI/CD-приключениях!
yellowmew
Почему то регулятор на уровне workflow (когда вообще запускать пайплайн, а не когда выполнять шаг) для github написан а для гитлаба - нет. https://docs.gitlab.com/ci/yaml/workflow/