Skip to main content

Що таке BuildKit і які переваги він надає?

BuildKit це сучасний Docker build engine. Legacy-builder це той, що Docker шиппив з 2014; BuildKit замінив його як дефолт у Docker 23 (2023). Різниці видно одразу: швидші білди, кращий cache, нові Dockerfile-фічі, без leak secret.

Теорія

TL;DR

  • Дефолт з Docker 23 (2023). Старіші версії Docker мали його opt-in через DOCKER_BUILDKIT=1.
  • Побудовано на іншій архітектурі: паралельне виконання стейджів, content-addressable граф операцій, pluggable frontend.
  • П'ять killer-фіч над legacy:
    1. Паралельний multi-stage — незалежні стейджі білдять конкурентно.
    2. Cache mountsRUN --mount=type=cache тримає cache між білдами без запікання у image.
    3. Secret mountsRUN --mount=type=secret для build-time secret, ніколи у image.
    4. Розумніший cache keyCOPY інвалідує лише на реальні зміни файлів, не на mtime директорії.
    5. Директива # syntax= — пін frontend-версії Dockerfile, нові інструкції без апгрейду daemon.
  • Доступ через docker buildx (BuildKit-aware CLI-розширення).

Архітектура vs legacy-builder

Legacy-builder: BuildKit: Лінійно, послідовно Граф-based, паралельно Вбудовано у dockerd Окремий engine (може бути remote) Без cache-mount First-class cache-mount ARG-значення leak у history Secret-mount (без leak) Один Dockerfile-парсер Pluggable frontend (#syntax=) Повільні rebuild Розумна cache-інвалідація

BuildKit по суті це новий build-daemon з іншою mental-model, операції утворюють DAG, BuildKit їх планує.

Killer-фіча 1: паралельні стейджі

dockerfile
FROM golang:1.23 AS go-builder RUN go build -o /out/server ./cmd/server FROM rust:1.81 AS rust-builder RUN cargo build --release --bin tool FROM ubuntu:24.04 COPY --from=go-builder /out/server /usr/local/bin/ COPY --from=rust-builder /target/release/tool /usr/local/bin/

Legacy-builder: будує Go-стейдж, потім Rust-стейдж, потім runtime. Послідовно. BuildKit: Go і Rust-стейджі білдять паралельно. Wall-clock час = max обох, не сума.

Killer-фіча 2: cache-mount

dockerfile
# syntax=docker/dockerfile:1.7 FROM python:3.13-slim WORKDIR /app COPY requirements.txt . RUN --mount=type=cache,target=/root/.cache/pip \ pip install --no-cache-dir -r requirements.txt COPY . . CMD ["python", "app.py"]

Pip wheel-cache живе у Docker-managed cache-директорії поза image. Наступні білди з тим же requirements.txt перевикористовують кешовані wheels. Image лишається малим (без pip-cache, запеченого); білд лишається швидким.

Поширені targets:

  • pip: /root/.cache/pip
  • npm: /root/.npm
  • apt: /var/cache/apt і /var/lib/apt/listssharing=locked)
  • Go: /go/pkg/mod
  • Cargo: /usr/local/cargo/registry

Killer-фіча 3: secret-mount

dockerfile
# syntax=docker/dockerfile:1.7 FROM node:22-alpine WORKDIR /app COPY package*.json ./ RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \ npm ci COPY . .
bash
docker buildx build --secret id=npmrc,src=$HOME/.npmrc -t myapp .

.npmrc змонтовано у білд, але ніколи не приземляється у жоден image-шар. docker history не показує сліду; pulled-image його не містять.

Порівняй з поганим патерном:

dockerfile
# НЕПРАВИЛЬНО: ARG з'являється у image-history ARG NPM_TOKEN RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc && npm ci

docker history --no-trunc показує RUN-рядок, включно з токеном.

Killer-фіча 4: розумніший cache key

Legacy-builder інвалідував COPY за file-timestamps (mtime). Touch файлу без редагування тригерив cache-miss.

BuildKit: cache-key для COPY це content-hash файлів. mtime-зміни не інвалідують. Лише реальні content-зміни.

Killer-фіча 5: директива # syntax=

dockerfile
# syntax=docker/dockerfile:1.7 # Тепер маєш доступ до: # --mount=type=cache # --mount=type=secret # --mount=type=ssh # --mount=type=tmpfs # here-doc синтаксис (RUN <<EOF ... EOF) # Anonymous build stages з named output

Директива пінить Dockerfile-frontend image. Нові фічі шиппяться без апгрейду daemon, просто бамп syntax-версію.

Увімкнення BuildKit (коли не дефолт)

bash
# Per-shell export DOCKER_BUILDKIT=1 docker build -t myapp . # Per-build DOCKER_BUILDKIT=1 docker build -t myapp . # Daemon-wide (старіший Docker) # /etc/docker/daemon.json { "features": { "buildkit": true } }

Docker 23+ має BuildKit увімкненим за замовчуванням; старіші версії потребують явного opt-in. Підтверди:

bash
docker buildx version # github.com/docker/buildx v0.17.0 ...

Якщо buildx встановлено і працює, BuildKit доступний.

docker buildx — BuildKit-aware CLI

bash
docker buildx build -t myapp . # як docker build, але з BuildKit-фічами docker buildx build --platform linux/amd64,linux/arm64 # multi-arch в одному заході docker buildx build --cache-to type=registry,ref=cache # registry cache export docker buildx build --cache-from type=registry,ref=cache # registry cache import docker buildx build --provenance=true --sbom=true # SLSA provenance + SBOM

buildx це те, що ти реально викликаєш для повних BuildKit-фіч. Звичайний docker build теж працює на Docker 23+ (використовує BuildKit під капотом).

Типові помилки

Забути директиву # syntax= при використанні нових фіч

dockerfile
# Не працюватиме, потребує syntax-директиви RUN --mount=type=cache,target=/root/.cache/pip pip install ...

Додай # syntax=docker/dockerfile:1.7 (або вище) нагорі Dockerfile.

Білд без buildx-builder для multi-arch

bash
$ docker buildx build --platform linux/amd64,linux/arm64 -t myapp . ERROR: Multi-platform builds require a builder instance.

Фікс: docker buildx create --use --name multi-builder спочатку. Дефолтний builder може бути single-platform Docker-driver.

Плутати docker build і docker buildx build

У Docker 23+ обидва викликають BuildKit. Але деякі флаги (--platform, --cache-from type=registry) працюють краще на buildx. Для складного CI бери buildx.

Сприймати BuildKit cache-mount як image-content

dockerfile
RUN --mount=type=cache,target=/build-output \ make && cp -r /build-output/* /app/ # Після цього RUN /build-output зник (це був mount, не частина image).

Cache-mount зникають після RUN. Щоб дістати content у image, копіюй у звичайний шлях у тому самому RUN.

Реальний impact

  • CI build-час: типовий Node/Python проект з 5 хвилин (legacy) → 90 секунд (BuildKit + cache-mount).
  • Image-розмір: менший, бо cache-директорії більше не запікаються.
  • Secret-гігієна: secret-leak через ARG-history переважно ліквідовано.
  • Multi-arch: один CI-крок будує для amd64 + arm64. Критично для кластерів, що міксують CPU.
  • Supply chain: --provenance=true --sbom=true дає SLSA-attested білди для верифікації admission-controller.

Питання для поглиблення

Q: Чи потрібен мені BuildKit, якщо мій Dockerfile простий?


A: Це дефолт у сучасному Docker, ти використовуєш BuildKit, якщо спеціально не вимикав. Навіть прості Dockerfile виграють від швидшого cache.

Q: Що таке buildx vs BuildKit?


A: BuildKit це engine. buildx це CLI-plugin, що говорить з BuildKit (і підтримує multi-platform, multi-cache тощо). docker buildx ... це як ти користуєшся повними BuildKit-фічами.

Q: Як мігрувати існуючі Dockerfile на BuildKit?


A: Вони працюють as-is на BuildKit (backward-compatible). Щоб отримати нові фічі, додай # syntax=docker/dockerfile:1.7 нагорі, тоді починай використовувати --mount=type=cache і --mount=type=secret, де допомагає.

Q: Чи може BuildKit крутитися віддалено?


A: Так. docker buildx create --driver kubernetes ... або --driver remote дозволяє крутити білди на remote-кластері. Корисно для великих білдів, що не хочеш на лептопі.

Q: (Senior) Як BuildKit DAG-архітектура відрізняється від лінійного підходу legacy-builder?


A: Legacy: кожна Dockerfile-інструкція створювала шар послідовно; cache-перевірка per-instruction. BuildKit: білд це DAG операцій LLB (low-level builder); BuildKit їх планує на основі залежностей. Незалежні операції крутяться паралельно; cache-lookup на operation-рівні (часто більш гранулярно, ніж інструкції). DAG також відкриває нові фічі (mount-points, що існують лише під час конкретної операції, frontend-свопи для різних Dockerfile-варіантів синтаксису, розподілені білди).

Приклади

Максимально сучасний Dockerfile

dockerfile
# syntax=docker/dockerfile:1.7 FROM node:22-alpine AS deps WORKDIR /app COPY package*.json ./ RUN --mount=type=cache,target=/root/.npm \ --mount=type=secret,id=npmrc,target=/root/.npmrc \ npm ci FROM deps AS build COPY . . RUN npm run build FROM node:22-alpine AS runtime WORKDIR /app COPY --from=build /app/dist /app/dist COPY --from=build /app/node_modules /app/node_modules USER node CMD ["node", "dist/server.js"]

Npm cache переживає між білдами; npmrc ніколи не у image; deps + build стейджі ділять роботу; runtime-стейдж тонкий.

CI registry-cache патерн

bash
docker buildx build \ --cache-to type=registry,ref=ghcr.io/myorg/myapp:cache,mode=max \ --cache-from type=registry,ref=ghcr.io/myorg/myapp:cache \ --platform linux/amd64,linux/arm64 \ --provenance=true \ --sbom=true \ --push \ -t ghcr.io/myorg/myapp:1.0 .

Multi-arch + registry-cache + provenance + SBOM в одній команді. CI-runner на різних машинах перевикористовують той самий cache.

Білд віддалено

bash
$ docker buildx create --name remote --driver remote tcp://buildkit:1234 --use $ docker buildx build -t myapp . # Білд крутиться на remote BuildKit-daemon, не на лептопі.

Корисно для shared-builder, GPU-accelerated білдів або просто щоб тримати лептоп вільним.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Коментарі

Ще немає коментарів