Пайплайн это описанный кодом путь от коммита до готового артефакта (и дальше до деплоя). Он лежит в репозитории рядом с кодом: .github/workflows/*.yml в GitHub Actions, .gitlab-ci.yml в GitLab CI. Значит, его ревьюят, версионируют и откатывают, как обычный код.
Из чего состоит пайплайн
- Триггер: push, pull/merge request, тег, расписание, ручной запуск.
- Джоба (job): набор шагов, который выполняется на одном раннере в чистом окружении.
- Стадии и зависимости: в GitLab джобы группируются в
stages(build, test, deploy), аneedsстроит граф, чтобы джоба стартовала сразу после нужных, не дожидаясь всей стадии. В GitHub Actions джобы по умолчанию параллельны, порядок задаётneeds. - Раннер: машина, где выполняется джоба. Облачные (GitHub-hosted, shared runners GitLab) или свои (self-hosted).
# GitHub Actions
name: ci
on:
pull_request:
push:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-24.04
strategy:
matrix:
node: [20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm test
build:
needs: test
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- run: docker build -t registry.example.ru/app:${{ github.sha }} .
# GitLab CI
stages: [build, test, deploy]
build:
stage: build
script: npm ci && npm run build
artifacts:
paths: [dist/]
expire_in: 1 week
test:
stage: test
needs: [build]
script: npm test
deploy:
stage: deploy
script: ./deploy.sh
environment: production
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
Кеш и артефакты: не путать
- Кеш ускоряет: папка зависимостей (
~/.npm,.m2,~/.cache/pip) переиспользуется между запусками. Он может не найтись, и джоба обязана работать и без него. Ключ кеша строят из хеша lock-файла. - Артефакт передаёт результат: собранный
dist/, бинарник, отчёт тестов. Он гарантированно доступен следующим джобам и скачивается из интерфейса.
Ошибка: передавать сборку между стадиями через кеш. Однажды кеш промахнётся, и деплой уедет без файлов.
Собрать один раз, выкатывать везде
Артефакт (образ, пакет) собирается один раз и продвигается по окружениям: stage, затем prod. Пересборка под каждое окружение означает, что в прод уходит не то, что тестировали. Различия окружений задаются конфигом и переменными, а не разными сборками.
Образы тегируются неизменяемо: SHA коммита или версия 1.4.2. Тег latest для деплоя плох: непонятно, что именно запущено, и откат «на прошлый latest» невозможен. Самый надёжный способ сослаться на образ это digest @sha256:....
Версии
Semantic Versioning: MAJOR.MINOR.PATCH. MAJOR при ломающих изменениях, MINOR при новых возможностях с обратной совместимостью, PATCH при исправлениях. Версию часто вычисляют автоматически из Conventional Commits (feat: поднимает minor, fix: patch, feat!: или BREAKING CHANGE major).
Быстрый и надёжный пайплайн
- Дешёвые проверки первыми: линтер и юнит-тесты до долгих e2e.
- Параллелить независимые джобы, матрица для версий.
- Кешировать зависимости и слои Docker.
concurrencyс отменой устаревших запусков на ту же ветку.- Флаки-тесты чинить или помечать, а не перезапускать до зелёного.
- Ограничить права токена:
permissions: contents: readпо умолчанию. - Сторонние actions фиксировать по SHA коммита, а не по плавающему тегу.
Монорепозиторий
В монорепо пайплайн собирает только то, что изменилось: paths в GitHub Actions, rules: changes в GitLab, или инструменты сборки с графом зависимостей (Nx, Turborepo, Bazel).
Частые ошибки
- Секреты в логах:
echo $TOKENилиset -xв скрипте. npm installвместоnpm ciв CI: lock-файл может измениться, сборка не воспроизводима.- Деплой тегом
latest. - Self-hosted раннер на публичном репозитории: любой форк может выполнить код на вашей машине.
Что спросят на собесе
- Из чего состоит ваш пайплайн и сколько он идёт.
- Чем кеш отличается от артефакта.
- Как ускорить пайплайн, который идёт 40 минут.
- Почему не стоит деплоить тег
latestи как делать откат. - Как хранить секреты в CI.