BuildKit とは

/用語集/BuildKit を参照

BuildKit の概要

BuildKit は buildkitd デーモンと buildctl クライアントにより構成されています。 buildkitd はデフォルトで gRPC による通信を受け付けます。

BuildKit のビルドは LLB(Low-Level intermediate build format)※ と呼ばれる中間形式のバイナリを基にして行われます。 LLB は DAG (有向非巡回グラフ)構造を持つため、ビルドステップの依存関係グラフを定義するために使用されます。

※ LLB の名称は docker con 18 の発表資料に書かれた資料 external_link の内容を採用し、moby project ブログ Introducing BuildKit external_link にある内容は採用しませんでした

Frontend と呼ばれる BuildKit に含まれるコンポーネントが Dockerfile 等のハイレベルの言語で記述されたファイルを LLB へ変換します。 Frontend には gateway.v0dockerfile.v0(開発中のみの存在) が存在し、dockerfile frontend を使うとホスト内のファイルが参照でき、gateway frontend を使うとネットワーク越しのファイルを参照することが出来ます。

ビルドした結果は次に示すとおり、いくつかの形式で出力でき、オプションによりレジストリへプッシュしたり、圧縮・展開などを行うことが出来ます。

  • ファイル
  • OCI tarball
  • Docker tarball (docker load 出来る形式)
  • containerd image store

ビルドキャッシュは出力先と形式をいくつか指定でき、出力先と形式の違いにより定義された次に示す exporter が用意されています。

  • inline exporter
    • キャッシュをイメージへ埋め込み、レジストリへ共にプッシュする
    • (キャッシュをインポートする際は --import-cache type=registry,ref=... を指定する必要があるため注意)
  • registry exporter
    • イメージとキャッシュを分離してプッシュする
  • local exporter
    • ローカルディレクトリへ出力する

Docker における BuildKit の導入

導入メリット

BuildKit を使って Docker コンテナイメージをビルドするメリットは具体的に次のとおりです。

  • 複数ステージのビルドを並列実行できる
  • 機密情報をビルド成果物(キャッシュを含む)に残さない仕組み(Dockerfile 内の RUN --mount コマンド)が使える(参考1 external_link, 参考2 external_link)
    • ローカルファイルをビルド時のみに使える
    • リモートファイルをビルド時にSSH接続して取得できる
  • ビルドキャッシュのインポート/エクスポートが出来る
  • 分散ビルドが出来る (将来的に出来るようになること)

Docker バージョンに対する BuildKit 使用方法

Docker 18.06 にて BuildKit が試験的に導入され、Docker 18.09 以降で正式に導入されました。 このバージョンでは環境変数を設定して一時的に BuildKit を有効にする方法と、設定ファイルによりデフォルトで有効にする方法があります。

  • 環境変数を設定する方法
    • DOCKER_BUILDKIT=1 を設定する
    • 例えば DOCKER_BUILDKIT=1 docker build . により一時的に BuildKit を使ってビルド出来ます
  • 設定ファイルに設定する方法
    • /etc/docker/daemon.jsonfeatures.buildkit: true を設定する
    • 例えば以下の値を設定して dockerd を再起動します
      • docker build --help を実行して --secret オプションが表示されれば BuildKit が使えるようになっていることが分かります
/etc/docker/daemon.json
{ "features": { "buildkit": true } }

また、Docker 19.03 ではさらに機能が強化された上で docker/buildx プラグインとして実装されました。(Docker Buildx external_link) このバージョンでは試験機能 (experimental features) モードを有効にすることで docker buildx <COMMAND> により BuildKit を使うことが出来ます。 例えば docker buildx build . により BuildKit を使ったビルドを行うことが出来ます。

試験機能モードを有効にするためには config.json (デフォルトでは ~/.docker/config.json に保存される) に "experimental": "enabled" を指定します。 このとき、環境変数 DOCKER_BUILDKIT=1 を設定する必要はありません。また dockerd の再起動は不要です。

~/.docker/config.json
{ "experimental": "enabled" }
$ docker -v Docker version 19.03.6, build 369ce74a3c $ docker buildx --help Usage: docker buildx COMMAND Build with BuildKit Management Commands: imagetools Commands to work on images in registry Commands: bake Build from a file build Start a build create Create a new builder instance inspect Inspect current builder instance ls List builder instances rm Remove a builder instance stop Stop builder instance use Set the current builder instance version Show buildx version information Run 'docker buildx COMMAND --help' for more information on a command.

ビルドコマンド

buildx プラグインを使わない場合は通常の docker build コマンドによりビルド出来ます。

BuildKitを利用したイメージビルド(環境変数を使う場合)
$ DOCKER_BUILDKIT=1 docker build .

buildx プラグインを使う場合は docker buildx build によりビルド出来ます。

BuildKitを利用したイメージビルド(docker/buildxプラグインを使う場合※Docker19.03以降)
$ docker buildx build .

ビルドキャッシュの利用

デフォルトでビルドキャッシュは BuildKit内部 に保存されます。 ここで、ビルドキャッシュが保存される場所は docker ドライバを使う場合は dockerd が動作するホスト内であり、 docker-container ドライバを使う場合は BuildKit が動作するコンテナ内です。

外部のビルドキャッシュに保存する場合は --cache-to で指定します。(オプションは docker-container ドライバの場合のみ指定可能)

外部のビルドキャッシュを使用する場合は --cache-from オプションにより指定します。

Docker Hub にキャッシュを保存して効率よくビルドする方法

Docker Hub にキャッシュを保存して効率よくビルドする方法は次のとおりです。

外部キャッシュを使ってビルドし、イメージをキャッシュと共にプッシュする
$ DOCKERHUB_REPOSITORY=<YOUR_NAME>/<REPOSITORY_NAME> $ docker buildx build \ --tag ${DOCKERHUB_REPOSITORY} \ --platform linux/amd64 \ --cache-from type=registry,ref=${DOCKERHUB_REPOSITORY} \ --cache-to type=inline \ --push \ .

Docker Hub のキャッシュを使ってビルドし、ローカルに保存する方法

Docker Hub のビルドキャッシュを使ってビルドし、ローカルに保存する方法は次のとおりです。 ビルドしたイメージにバージョン等のタグをつける場合に有効です。

外部キャッシュを使ってビルドし、イメージをローカルに保存する
$ DOCKERHUB_REPOSITORY=<YOUR_NAME>/<REPOSITORY_NAME> $ docker buildx build \ --tag ${DOCKERHUB_REPOSITORY} \ --platform linux/amd64 \ --cache-from type=registry,ref=${DOCKERHUB_REPOSITORY} \ --load \ . $ VERSION=<VERSION(ex. "0.3.0")> $ docker image tag ${DOCKERHUB_REPOSITORY} ${DOCKERHUB_REPOSITORY}:${VERSION}

BuildKit のビルドキャッシュと Docker のビルドキャッシュは別の場所に保存されますが、docker history で同様に確認することが出来ます。

ビルドキャッシュの確認

docker history コマンドにイメージ名を指定すると出力される結果の IMAGE 列を見るとイメージをビルドした時に作成されたキャッシュが確認できます。(docker image ls -a コマンドによりキャッシュされたイメージが確認できます) これらのキャッシュはイメージが削除されると破棄されます。

BuildKit未使用時のビルドキャッシュ
$ docker history 2cae1c7f3bb1 IMAGE CREATED CREATED BY SIZE COMMENT 2cae1c7f3bb1 30 seconds ago /bin/sh -c #(nop) CMD ["backup" "prune" "li… 0B 8a8ba5ba5ed8 30 seconds ago /bin/sh -c #(nop) ENTRYPOINT ["/opt/bin/ent… 0B 96a1b883328d 30 seconds ago /bin/sh -c #(nop) WORKDIR /opt/bin 0B aa7a6ac5bc35 31 seconds ago /bin/sh -c #(nop) COPY dir:ebefed72581170cd5… 10.6kB c14edf2fc444 31 seconds ago /bin/sh -c #(nop) ENV AWS_DEFAULT_REGION=ap… 0B 64972185d8d4 31 seconds ago |2 CLOUD_SDK_URL=https://dl.google.com/dl/cl… 309B b77564bb5b09 34 seconds ago |2 CLOUD_SDK_URL=https://dl.google.com/dl/cl… 5.13MB f9a28314aea4 41 seconds ago |2 CLOUD_SDK_URL=https://dl.google.com/dl/cl… 251MB 6cceb62a25f0 About a minute ago /bin/sh -c #(nop) ARG CLOUD_SDK_URL=https:/… 0B dbda18957072 About a minute ago /bin/sh -c #(nop) ARG CLOUD_SDK_VERSION=281… 0B 76f445c07602 About a minute ago /bin/sh -c pip install awscli 68.6MB fb8673a8e244 About a minute ago /bin/sh -c apk add --no-cache coreutils … 165MB 0fe716eb3f28 About a minute ago /bin/sh -c #(nop) LABEL maintainer=WESEEK <… 0B 6d1ef012b567 12 months ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 12 months ago /bin/sh -c #(nop) ADD file:aa17928040e31624c… 4.21MB
BuildKit使用時のビルドキャッシュ
$ docker history 7f2334605161 IMAGE CREATED CREATED BY SIZE COMMENT 7f2334605161 40 seconds ago CMD ["backup" "prune" "list"] 0B buildkit.dockerfile.v0 <missing> 40 seconds ago ENTRYPOINT ["/opt/bin/entrypoint.sh"] 0B buildkit.dockerfile.v0 <missing> 40 seconds ago WORKDIR /opt/bin 0B buildkit.dockerfile.v0 <missing> 40 seconds ago COPY bin /opt/bin # buildkit 10.6kB buildkit.dockerfile.v0 <missing> 40 seconds ago ENV AWS_DEFAULT_REGION=ap-northeast-1 0B buildkit.dockerfile.v0 <missing> 40 seconds ago RUN |2 CLOUD_SDK_VERSION=281.0.0 CLOUD_SDK_U… 309B buildkit.dockerfile.v0 <missing> 42 seconds ago RUN |2 CLOUD_SDK_VERSION=281.0.0 CLOUD_SDK_U… 5.13MB buildkit.dockerfile.v0 <missing> 47 seconds ago RUN |2 CLOUD_SDK_VERSION=281.0.0 CLOUD_SDK_U… 251MB buildkit.dockerfile.v0 <missing> 57 seconds ago ARG CLOUD_SDK_URL=https://dl.google.com/dl/c… 0B buildkit.dockerfile.v0 <missing> 57 seconds ago ARG CLOUD_SDK_VERSION=281.0.0 0B buildkit.dockerfile.v0 <missing> 57 seconds ago RUN /bin/sh -c pip install awscli # buildkit 68.6MB buildkit.dockerfile.v0 <missing> About a minute ago RUN /bin/sh -c apk add --no-cache coreut… 165MB buildkit.dockerfile.v0 <missing> About a minute ago LABEL maintainer=WESEEK <info@weseek.co.jp> 0B buildkit.dockerfile.v0 <missing> 12 months ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 12 months ago /bin/sh -c #(nop) ADD file:aa17928040e31624c… 4.21MB

ビルドキャッシュの削除

docker ドライバを使う場合は docker builder prune コマンドにより削除できます。

$ docker builder prune WARNING! This will remove all dangling build cache. Are you sure you want to continue? [y/N] y Deleted build cache objects: lvle82pkre0lf8kf0mfk7oql6 e28zwrr5mc29ypc8rx9kiexre kyjw2zvkv5nnm0r58b3jdrxsq Total reclaimed space: 142B

docker-container ドライバを使う場合は BuildKit コンテナのシェルにて buildctl prune コマンドを実行することで削除できます。(docker builder prune で消せればよいと思うのですが消えません...)

$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fec0ae39de39 moby/buildkit:buildx-stable-1 "buildkitd" 2 days ago Up 2 days buildx_buildkit_elated_dewdney0 $ $ docker exec buildx_buildkit_elated_dewdney0 buildctl prune ID RECLAIMABLE SIZE LAST ACCESSED j9uce3ql87wwe5s4mjghj2kct* true 8.22kB yvmcrshb44p02r2wggdpv7z5f true 8.48kB cwnkq42al3idkfeas0g0miv3z* true 4.21kB yr2wyc3rw11ehbp3yox7ffgx4* true 4.10kB sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10 true 8.77MB Total: 8.79MB

非 BuildKit におけるキャッシュイン・キャッシュアウトの仕組み

docker build では同一 ID のイメージがある場合にキャッシュ機能が有効になります。 そのため、マルチステージビルドでキャッシュを使う場合は各ステージのイメージを push する必要があります。

docker build の --cache-from オプションを使ってキャッシュ機能を有効にする場合は次のようになります。

単一ステージビルドでキャッシュを利用する
$ DOCKERHUB_REPOSITORY=<YOUR_NAME>/<REPOSITORY_NAME> $ docker pull ${DOCKERHUB_REPOSITORY} $ docker build \ --tag ${DOCKERHUB_REPOSITORY} \ --cache-from ${DOCKERHUB_REPOSITORY} \ .
マルチステージビルドでキャッシュを利用する
$ DOCKERHUB_REPOSITORY=<YOUR_NAME>/<REPOSITORY_NAME> $ docker pull ${DOCKERHUB_REPOSITORY} $ docker pull ${DOCKERHUB_REPOSITORY}:stage1 : 各ステージのビルド結果を pull する $ docker build \ --tag ${DOCKERHUB_REPOSITORY} \ --cache-from ${DOCKERHUB_REPOSITORY} \ --cache-from ${DOCKERHUB_REPOSITORY}:stage1 \ : 各ステージのビルド結果を指定する .

Dockerfile はステージの数によらず上から下に 1 ステップずつビルドされます。 これは docker buikd コマンドの実行結果の Step X/Y を見ることで確認できます。

キャッシュは各ステップごとに作成されます。 また、キャッシュには ID が採番され、ID が同じであればキャッシュが利用されます。 ID が異なればキャッシュは使われず、同一ステージにおける以降の全てのステップではキャッシュが利用されません。

ID は docker build コマンドの実行結果内の ---> dd025cdfe837 等と表示されます。 もしキャッシュが利用されたときは ---> Using cache と表示されます。

キャッシュ ID はステップのコマンド内容によって次のように作成され、ID が変わる条件は次のとおりです。 ID が変わるとキャッシュアウトされます。

  • FROM コマンドではベースとなるイメージの内容がキャッシュ ID に反映される
    • つまり、イメージが変わるとキャッシュ ID が変わる
  • ADD, COPY コマンドでは COPY する元のファイル内容がキャッシュ ID に反映される
    • つまり、ファイル内容が変わるとキャッシュ ID が変わる
  • LABEL, ENV, ARG コマンドでは設定する値がキャッシュ ID に反映される
    • つまり、設定する値が変わるとキャッシュ ID が変わる
  • RUN, CMD, ENTRYPOINT, EXPOSE, USER コマンドではコマンド内容がキャッシュ ID に反映される
    • つまり、実行するコマンドが変わるとキャッシュ ID が変わる
    • そのため、コマンドを実行する度に結果が変わる(冪等ではない)場合はキャッシュされることを注意する必要があります

※ もし COPY . . を使う場合 Dockerfile が含まれないように .dockerignore を用意することをお勧めします。Dockerfile が COPY 元に含まれるとファイル変更によりキャッシュアウトするためです。

※ キャッシュを pull すると Docker Hub の pull 数が 1 より大きい値になっているようです。ただ、キャッシュが空の時に pull しても数が不定であり、仕組みがよく分かりません。('2020/03/15現在)

BuildKit におけるキャッシュイン・キャッシュアウトの仕組み

docker buildx build ではイメージを pull しなくても --cache-from を指定することでキャッシュが利用できます。 また、マルチステージビルドの場合も結果のイメージに各ステージのキャッシュを埋め込むことが出来ます。

  • CMD, ENTRYPOINT, EXPOSE, ENV, LABEL, USER が変わってもキャッシュに影響しない
  • ARG は値が変わっただけではキャッシュに影響しない
    • 但し、ARG を利用したコマンドの内容が変わることで、それらのコマンドの条件に応じてキャッシュが利用されるかどうかが分かれます

これは docker buildx build コマンド実行結果の [builder][stage-X X/Y] に上記コマンドが表示されないことから確認できます。

ビルダインスタンスを操作する (docker/buildxプラグインで利用可能)

ビルダインスタンスとは独立したビルド環境のことです。

docker/buildx プラグインではビルダインスタンスを操作することができます。

ビルダインスタンスを確認する

ビルドインスタンスを確認する
$ docker buildx ls NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS default * docker default default running linux/amd64, linux/386

ビルダインスタンスを作成する

利用中の docker 設定がdefault の値として適用されます。

default は docker ドライバが使われますが、次のようにいくつか機能が制限されています。

  • --output オプションで使用できるのは local, tarball exporter, image exporter のみである
  • --cache-from オプションで使用できるのは registry type のみである
  • --cache-to オプションで使用できるのは inline type のみである

docker-container ドライバを使うにはビルダインスタンスを作成する必要があります。

ビルダインスタンスを作成する
$ docker buildx create --name docker-container --driver docker-container --use docker-container
# ビルダインスタンスが作成され、使用するように設定されていることを確認する $ docker buildx ls NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS docker-container * docker-container docker-container0 unix:///var/run/docker.sock inactive default docker default default running linux/amd64, linux/386 # docker-container が起動していないことを確認する (docker-container ビルダインスタンスの STATUS が inactive である) $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES # 一度ビルドを行ってみる $ docker buildx build . : <snip> # すると、コンテナが起動して STATUS が running になる $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3622af1006df moby/buildkit:buildx-stable-1 "buildkitd" About a minute ago Up About a minute buildx_buildkit_docker-container0 $ docker buildx ls NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS docker-container * docker-container docker-container0 unix:///var/run/docker.sock running linux/amd64, linux/386 default docker default default running linux/amd64, linux/386 # ちなみに、コンテナが停止した状態だと STATUS は stopped になる $ docker stop buildx_buildkit_docker-container0 buildx_buildkit_docker-container0 $ docker buildx ls NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS docker-container * docker-container docker-container0 unix:///var/run/docker.sock stopped default docker default default running linux/amd64, linux/386

Docker Compose における BuildKit の利用

BuildKit による Docker コンテナイメージビルドのメリットは具体的に次のとおりです。

  • ステージビルドを並列実行できる
  • 機密情報をビルド成果物(キャッシュを含む)に残さない仕組み(Dockerfile 内の RUN --mount コマンド)が使える(参考 external_link)
    • ローカルファイルをビルド時のみに使える
    • リモートファイルをビルド時にSSH接続して取得できる

Docker Compose 1.25.0 にて docker-compose build 時に BuildKit が使用できる方法が追加されました。

Add BuildKit support, use DOCKER_BUILDKIT=1 and COMPOSE_DOCKER_CLI_BUILD=1 [出典] https://github.com/docker/compose/releases/tag/1.25.0 external_link

環境変数を設定することで Docker Compose において BuildKit を使ってビルドすることができます。 ただし、Docker が BuildKit を使えるように設定されていることが前提です。

また、一方で docker buildx bake コマンドにより docker-compose.yml を読み取ってビルドすることが出来ます。

  • 環境変数を設定する方法
    • COMPOSE_DOCKER_CLI_BUILD=1 を設定する
    • 例えば COMPOSE_DOCKER_CLI_BUILD=1 docker-compose build により BuildKit を使ってビルド出来ます
  • docker/buildx プラグインを使う方法
    • buildx bake の対象ファイルとして docker-compose.yml を指定して実行します
    • 例えば docker buildx bake -f docker-compose.yml を実行すると BuildKit を使ってビルド出来ます

もともと Docker Compose は docker/cli は使わず、docker-py external_link を使って dockerd の API にアクセスしますが、環境変数を使って BuildKit を使う仕組みは docker/cli を使います。

これは、執筆当時の '20/03/09 現在では docker-py は BuildKit に対応していなかった(参考 external_link) ため、docker-compose にて BuildKit を使うために docker/cli を使用する方法が実装されたためです。(参考1 external_link, 参考2 external_link, 参考3 external_link)

# docker-compose が docker-py を利用していることの確認 $ docker-compose version docker-compose version 1.25.4, build 8d51620a docker-py version: 4.1.0 CPython version: 3.7.5 OpenSSL version: OpenSSL 1.1.0l 10 Sep 2019

Docker Compose における BuildKit ビルド時に外部キャッシュを利用する

  • 環境変数 COMPOSE_DOCKER_CLI_BUILD=1 を使う場合
    • docker-compose.yml 内の services.<service名>.build.cache_from に外部キャッシュを指定します
    • 例えば以下の docker-compose.yml を記述して docker-compose build を実行するとキャッシュが有効になります
    • なお、docker-compose up --build でもキャッシュを利用してビルドすることが出来ます
  • docker/buildx プラグインを使う方法
    • 事前に外部キャッシュを使ってベースイメージをビルドした後に docker-compose.yml 内のサービスをビルドします
    • 例えば以下の docker-compose.yml の場合、docker buildx build --cache-from を実行してベースイメージをビルドした後に docker buildx bake -f docker-compose.yml を実行します

環境変数を使う場合、docker-compose build を実行した際に、次の warning メッセージが表示されれば BuildKit によるビルドが有効になっています。

WARNING: Native build is an experimental feature and could change at any time

環境変数を使う場合の外部キャッシュ利用方法(docker-compose.yml)
# cache_from は v3.2 以降で利用可能です (https://docs.docker.com/compose/compose-file/) version: '3.2' services: app: build: context: . cache_from: # イメージは適当です。後続で紹介する Dockerfile を使ってビルドした結果を指定しましょう - ryu310/github_action_sandbox
環境変数を使う場合の外部キャッシュ利用方法(Dockerfile)
FROM alpine:3.11.3 LABEL maintainer="Ryu Sato" COPY scripts/entrypoint.sh /root CMD ["/root/entrypoint.sh"]
docker/buildxプラグインを使う場合の外部キャッシュ利用方法(docker-compose.yml)
version: '3' services: app: build: context: .
docker/buildxプラグインを使う場合の外部キャッシュ利用方法(Dockerfile)
FROM alpine:3.11.3 LABEL maintainer="Ryu Sato" COPY scripts/entrypoint.sh /root CMD ["/root/entrypoint.sh"]
環境変数を使う場合の外部キャッシュ利用方法
$ DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker-compose build WARNING: Native build is an experimental feature and could change at any time : <snip> => CACHED [2/2] COPY scripts/entrypoint.sh /root : <snip> Successfully built d1acbb2a8544a28dd4a4d300a6caab3f0c9aabe030a96e6621cbafd9114f1f0a
docker/buildxプラグインを使う場合の外部キャッシュ利用方法
$ docker buildx build --cache-from type=registry,ref=ryu310/github_action_sandbox . : <snip> => CACHED [2/2] COPY scripts/entrypoint.sh /root $ docker buildx bake -f docker-compose.yml : <snip> => CACHED [2/2] COPY scripts/entrypoint.sh /root