Пайплайн, артефакты и версионирование

Вопрос, который открывает блок про доставку кода и почти всегда переходит в обсуждение вашего реального опыта.

Вопрос: «Опишите путь коммита от push в репозиторий до работающего продакшена. Из каких этапов состоит ваш пайплайн и что является артефактом?»

Что на самом деле проверяет интервьюер

Проверяют три вещи. Первая — понимаете ли вы, что пайплайн это не «скрипт деплоя», а конвейер с воротами качества: каждый этап отсекает часть проблем как можно раньше. Вторая — знаете ли вы принцип build once, deploy anywhere. Третья, самая показательная, — как вы обращаетесь с секретами. Кандидат, который признаётся, что пароли лежат в переменных пайплайна открытым текстом «потому что так проще», сразу теряет очки.

Развёрнутый ответ: этапы конвейера

  1. Триггер. Push в ветку или открытие pull request. Для PR обычно гоняют полный набор проверок, но не деплоят.
  2. Статические проверки. Линтеры, форматирование, проверка типов, анализ секретов в коде (gitleaks), SAST. Дёшево и быстро — поэтому идёт первым.
  3. Тесты. Unit-тесты, затем интеграционные с поднятыми зависимостями (обычно в docker compose или service-контейнерах).
  4. Сборка артефакта. Один раз собирается образ и подписывается неизменяемым тегом. Здесь же сканирование на уязвимости (trivy, grype).
  5. Публикация. Артефакт кладётся в registry — единственный источник правды для всех сред.
  6. Деплой в staging. Автоматически. Прогон smoke-тестов и, если есть, e2e.
  7. Деплой в production. С ручным подтверждением либо автоматически, если зрелость процесса позволяет. Затем проверка метрик и готовность к откату.
name: ci
on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: make lint
      - run: make test

  build:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - name: Build and push
        run: |
          TAG="${GITHUB_SHA::12}"
          docker build -t "$REGISTRY/api:$TAG" .
          docker push "$REGISTRY/api:$TAG"

  deploy-staging:
    needs: build
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - run: kubectl set image deploy/api api="$REGISTRY/api:${GITHUB_SHA::12}"

Build once, deploy anywhere

Главный принцип, который обязательно нужно назвать: артефакт собирается ровно один раз и в неизменном виде проходит все среды — staging, pre-prod, production. Отличается только конфигурация, которая подаётся снаружи через переменные окружения, ConfigMap и Secret.

Пересборка образа для каждой среды означает, что в прод едет не тот бинарник, который тестировали: базовый образ мог обновиться, зависимость подтянуться новой минорной версией, кэш сработать иначе. Это ровно тот класс багов, которые невозможно воспроизвести.

Отсюда же требование к тегам. latest и перезаписываемые теги в проде запрещены: непонятно, что именно работает, и откат превращается в лотерею. Практика — тег по коммиту (api:9f3c1a2b) плюс, при релизе, семантическая версия (api:1.5.0), причём тег на артефакте неизменяемый (immutable tags в registry).

Вопрос 2: CI, CD и ещё раз CD

Формально короткий, но его задают, чтобы услышать точность формулировок.

ТерминЧто означает
Continuous Integrationизменения часто вливаются в общую ветку и на каждом слиянии автоматически проверяются сборкой и тестами
Continuous Deliveryкаждая успешная сборка готова к выкатке в прод; кнопку нажимает человек
Continuous Deploymentкаждая успешная сборка выкатывается в прод автоматически, без ручного шага

Полезное дополнение к ответу: разница между delivery и deployment — не техническая, а организационная. Технически они почти идентичны; убрать ручной шаг можно только при хорошем покрытии тестами, надёжном мониторинге и отработанном автооткате.

Вопрос 3: где хранить секреты пайплайна

Правильный ответ строится по уровням: секреты никогда не лежат в репозитории; в самом CI используются встроенные хранилища секретов с маскированием в логах; лучший вариант — вообще не хранить долгоживущих секретов, а получать короткоживущий токен через OIDC-федерацию (runner доказывает облаку свою личность, облако выдаёт временные креды). Дополнительно: ограничение доступа к секретам по окружениям, запрет их использования в пайплайнах из форков и обязательный сканер, который не даст закоммитить ключ.

# проверить, не утекало ли что-то в историю
gitleaks detect --source . --report-format json

# сканирование образа перед публикацией
trivy image --exit-code 1 --severity HIGH,CRITICAL "$REGISTRY/api:$TAG"

Типичные ошибки кандидатов

  • Собирают образ отдельно для каждой среды, нарушая принцип единого артефакта.
  • Деплоят по тегу latest — и не могут ответить, какая именно версия сейчас в проде.
  • Ставят тесты после сборки и деплоя «чтобы быстрее» — конвейер перестаёт быть воротами качества.
  • Путают Continuous Delivery и Continuous Deployment.
  • Хранят креды в .env, закоммиченном в репозиторий, или печатают их в логах пайплайна.
  • Не имеют плана отката и рассчитывают «откатиться коммитом» — то есть ждать ещё одну полную сборку во время инцидента.

Как ответить кратко (20–30 секунд)

«Push запускает конвейер: сначала дешёвые проверки — линтеры и статический анализ, потом unit- и интеграционные тесты, затем одна-единственная сборка образа с неизменяемым тегом по хэшу коммита и сканированием уязвимостей, публикация в registry, деплой в staging со smoke-тестами и деплой в прод. Ключевой принцип — build once, deploy anywhere: один и тот же артефакт едет во все среды, различается только конфигурация снаружи. Секреты — в хранилище CI с маскированием, а лучше вообще без долгоживущих ключей, через OIDC с временными кредами.»

Проверьте себя
1. Почему принцип build once, deploy anywhere важен для надёжности выкаток?
AОн экономит место в registry
BВ прод попадает ровно тот артефакт, который прошёл тесты; пересборка под каждую среду может незаметно изменить зависимости и базовый образ
CОн позволяет не хранить конфигурацию отдельно от кода
DОн ускоряет запуск контейнеров в Kubernetes
2. Чем Continuous Delivery отличается от Continuous Deployment?
ADelivery относится к тестам, Deployment — к сборке
BПри Delivery каждая сборка готова к выкатке, но релиз запускает человек; при Deployment выкатка в прод происходит автоматически
CDelivery работает только с контейнерами
DЭто одно и то же, разница лишь в терминологии вендоров
3. Какой способ работы с облачными кредами в CI считается лучшей практикой?
AХранить долгоживущий access key в переменных пайплайна
BКласть креды в .env-файл рядом с кодом, добавив его в .gitignore
CПолучать короткоживущие креды через OIDC-федерацию, не храня долгоживущих секретов вовсе
DПередавать креды аргументом командной строки при запуске job