イメージのビルド高速化機構 Docker BuildKit、BuildX を理解していなかったので調べてみました

2021-09-08Docker,Kubernetes

はじめに

Dockerfile を作成した後は docker build コマンドを使って Docker イメージをビルドします。

ある日を境に docker build コマンド実行時のコンソールメッセージの表示が様変わりしたのですが、
これは Docker (正確には僕のローカル PC で利用している Docker for Mac)BuildKit と言われるものが導入されたためのようです。

この BuildKit について理解していませんでしたので、調べてみました。

検証環境

$ uname -moi
x86_64 MacBookPro16,1 Darwin

$ bash -version | head -n 1
GNU bash, version 5.1.8(1)-release (x86_64-apple-darwin20.3.0)

$ docker --version
Docker version 20.10.7, build f0df350

BuildKit とは?

BuildKit は、 Docker の 18.06 から試験的に導入され、18.09 以降で正式に導入された機構になります。

ビルド処理時にバックグラウンドで実行されます。

環境変数を使って有効化することができる という記事を拝見しましたが、
僕の PC では特に環境変数を設定していない状態でいつの間にかビルド時の表示が変わっていたので、
もう正式導入しているようです。

従来の docker build コマンドでは、Dockerfile の各命令( FROM とか RUN とか )を順番に実行していきます。

ビルド時の各命令実行結果はすべて レイヤ としてイメージに保存されます。
こうしてミルフィール上のレイヤをもつ Docker イメージ が出来上がります。

BuildKit を有効にすると、
これまで「直列」で順次実行されていた命令をできる限り「並列」に実行します。
結果、Docker イメージのビルドにかかる時間が削減されるというわけです。

ちなみに、Github のリポジトリは以下になります。

BuildKit の特徴

Github ページには、以下のような特徴があると書かれています。

  • 自動的なゴミ掃除(ガベージコレクション)
  • 拡張されたフロントエンドフォーマット(?)
  • 同時実行のための効率的な依存性解決
    • このあたりが Docker イメージの従来の直列性を改善するための秘策なのでしょう
  • 効率的なキャッシュ
    • 従来との違いがわかりませんが、キャッシュ機能が効率化したようです
  • ビルドキャッシュのインポート、エクスポート
  • ネストされたビルドジョブの呼び出し
  • 分散ワーカー
  • 複数のアウトプットフォーマット
  • プラガブルな設計
    • プラグインによる拡張などができるということ?
  • ルート権限無しでの実行
    • 従来の docker build は、ルート権限で実行されていたようですね。

moby プロジェクトが作成したビルドツールキットであり、Docker とは独立しているプロジェクトで開発されたものです。

加えて、キャッシュを効率的に利用でき、
リモートのコンテナレジストリ( ex: DockerHub、GCR、ECR )にキャッシュを保存する機構もあるようです。

BuildKit の有効化

以前は環境変数を設定する必要がありました。

$ export DOCKER_BUILDKIT=1

僕は Docker for Mac を利用していますが、
設定画面から以下のように "buildkit": true と書かれていることが確認できれば、環境変数は不要です。

Dcoker for Mac のメニューから Preferences > Docker Engine と開くと閲覧できます。

こちらの設定を変更した後、Docker for Mac を再起動をお忘れなく。

あまり実感は有りませんが、これだけで Docker イメージのビルド処理が改善されます。

BuildKit のインストール

docker build コマンドの内部が BuildKit を利用するように置き換えられているので
buildkit を明示的に個別に呼び出すケースはほぼないと思いますが、
Mac でインストールしたい場合は以下のコマンドが利用できます。

$ brew install buildkit

更にビルドの性能を向上させる BuildX

BuildKit の恩恵について見てきました。

といっても最新の Docker 環境を利用している限り、すでに有効になっており特に何もしなくてよいというわけです。

ここで更にビルドの性能を向上させる方法として、 BuildX というものを利用する事ができます。

BuildXBuildKit の拡張です。
BuildKit の機能を完全サポートしていて、更に docker コマンドを拡張する CLI のプラグインです。

コマンドは docker build の代わりに docker buildx build を使えばいいだけのようです。

それではビルドを実行してみます。

$ docker buildx build -t x . --no-cache
[+] Building 81.7s (7/7) FINISHED
$ time docker build -t x . --no-cache
[+] Building 120.9s (7/7) FINISHED

あんまり違いがなさそうですが、ごもっともで、 apt-get install しかしていない Dockerfile を使ってのビルドでした。

ちなみに BuildXドキュメントを見てみると、

Set buildx as the default builder

Running the command docker buildx install sets up docker builder command as an alias to docker buildx.
This results in the ability to have docker build use the current buildx builder.
To remove this alias, run docker buildx uninstall.

docker buildx install コマンドを実行すると、 docker build コマンド実行時に BuildX が利用されるように変わってくれるとのこと。

早速試してみました。

$ docker buildx install

何も表示されませんが有効になったということでしょうか?
もしかしたらすでに有効になっていたのかもしれません。

なにはともあれ docker build コマンド実行時に BuildX が使われるようになりましたので、学習を続けながらこのまま利用してみようと思います。

DockerHub の Automated Build ではすでに BuildKit が使われている

DockerHub には Github と連携し、Github へのプッシュを受けて dockker build を実行する、という機構があります。

これを Automated Build とよんでいますが、このなかでは BuildKit が使われているようです。

なぜかというと、ビルド失敗時のログメッセージとして、 以下のようなメッセージが表示される時があるためです。

failed to solve: rpc error: code = Unknown desc = failed to solve with frontend dockerfile.v0: failed to build LLB: executor failed running

LLB というのは BuildKit の中にあるコンポーネントです。
dockerfile.v0 も同様です。

ひとこと

結論としては、だんだん docker build の性能は改善されていて、その恩恵を受けるためにしないといけないことは特にないよ (よしなにやってくれている) 、ということでした。

2021-09-08Docker,Kubernetes