Bake
Docker Bake は、Compose がランタイム環境の管理を簡素化するのと同様に、Docker ビルドを効率化するオーケストレーション ツールです。 Bake を使用すると、ビルドステージとデプロイ環境を宣言型ファイルで定義できるため、複雑なビルドの管理が容易になります。 また、BuildKit の並列化および最適化機能を活用して、ビルド時間を短縮します。
Dockerfileはイメージのビルドステップを定義するのに最適ですが、チームは多くの場合、複数のイメージをビルドし、テスト、リンティング、コード生成などのヘルパータスクを実行する必要があります。 従来、これは多数の docker build コマンドと独自のオプションや引数をジャグリングすることを意味し、退屈でエラーが発生しやすいプロセスでした。
これまで BuildKit を用いてイメージをビルドする場合、下記のような複雑な引数をたくさん渡す必要がありました。
$ docker buildx build \
--push \
--cache-from="type=registry,ref=foo/myapp:latest" \
--cache-to="type=registry,ref=foo/myapp:latest,mode=max,image-manifest=true,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true,push=true" \
--tag foo/myapp:v1.0.0 \
--tag foo/myapp:latest \
--file ./main.Dockerfile \
.
イメージをビルドするためにこのような引数を、 monorepo で様々なターゲットに対して行うのは非効率です。 CI などの bash スクリプトの中で管理することになると思いますが、煩雑ですね。
また、ローカル開発環境では docker compose で管理しているのに、本番環境はファイル管理されていないという一貫性のなさも問題です。
これらを解決するために、 docker buildx bake
コマンドが生まれました。
$ docker buildx bake
これだけで自動的に bake に必要なファイルを読み込み、ビルドを行います。
このコマンドは Docker Desktop では 4.38.0 から(執筆段階での最新は 4.42.0)、 Docker Engine では 23.0 から(DOCKER_BUILDKIT=1 環境変数が必要)利用可能となっています。 BuildKit のバージョンは最新がおススメです。
Bake に必要な Bakefile は、 HCL(Terraform の記法), JSON, YAML を選択出来ますが、拡張性の点で HCL を選択するのが良いと思います。
最小構成だとこんな感じです。
$ docker build -t myapp:latest .
これが
# docker-bake.hcl
target "default" {
context = "."
tags = [ "myapp:latest" ]
}
こうなります。もう少し複雑な例でいくと、
# docker-bake.hcl
group "default" {
targets = ["frontend", "backend"]
}
target "frontend" {
context = "./frontend"
dockerfile = "frontend.Dockerfile"
args = {
NODE_VERSION = "22"
}
tags = ["myapp/frontend:latest"]
}
target "backend" {
context = "./backend"
dockerfile = "backend.Dockerfile"
args = {
GO_VERSION = "1.24"
}
tags = ["myapp/backend:latest"]
}
こんな感じに書くことで、フロントエンドとバックエンドのビルドを一発で行えるようになります。
cache-from
とかの key=value,key=value
のような形式のオプションを { key = value, key = value }
と書けるようになるのでだいぶ閲覧性が上がります。
Docker DX vscode extension を入れることで、この Bake ファイルのサポートをしてくれるようになるので便利です(まだ Beta なので色々エラーは出ます)。
compose.yaml では環境変数を .env
ファイルに書き込んでおくことで、自動的にそれを展開してくれます(hcl では .env
ファイルは読まれません)。
# .env
TAG=v1.1.0
# compose.yaml
services:
webapp:
image: docker.io/username/webapp:${TAG:-v1.0.0}
build:
dockerfile: Dockerfile
$ docker buildx bake --print
{
"group": {
"default": {
"targets": ["webapp"]
}
},
"target": {
"webapp": {
"context": ".",
"dockerfile": "Dockerfile",
"tags": ["docker.io/username/webapp:v1.1.0"]
}
}
}
このように、 docker compose の YAML とも互換性があるので、既存の compose.yaml からビルド設定を追加していくことも可能です。
この Bake に関しては公式ドキュメントに全部載っています。
Builder
Dockerfile をパースし実際にビルドを実行するのは BuildKit です。デフォルトでは docker
というドライバーが選択されているのですが、このドライバーだと使えないオプションが結構あります。例えばマルチアーキテクチャビルドをするために QEMU でエミュレーションしたり、キャッシュを細かく指示することが出来ません。
そこで、 Bake する際は新しいドライバーを作成することが推奨されます。
$ docker buildx create --name=container --driver=docker-container --use --bootstrap
とすることで、デフォルトのドライバーを docker-container
にすることが出来ます。これは Docker Engine についてきた BuildKit でビルドするのではなく、新しくコンテナを立ち上げてその中でイメージをビルドするドライバです。
--use
を指定しておくことで、明示しなくても docker buildx
を使った時に優先して利用してくれます。
zstd 圧縮による起動高速化
例えば Amazon ECR では、 zstd という圧縮アルゴリズムをイメージ転送時に使うことで、イメージサイズを小さくし解凍効率を上げコンテナの起動時間を短縮することが出来るので、その設定を追加してみます。
group "default" {
output = [
{
type = "image",
name = "${IMAGE_URI}:${IMAGE_TAG}",
oci-mediatypes = true,
compression = zstd,
compression-level = 3,
force-compression = true,
push = true
}
]
}
この設定を追加することで、 Fargate でのコンテナ起動の高速化が期待出来ます。記事に書いてある通り、 AWS の試験では compression-level = 3 が最適だったそうです。
※ zstd 圧縮はまだ OCI の標準に入っていないため、全てのランタイムでサポートされているわけではないので注意が必要です(例えば、 docker では containerd ランタイムを使わないと動作しません)
BuildKit のリモートキャッシュ
BuildKit のビルドはレイヤーごとにキャッシュを効かせることで、同一ハッシュのレイヤーをキャッシュから読み込んでビルド時間を高速化することが可能です。
Amazon ECR ではレジストリでのリモートキャッシュをサポートしているので、その設定を追加してみます。
group "default" {
cache-from = [
{
type = "registry",
ref = "${REPOSITORY_URL}:cache"
}
]
cache-to = [
{
type = "registry",
ref = "${REPOSITORY_URL}:cache",
mode = "max",
image-manifest = true,
oci-mediatypes = true
}
]
}
この設定を追加することで、 2 回目以降のイメージのビルド時間を大幅に短縮することが可能です。
どちらも、 oci-mediatypes = true
を設定しています。これは、デフォルトの Docker Image v2 Schema 2 ではなく OCI v1 イメージ標準 に基づいてビルドすることを指します。
もちろん先ほどの zstd 圧縮の設定と一緒に使うことも可能です。
GitHub Actions で使う場合
公式で docker/bake-action
が用意されているので、それを使うだけです。簡単ですね。