Часть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-приключениях!
ссылка на оригинал статьи https://habr.com/ru/articles/915370/
Добавить комментарий