Модуль 5 / 18
BuildKit: cache, multi-stage и build contexts
Содержание
1. Цели и границы модуля
После модуля вы сможете управлять BuildKit builder, строить multi-stage graph, объяснять cache key, временно подключать cache и secret mounts, использовать named contexts и переносить cache между независимыми сборками.
- BuildKit решает build graph и выполняет независимые операции параллельно
- buildx управляет builders и расширенным интерфейсом build
- multi-stage отделяет инструменты сборки от runtime image
- cache mount ускоряет команду, но не становится layer автоматически
- secret mount существует только во время конкретного RUN
- external cache требует явных cache-to и cache-from
2. Теория: BuildKit, Buildx и driver
BuildKit — backend сборки, который преобразует Dockerfile frontend в LLB graph, вычисляет зависимости и cache keys, выполняет операции и экспортирует результат. Buildx — Docker CLI plugin для управления одним или несколькими builder instances и вызова BuildKit.
Builder имеет driver. docker использует BuildKit, встроенный в Engine, и имеет ограничения, зависящие от image store. docker-container запускает отдельный BuildKit daemon в container, даёт управляемую версию и расширенные exporters, но результат не появляется в локальном image store без --load. --push отправляет image в registry; отсутствие явного output может оставить только cache result.
3. Теория: multi-stage build graph
Каждый FROM начинает stage. Имена через AS стабильнее числовых индексов. COPY --from переносит только выбранные artifacts; compiler, source и package cache не обязаны входить в runtime image. --target останавливает сборку на указанной цели и удобен для test, debug и анализа.
BuildKit строит dependency graph и пропускает stage, от которого выбранный target не зависит. Уменьшение runtime image — следствие явно выбранных artifacts и base image, а не самого количества FROM. Уязвимые библиотеки можно случайно перенести вместе с широким COPY.
4. Теория: cache keys и mounts
Cache hit определяется операцией и её inputs. Для COPY учитываются metadata и checksum файлов; mtime сам по себе обычно не является ключом. Для RUN строка команды сравнивается, но BuildKit не определяет, изменился ли удалённый package repository. Secret value не участвует в cache key, поэтому при необходимой инвалидизации добавляют отдельный безопасный ARG, а не раскрывают secret.
RUN --mount=type=cache даёт package manager постоянный рабочий cache между builds, но его содержимое не попадает в итоговый layer. sharing=locked нужен инструментам, которым опасна параллельная запись. Cache — ускоритель, а не источник истины: build должен оставаться корректным после очистки cache.
5. Теория: secret, SSH и bind mounts
Secret передают build client через --secret и подключают только к нужному RUN. required=true превращает отсутствие secret в явный failure. SSH mount проксирует agent socket для Git/SSH, а не копирует private key в image. Remote Git context использует отдельные pre-flight authentication secrets.
Bind mount в RUN временно показывает файлы context без сохранения в layer. Он полезен для больших source trees, когда нужен только build artifact. Secret нельзя печатать, копировать в /out или включать в cache export: механизм mount уменьшает риск, но команда внутри RUN всё ещё может утечь данные намеренно или по ошибке.
6. Теория: named contexts и external cache
Named context задаётся --build-context name=value и используется как stage в COPY --from=name или mount from=name. Это позволяет раздельно передавать source, документацию, конфигурацию либо pinned image, не расширяя default context.
Local cache подходит для лаборатории, registry cache — для независимых CI runners, inline проще, но обычно содержит только минимальный cache. mode=max сохраняет также промежуточные stages и увеличивает объём. Один cache destination нельзя безопасно перезаписывать несколькими jobs; scope проектируют по repository, platform и branch, а main cache часто импортируют как fallback.
7. Методические указания
- Зафиксируйте docker buildx version, driver и BuildKit version
- Именуйте stages по назначению: build, test, runtime
- Используйте --load только для single-platform локальной проверки
- Не ожидайте локальный image после container-driver build без output
- Передавайте secrets только через --secret или --ssh
- Не выводите содержимое secret в build logs
- Разделяйте cache образа, package cache и external cache
- Не выполняйте prune до сбора du, logs и metadata builder
8. Лабораторная работа: graph, mounts и cache export
Потребуется установленный docker-buildx-plugin и доступ Docker daemon к registry. Создайте отдельные default и named contexts, учебный secret с правами 0600 и multi-stage Dockerfile.
docker version
docker buildx version
docker buildx ls
docker info --format 'containerd_image_store={{json .DriverStatus}}'
docker system dfmkdir -p /tmp/docker-module-05/app /tmp/docker-module-05/docs
printf '%s
' 'BuildKit module 5' > /tmp/docker-module-05/app/source.txt
printf '%s
' 'named context notice' > /tmp/docker-module-05/docs/notice.txt
printf '%s
' 'not-a-real-secret' > /tmp/docker-module-05/course-token.txt
chmod 600 /tmp/docker-module-05/course-token.txt
printf '%s
' 'course-token.txt' '.git' '*.log' > /tmp/docker-module-05/app/.dockerignore
printf '%s
' '# syntax=docker/dockerfile:1' 'FROM busybox:1.37 AS build' 'WORKDIR /work' 'COPY source.txt ./' 'RUN --mount=type=cache,target=/var/cache/course sh -c '''printf "cache-ready\n" > /var/cache/course/state'''' 'RUN --mount=type=secret,id=course_token,required=true sh -c '''test -s /run/secrets/course_token && mkdir -p /out && cp source.txt /out/index.html'''' 'FROM busybox:1.37 AS runtime' 'WORKDIR /srv/www' 'COPY --from=build --chown=65532:65532 /out/index.html ./' 'COPY --from=course_docs --chown=65532:65532 /notice.txt ./' 'USER 65532:65532' 'EXPOSE 8080' 'ENTRYPOINT ["httpd","-f","-p","8080","-h","/srv/www"]' > /tmp/docker-module-05/app/Dockerfile
find /tmp/docker-module-05 -maxdepth 2 -type f -printf '%P %m %s bytes
' | sortСоздайте изолированный container builder. Bootstrap может загрузить официальный BuildKit image, поэтому первая операция дольше последующих.
docker buildx create --name docker-m5-builder --driver docker-container --use
docker buildx inspect --bootstrap
docker buildx ls
docker buildx inspect docker-m5-builderСоберите runtime target, явно передав secret и named context, затем докажите отсутствие ephemeral mounts в image.
docker buildx build --builder docker-m5-builder --progress=plain --secret id=course_token,src=/tmp/docker-module-05/course-token.txt --build-context course_docs=/tmp/docker-module-05/docs --tag docker-m5:runtime --load /tmp/docker-module-05/app
docker image inspect docker-m5:runtime --format '{{json .Config}}' | jq '{User,WorkingDir,Entrypoint,ExposedPorts}'
docker run --rm --entrypoint cat docker-m5:runtime /srv/www/index.html
docker run --rm --entrypoint cat docker-m5:runtime /srv/www/notice.txt
docker run --rm --entrypoint sh docker-m5:runtime -c 'test ! -e /run/secrets/course_token && test ! -e /var/cache/course && printf "ephemeral mounts absent\n"'
docker image history --no-trunc docker-m5:runtimeИсследуйте target и выборочную инвалидизацию, затем экспортируйте и импортируйте local external cache.
docker buildx build --builder docker-m5-builder --progress=plain --target build --secret id=course_token,src=/tmp/docker-module-05/course-token.txt --build-context course_docs=/tmp/docker-module-05/docs /tmp/docker-module-05/app
docker buildx build --builder docker-m5-builder --progress=plain --no-cache-filter build --secret id=course_token,src=/tmp/docker-module-05/course-token.txt --build-context course_docs=/tmp/docker-module-05/docs --tag docker-m5:rebuilt --load /tmp/docker-module-05/app
docker image inspect --format '{{.Id}}' docker-m5:runtime docker-m5:rebuiltmkdir -p /tmp/docker-module-05/cache-a /tmp/docker-module-05/cache-b
docker buildx build --builder docker-m5-builder --secret id=course_token,src=/tmp/docker-module-05/course-token.txt --build-context course_docs=/tmp/docker-module-05/docs --cache-to type=local,dest=/tmp/docker-module-05/cache-a,mode=max /tmp/docker-module-05/app
docker buildx build --builder docker-m5-builder --secret id=course_token,src=/tmp/docker-module-05/course-token.txt --build-context course_docs=/tmp/docker-module-05/docs --cache-from type=local,src=/tmp/docker-module-05/cache-a --cache-to type=local,dest=/tmp/docker-module-05/cache-b,mode=max /tmp/docker-module-05/app
find /tmp/docker-module-05/cache-a -maxdepth 2 -type f -printf '%P %s bytes
' | sort | head -20Критерий: runtime image загружен локально; named context присутствует; secret и cache mount отсутствуют; повторная сборка показывает cache hits; local cache содержит OCI index и blobs.
9. Диагностика: build не стартует или теряет cache
Запустите сборку без обязательного secret. Ожидаемая ошибка required secret подтверждает работу policy, а не проблему registry. После неё исследуйте builder и его container.
docker buildx build --builder docker-m5-builder --progress=plain --build-context course_docs=/tmp/docker-module-05/docs /tmp/docker-module-05/app || true
docker buildx inspect docker-m5-builder
docker buildx du --builder docker-m5-builder
docker logs buildx_buildkit_docker-m5-builder0 2>&1 | tail -80
docker context show- unknown command buildx — отсутствует или несовместим CLI plugin
- inactive builder — выполнить inspect --bootstrap и проверить daemon/network
- результат не виден docker image ls — не выбран --load или image exporter
- failed to compute cache key — проверить context, .dockerignore и COPY source
- cache import miss — проверить backend, ref, scope, mode и credentials
- exec format error — проверить target platform и runtime architecture
- secret not found — сверить id в CLI и RUN mount
10. Контроль, cleanup и шпаргалка
- Чем BuildKit отличается от buildx?
- Зачем container driver требует --load?
- Что исключает multi-stage из runtime image?
- Почему значение secret не должно входить в cache key?
- Чем cache mount отличается от layer cache?
- Когда выбирать mode=max?
- Как named context отличается от default context?
- Почему успешный cache import не доказывает воспроизводимость?
docker image rm docker-m5:runtime docker-m5:rebuilt 2>/dev/null || true
docker buildx rm docker-m5-builder
rm -f /tmp/docker-module-05/app/Dockerfile /tmp/docker-module-05/app/.dockerignore /tmp/docker-module-05/app/source.txt /tmp/docker-module-05/docs/notice.txt /tmp/docker-module-05/course-token.txt
rm -rf /tmp/docker-module-05/cache-a /tmp/docker-module-05/cache-b
rmdir /tmp/docker-module-05/app /tmp/docker-module-05/docs /tmp/docker-module-05Модуль освоен, если вы читаете build как dependency graph, прогнозируете cache hit, безопасно подключаете transient inputs и переносите cache без включения secrets в image или logs.