← К программе курса

Модуль 5 / 18

BuildKit: cache, multi-stage и build contexts

DOCKER

Содержание
01

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
02

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.

03

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.

04

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.

05

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 всё ещё может утечь данные намеренно или по ошибке.

06

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.

07

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
08

8. Лабораторная работа: graph, mounts и cache export

Потребуется установленный docker-buildx-plugin и доступ Docker daemon к registry. Создайте отдельные default и named contexts, учебный secret с правами 0600 и multi-stage Dockerfile.

terminal
docker version
docker buildx version
docker buildx ls
docker info --format 'containerd_image_store={{json .DriverStatus}}'
docker system df
terminal
mkdir -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, поэтому первая операция дольше последующих.

terminal
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.

terminal
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.

terminal
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:rebuilt
terminal
mkdir -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.

09

9. Диагностика: build не стартует или теряет cache

Запустите сборку без обязательного secret. Ожидаемая ошибка required secret подтверждает работу policy, а не проблему registry. После неё исследуйте builder и его container.

terminal
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

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 не доказывает воспроизводимость?
terminal
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.