← До програми курсу

Модуль 16 / 18

CI/CD: buildx, multi-platform та immutable delivery

DOCKER

Зміст
01

1. Цілі та результат CI/CD

Після модуля ви зможете побудувати pipeline, який один раз збирає multi-platform image, перевіряє його та просуває один digest між середовищами.

  • commit запускає build
  • gates передують publication
  • release задається digest
  • promotion не перебудовує artifact
02

2. Теорія: стадії pipeline

Pipeline розділяє lint і unit tests, перевірку Dockerfile, build, integration tests, supply-chain gates, push та deployment. Ранній дешевий failure має зупинити наступні стадії.

Runner входить до trusted computing base. Його image, Docker/Buildx versions, permissions і network access фіксують. Спільний daemon збільшує ризик витоку cache, credentials і privileged control.

03

3. Теорія: Buildx і BuildKit

Buildx керує builders, BuildKit виконує build graph. docker-container driver дає isolated builder та OCI, registry і cache exporters, але результат не завантажується автоматично до класичного image store.

Builder створюють детерміновано, bootstrap перевіряють до build, metadata і build record зберігають як evidence.

04

4. Теорія: multi-platform images

Multi-platform image — OCI index із manifest для кожної OS/architecture. Один digest index описує весь набір, а registry вибирає manifest для клієнта.

Стратегії: QEMU emulation, native nodes і cross-compilation. Для cross-build stage закріплюють за BUILDPLATFORM, compiler отримує TARGETOS, TARGETARCH і TARGETVARIANT.

05

5. Теорія: cache та відтворюваність

Для ephemeral runners використовують registry або CI cache exporter. Cache лише прискорює build: scope враховує platform, toolchain і lockfiles.

Base images та dependencies закріплюють digest/lockfile; secrets передають secret або SSH mounts, не ARG. SOURCE_DATE_EPOCH допомагає, але інші inputs чи builder змінюють digest.

06

6. Теорія: immutable delivery

Tag зручний, але змінний. Після push pipeline отримує digest із metadata або registry, перевіряє repository@digest і передає його deployment-системі.

Promotion переносить перевірений manifest без rebuild. Production rebuild створює новий неперевірений artifact. Environment config зберігають окремо.

07

7. Методичні вказівки

  • Фіксуйте CI actions та builder
  • Мінімальні job permissions
  • Без secrets для untrusted PR
  • Tests до push
  • SBOM, provenance, scan і signature
  • Окремі build/deploy identities
  • Deployment concurrency control
  • Зберігайте digest та evidence
  • Rollback на попередній digest
08

8. Лабораторна робота: multi-platform artifact

Створіть isolated builder і native build stage для двох TARGETPLATFORM.

terminal
rm -rf /tmp/docker-m16
mkdir -p /tmp/docker-m16/context /tmp/docker-m16/unpacked
cat > /tmp/docker-m16/context/Dockerfile <<'EOF'
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM alpine:3.21 AS build
ARG BUILDPLATFORM
ARG TARGETPLATFORM
RUN printf 'built_on=%s\ntarget=%s\n' "$BUILDPLATFORM" "$TARGETPLATFORM" > /artifact.txt
FROM alpine:3.21
COPY --from=build /artifact.txt /artifact.txt
CMD ["cat", "/artifact.txt"]
EOF
docker buildx rm docker-m16 2>/dev/null || true
docker buildx create --name docker-m16 --driver docker-container --use --bootstrap
docker buildx inspect --bootstrap

Експортуйте amd64 та arm64 до OCI archive. Provenance тут вимкнено лише для дослідження index; у production увімкніть його.

terminal
docker buildx build --builder docker-m16 \
  --platform linux/amd64,linux/arm64 --provenance=false \
  --metadata-file /tmp/docker-m16/metadata.json \
  --output type=oci,dest=/tmp/docker-m16/image.oci.tar \
  /tmp/docker-m16/context
tar -xf /tmp/docker-m16/image.oci.tar -C /tmp/docker-m16/unpacked
jq . /tmp/docker-m16/metadata.json

Знайдіть root digest та перевірте content-addressed manifests.

terminal
index_digest=$(jq -r '."containerimage.digest"' /tmp/docker-m16/metadata.json)
index_file="/tmp/docker-m16/unpacked/blobs/sha256/${index_digest#sha256:}"
test -f "$index_file"
jq -r '.manifests[].platform | "\(.os)/\(.architecture)"' "$index_file" | sort
actual="sha256:$(sha256sum "$index_file" | awk '{print $1}')"
test "$actual" = "$index_digest"
for digest in $(jq -r '.manifests[].digest' "$index_file"); do
  blob="/tmp/docker-m16/unpacked/blobs/sha256/${digest#sha256:}"
  test "sha256:$(sha256sum "$blob" | awk '{print $1}')" = "$digest"
  printf 'verified_manifest=%s\n' "$digest"
done
terminal
test "$(jq -r '.manifests | length' "$index_file")" -eq 2
test "$(jq -r '[.manifests[].platform | select(.os=="linux" and (.architecture=="amd64" or .architecture=="arm64"))] | length' "$index_file")" -eq 2
printf 'release_candidate=%s\n' "$index_digest"

Критерій: рівно дві platforms, index і обидва manifests пройшли checksum.

09

9. Діагностика CI та release

Перевіряйте checkout → bootstrap → dependencies → platform execution → cache → exporter → registry auth → policy → deploy.

terminal
docker buildx ls
docker buildx inspect docker-m16 --bootstrap
docker logs buildx_buildkit_docker-m160 --tail 100
jq . /tmp/docker-m16/metadata.json
tar -tf /tmp/docker-m16/image.oci.tar | sed -n '1,40p'
docker system df
df -h /var/lib/docker
  • exec format error — немає emulator
  • no match for platform — немає base manifest
  • failed to solve — знайдіть BuildKit vertex
  • cache miss — scope/inputs
  • denied — identity/scope
  • digest змінився — inputs, builder або rebuild
10

10. Контроль і cleanup

  • Buildx чи BuildKit?
  • Чому результат є index?
  • Коли emulation, native чи cross-build?
  • Чому --load не для звичайного multi-platform output?
  • Чому tag недостатній?
  • Чому не можна rebuild після tests?
  • Які evidence потрібні?
terminal
docker buildx rm docker-m16
rm -rf /tmp/docker-m16
docker ps -a --filter name=buildx_buildkit_docker-m16

Модуль засвоєно, якщо один index digest проходить build → verify → promote → deploy без перебудови.