CI/CD пайплайн: lint, test, build, scan, deploy

О лекции

Сначала проверяют код и собирают один неизменяемый образ. Потом его выкатывают. В staging и production уходит тот же digest, который собрали в CI. Не свежий latest с сервера.

CI/CD cicd собеседование

CI — проверка на коммит или merge request: линт, тесты, сборка, скан. CD — доставка на окружение. Автоматом с main или кнопкой. Кнопка на prod — всё ещё CD, если артефакт тот же.

Главное слово — один артефакт. Собрали образ с digest sha256:abc. На нём тесты и сканер. Staging и prod тянут его же.

Пересборка «с тем же тегом» через час — уже другой бинарь. Сдвинулся базовый слой alpine. Обновился пакет. Сработал случайный кэш. Вы думаете, что выкатили то, что тестировали. Байты другие.

Пример. CI собрал api@sha256:abc. Staging подняли этот digest. На prod ставят тот же sha256:abc. Не docker build заново на проде и не :latest.

Стадии

  • Lint и format. Дёшево ловят шум до ревью.
  • Тесты. В Go на CI включают -race. Интеграция бьёт в настоящий Postgres, если баг в SQL.
  • Build. Образ один раз. Дальше только этот digest.
  • Scan. Дыры в ОС и зависимостях, секреты в диффе. Что блокирует merge — политика команды.
  • Deploy. Сначала staging, потом prod.
stages: [test, build, scan, deploy]

variables:
  IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

test:
  stage: test
  script:
    - go test -race ./...

build:
  stage: build
  script:
    - docker build -t $IMAGE .
    - docker push $IMAGE

scan:
  stage: scan
  script:
    - trivy image --exit-code 1 $IMAGE

deploy:
  stage: deploy
  script:
    - helm upgrade --install api ./chart --set image=$IMAGE
  when: manual

Один digest проходит test и scan и уходит в деплой. latest здесь нет.

Секреты и права

Пароль деплоя берут из переменных CI, Vault, OIDC в облако. Не из YAML репозитория. Не из Dockerfile. Логи job не печатают секреты «для отладки».

Сборка на сервере через git pull ломает правило одного артефакта. На сервере другой Go, другой кэш модулей, иногда ручные правки. Откат тогда — лотерея.

Пример. Job напечатал пароль registry в лог. Лог видят все, у кого доступ к пайплайну. Это уже утечка, даже если git чистый.

Типичная ошибка

Тег latest, пересборка образа на каждом окружении и вера, что «это тот же коммит». Базовый слой обновился ночью — в prod уехал другой бинарь. Скан с always allow_failure не блокирует дыры.

На собеседовании

Один digest через все среды. Тесты и скан до деплоя. Секреты из store, не из git. Кнопка на prod нормальна, если байты те же.