Модуль 16 / 18
CI/CD: buildx, multi-platform та immutable delivery
Зміст
1. Цілі та результат CI/CD
Після модуля ви зможете побудувати pipeline, який один раз збирає multi-platform image, перевіряє його та просуває один digest між середовищами.
- commit запускає build
- gates передують publication
- release задається digest
- promotion не перебудовує artifact
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.
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.
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.
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.
6. Теорія: immutable delivery
Tag зручний, але змінний. Після push pipeline отримує digest із metadata або registry, перевіряє repository@digest і передає його deployment-системі.
Promotion переносить перевірений manifest без rebuild. Production rebuild створює новий неперевірений artifact. Environment config зберігають окремо.
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
8. Лабораторна робота: multi-platform artifact
Створіть isolated builder і native build stage для двох TARGETPLATFORM.
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 увімкніть його.
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.
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"
donetest "$(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.
9. Діагностика CI та release
Перевіряйте checkout → bootstrap → dependencies → platform execution → cache → exporter → registry auth → policy → deploy.
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. Контроль і cleanup
- Buildx чи BuildKit?
- Чому результат є index?
- Коли emulation, native чи cross-build?
- Чому --load не для звичайного multi-platform output?
- Чому tag недостатній?
- Чому не можна rebuild після tests?
- Які evidence потрібні?
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 без перебудови.