Часть 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-приключениях!
No comments to display
No comments to display