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

Модуль 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, обчислює dependencies і cache keys, виконує operations та експортує результат. 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. Вразливі libraries можна випадково перенести разом із широким COPY.

04

4. Теорія: cache keys і mounts

Cache hit визначається операцією та її inputs. Для COPY враховуються metadata і checksum файлів; mtime сам по собі зазвичай не є ключем. Для RUN порівнюється рядок команди, але BuildKit не визначає, чи змінився віддалений package repository. Secret value не бере участі в cache key, тому за потрібної invalidation додають окремий безпечний 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 і вибіркову invalidation, потім експортуйте та імпортуйте 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.