ЯдроКодаподготовка к экзаменам
Учебная платформа

Загружаем материалы

Подготавливаем материалы и навигацию по разделу.

CI/CD контейнеров: проверка образа, публикация и происхождение

Автор: · Обновлено

CI должен проверять тот артефакт, который команда собирается запускать. Успешный lint исходников не доказывает, что в итоговый образ попал нужный файл и процесс стартует. Построим небольшую проверку Dockerfile в GitHub Actions, затем разберём публикацию, SBOM и связь образа с коммитом.

Минимальный build и smoke test

Предположим, в репозитории находятся Dockerfile и app.py из [Python-практикума](/lessons/without-university/docker-containerization/docker-developer-08). Сохраните .github/workflows/container-check.yml:

name: Container check
on:
  push:
  pull_request:
permissions:
  contents: read
jobs:
  image:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - name: Build and inspect application
        run: |
          docker build --pull -t lesson-app:ci .
          docker run --rm --entrypoint python lesson-app:ci -X pycache_prefix=/tmp/pycache -m py_compile /app/app.py

Параметр pycache_prefix направляет байткод в доступный non-root процессу /tmp, не требуя записи в /app. Проверка py_compile доказывает наличие и синтаксическую корректность файла в итоговом образе. Она не проверяет HTTP, поэтому следующим шагом запускают контейнер, ждут readiness, выполняют контрольный запрос и всегда убирают ресурс. Не заменяйте эти ожидания одним фиксированным sleep: runner может быть быстрее или медленнее ноутбука.

Версия checkout сверена с официальным репозиторием; для воспроизводимой политики команды закрепите проверенный полный commit SHA action и обновляйте его управляемо. Образ runner также развивается: записывайте версии Docker/Buildx в диагностике сборки. Секреты registry не нужны для проверки pull request, которая ничего не публикует.

От проверки к публикации

После тестов доверенный release workflow авторизуется в выбранном registry и публикует образ с тегом версии или SHA коммита. Возвращённый digest сохраните в release manifest. Тег удобен человеку, но может быть переназначен; digest обозначает конкретное содержимое. Не пересобирайте молча другой образ на сервере после проверки в CI, иначе проверенный артефакт и запущенный расходятся.

Команды для уже настроенного доверенного CI-окружения выглядят так:

docker build -t "$IMAGE_REF" .
docker run --rm --entrypoint python "$IMAGE_REF" -X pycache_prefix=/tmp/pycache -m py_compile /app/app.py
docker push "$IMAGE_REF"

IMAGE_REF должен содержать разрешённый registry/repository и неизменяемый в вашей политике тег, а доступ на push — предоставляться отдельному release job. Это фрагмент после настройки авторизации, не предложение передавать пароль аргументом командной строки. Для Buildx используйте его проверенный builder и явный output: отсутствие --load или --push может оставить результат только в кеше.

Что показывают SBOM и provenance

SBOM перечисляет обнаруженные компоненты артефакта. Он помогает искать затронутые версии библиотек, но не доказывает отсутствие уязвимостей. Provenance описывает происхождение сборки и её входы в выбранной модели доверия. Подпись нужна для проверки связи с доверенным издателем; само наличие документа рядом с образом не делает его достоверным. Attestations BuildKit.

Buildx поддерживает --sbom=true и --provenance=true; сохранение attestations зависит от exporter и image store. Проверьте их наличие в registry, а не только успешный build. Для локальной проверки без push не считайте, что Docker image store обязательно сохранил все дополнительные документы. SBOM.

Подписать образ и проверить доверенного издателя

Для практики нужен установленный Cosign, собственный тестовый repository в registry с правом записи и уже опубликованный digest. Получите его из успешного push/inspect и задайте переменную IMAGE_DIGEST полным значением вида registry.example/your/image@sha256:..., заменив многоточие настоящим digest. Используйте этот digest во всех шагах, чтобы изменение тега не меняло предмет проверки.

В новой закрытой папке создайте учебную пару ключей; Cosign запросит пароль для приватного ключа. Не добавляйте cosign.key в Git, CI-логи или образ. Публичный cosign.pub передайте проверяющей стороне отдельным доверенным каналом.

cosign generate-key-pair
cosign sign --key cosign.key "$IMAGE_DIGEST"
cosign verify --key cosign.pub "$IMAGE_DIGEST"

Sign публикует подпись рядом с артефактом в выбранном registry и в стандартной конфигурации может обращаться к журналу прозрачности. До подтверждения просмотрите вывод, особенно для закрытых проектов: не публикуйте сведения о секретном артефакте в публичный журнал без принятой политики. Подпись контейнеров.

Проверка должна завершиться успешно только с доверенным ключом и корректной подписью нужного digest. Возьмите другой учебный публичный ключ: verify должен отказать. Загрузка ключа с той же непроверенной страницы, откуда пришёл образ, не устанавливает доверие. Подпись подтверждает связь артефакта с ключом, но не отсутствие уязвимостей и не качество тестов. Семантика verify.

Для keyless-подписей вместо доверенного собственного ключа проверяют ожидаемую identity и OIDC issuer; нельзя принимать произвольную личность только потому, что подпись математически корректна. В release pipeline отклоняйте запуск при неудачном verify и после него используйте тот же digest. Такой gate проверяет происхождение конкретного содержимого, а не плавающего названия latest.

Проверка отказа

Измените путь COPY так, чтобы app.py не попал в финальный stage: сборка может завершиться, а тест образа обязан упасть. Затем восстановите файл и добавьте реальный HTTP smoke test с ограниченным временем ожидания. В отчёт сохраните commit, digest, версии инструментов и результаты. Такая цепочка позволяет обосновать выпуск конкретного артефакта и затем откатиться к ранее проверенному, не обещая абсолютной безопасности на основании одного зелёного job.

Источники