🐳

docker buildx bakeで複数のイメージを並列でビルドする

2021/08/21に公開約5,200字

概要

かつて、Dockerではイメージの並列ビルドを行うことはできませんでした。2017年に導入されたmulti-stage buildでは並列ビルドがサポートされましたが、あくまでも一つのイメージをビルドする過程でステージを並列実行するというものでした。

しかし、buildxの新機能であるbuildx bakeを使うと、一つのコマンドで複数のイメージを同時にビルドすることができます。例えば、以下のDockerfileからmicroservice-amicroservice-bの2つのイメージを出力することができます。

# Microservice AとBをビルドするステージ
FROM rust as builder-stage

COPY ./src /services-src

WORKDIR /services-src
RUN cargo build --release --bins

# Microservice Aに必要なファイルだけを用意するステージ
FROM debian:bullseye-slim as microservice-a-stage

RUN apt-get update && \
    apt-get install -y service-a-dependencies && \
    rm -rf /var/lib/apt/lists/*

COPY --from=builder-stage /services-src/target/release/service-a /service-a

ENTRYPOINT ["/service-a"]

# Microservice Bに必要なファイルだけを用意するステージ
FROM debian:bullseye-slim as microservice-b-stage

RUN apt-get update && \
    apt-get install -y service-b-dependencies && \
    rm -rf /var/lib/apt/lists/*

COPY --from=builder-stage /services-src/target/release/service-b /service-b

ENTRYPOINT ["/service-b"]

この記事では上記のDockerfileを例にとり、buildx bakeコマンドを使って複数のイメージを並列でビルドする方法を紹介します。

ビルド定義ファイル

buildx bakeを使うには、まずビルド定義ファイルを用意する必要があります。冒頭で示したDockerfileをビルドするには、以下のようにdocker-bake.hclを書きます。

target "microservice-a" {
  dockerfile = "Dockerfile" # Dockerfileのファイル名(デフォルト値なので省略可能)
  target = "microservice-a-stage" # ステージ名(紛らわしいが、2行上のtargetとは無関係)
  tags = ["ciffelia/microservice-a"] # イメージにつけるタグ(複数可)
}

target "microservice-b" {
  dockerfile = "Dockerfile"
  target = "microservice-b-stage"
  tags = ["ciffelia/microservice-b"]
}

ビルド定義ファイルでは複数のtargetを定義できます。targetの中では、docker buildを実行するときと同じように、イメージをビルドするオプションを指定します。
従来は、1回のdocker buildで1つのイメージを作成していました。buildx bakeでは、1つのtargetで1つのイメージを作成します。

なお、定義ファイルの記法は以下の3つの中から選ぶことができます。

  • Docker Compose (docker-compose.ymlbuildセクションに書く)
  • HCL (HashiCorp configuration language)
  • JSON

個人的にはdocker-compose.ymlの肥大化を避けたいので、ビルド定義はHCLかJSONに書くのが良いのではないかと思います。JSONを手で書くのは大変なので、この記事ではHCLを採用しています。

buildx bakeコマンド

buildx bakeは、予め用意しておいたビルド定義ファイルに基づいてビルドを実行するコマンドです。以下のコマンドで、先程示したdocker-bake.hclmicroservice-a microservice-b targetを実行できます。

docker buildx bake --file docker-bake.hcl microservice-a microservice-b

このコマンド1つで、ciffelia/microservice-aciffelia/microservice-bの2つのイメージが作成されます。また、3つのステージは並列で実行されます。

便利な機能

ここからは、buildx bakeを使う上で便利な機能をいくつか紹介します。

Target options

targetの中で指定できるオプションは、
args, cache-from, cache-to, context, dockerfile, inherits, labels, no-cache, output, platform, pull, secrets, ssh, tags, target
です。使い方は基本的にdocker buildx buildと同じです。

target "example" {
  dockerfile = "docker/Dockerfile.webapp"
  tags = ["ciffelia/webapp"]
  cache-from = ["type=registry,ref=ciffelia/webapp"] # 事前にレジストリからキャッシュをpullする
  push = true # ビルド完了後、レジストリにpushする
}

Groups

複数のtargetをまとめることができます。

group "default" {
  targets = ["microservice-a", "microservice-b"]
}

target "microservice-a" {
  target = "microservice-a-stage"
  tags = ["ciffelia/microservice-a"]
}

target "microservice-b" {
  target = "microservice-b-stage"
  tags = ["ciffelia/microservice-b"]
}

上記のようにdocker-bake.hclを書いて、以下のコマンドを実行すれば、microservice-amicroservice-bの2つのtargetが実行されます。

docker buildx bake --file docker-bake.hcl default

なお、実行したいtargetやgroupの名前がdefaultの場合は省略することができます。

# 同じ結果が得られる
docker buildx bake --file docker-bake.hcl

継承

inheritsを使うと、既存のtargetをもとに新しいtargetを作成することができます。

target "microservice-a" {
  target = "microservice-a-stage"
  tags = ["ciffelia/microservice-a"]
}

target "microservice-a-multiarch" {
  inherits = ["microservice-a"]
  platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v7"]
}

複数のDockerfile

1つのdocker-bake.hclで複数のDockerfileを使うことができます。当然、並列でビルドされます。

target "frontend" {
  dockerfile = "docker/Dockerfile.frontend"
  tags = ["ciffelia/my-frontend"]
}

target "backend" {
  dockerfile = "docker/Dockerfile.backend"
  tags = ["ciffelia/my-backend"]
}

GitHub Action

buildx bakeを実行するGitHub Actionが、Docker公式でリリースされています。

https://github.com/docker/bake-action

Reference

Discussion

ログインするとコメントできます