Docker Buildx

読む時間の目安: 6 分

概要

Docker Buildx は Docker コマンドを拡張する CLI プラグインであり、Moby BuildKit ビルダーツールキットにより提供される機能に完全対応するものです。 Docker ビルドと同様のユーザー操作を提供し、さらにスコープ化されたビルダーインスタンス、複数ノードへの同時ビルドなど、数多くの新機能を提供します。

インストール

Docker Buildx は Docker Desktop に含まれます。 また DEB または RPM パッケージ により Linux 上にインストールする Docker パッケージに含まれます。

また最新のbuildxバイナリを GitHub 上の Docker buildx リリースページからダウンロードすることもできます。 ダウンロードしたら、~/.docker/cli-pluginsフォルダーにコピーして、名前をdocker-buildxとします。 そして以下のようにして実行権限を与えます。

$ chmod a+x ~/.docker/cli-plugins/docker-buildx

以下に示すのは、docker/buildx-bin イメージを使って、DOckerfile 内において buildx を利用する例です。

FROM docker
COPY --from=docker/buildx-bin /buildx /usr/libexec/docker/cli-plugins/docker-buildx
RUN docker buildx version

デフォルトビルダーとして buildx を設定

コマンド docker buildx install を実行すれば builder コマンドがdocker buildxへのエイリアスとして設定されます。 これによって docker build が現時点での buildx builder を利用するようになります。

このエイリアスを削除するには docker buildx uninstall を実行します。

buildx を使ったビルド

新たなビルドを行うにはdocker buildx buildを実行します。

$ docker buildx build .
[+] Building 8.4s (23/32)
 => ...

Buildx は Buildx エンジンを使ってビルドを行います。 ビルドを行うにあたって、環境変数DOCKER_BUILDKIT=1を設定する必要はありません。

docker buildx buildコマンドはdocker buildによって利用できる機能はすべて対応しています。 つまり出力設定、インラインビルドキャッシング、ターゲットプラットフォーム指定といったシ機能にも対応します。 さらに Buildx では、いつものdocker buildでは実現できない新機能として、マニフェスト一覧の生成、分散キャッシング、ビルド結果の OCI イメージ tarball への出力も実現します。

Buildx の実行においては、ドライバーの仕様に基づいて提供されるさまざまな設定を行うことができます。 現時点にて Docker がサポートするドライバーは以下のとおりです。 「docker」ドライバーは BuildKit ライブラリを利用しており、Docker デーモンバイナリにバンドルされます。 「docker-container」ドライバーは Docker コンテナー内部にて BuildKit を自動的に実行します。

Buildx の利用するドライバーが違っていても、ユーザー操作は非常によく似ています。 ただし現在のところ「docker」ドライバーが対応していない機能もあります。 これは Docker デーモンにバンドルされる BuildKit ライブラリが、さまざまなストレージコンポーネントを利用するためです。 「docker」ドライバーによりビルドされたイメージは、デフォルトではすべて自動的に「docker images」の一覧に加えられます。 一方で別のドライバーを用いる場合には、イメージの出力方法を--outputにより選択することが必要です。

ビルダーインスタンスを使った操作

Buildx はdockerドライバーが対応していれば、デフォルトでこのドライバーを利用します。 ユーザー操作は、ネイティブなdocker buildと非常によく似ています。 アプリケーションをビルドするには、ローカルで稼動する共有デーモンを利用する必要があります。

Buildx allows you to create new instances of isolated builders. You can use this to get a scoped environment for your CI builds that does not change the state of the shared daemon, or for isolating builds for different projects. You can create a new instance for a set of remote nodes, forming a build farm, and quickly switch between them.

新たなインスタンスを生成するには docker buildx create コマンドを利用します。 このコマンドを実行すると、現時点の設定に基づいて、単一ノードを持つ新たなビルダーインスタンスが生成されます。

新たなビルダーの生成時にリモートノードを利用するには、DOCKER_HOSTを設定するか、あるいはリモートコンテキスト名を指定します。 新たなインスタンスが生成された後は、docker buildx inspectdocker buildx stopdocker buildx rm コマンドを利用していき、そのライフサイクルを管理していきます。 利用可能なビルダーを一覧表示するには docker buildx ls を利用します。 新しいビルダーを生成したら、そこに新たなノードを追加していくことができます。

To switch between different builders, use docker buildx use <name>. After running this command, the build commands will automatically use this builder.

Docker also features a docker context command that you can use to provide names for remote Docker API endpoints. Buildx integrates with docker context to ensure all the contexts automatically get a default builder instance. You can also set the context name as the target when you create a new builder instance or when you add a node to it.

マルチプラットフォームのイメージビルド

BuildKit はマルチプラットフォームのビルドを得意とするものとして設計されています。 対象となるプラットフォームは、ユーザーがビルド時に実行することになるアーキテクチャーやオペレーティングシステムに限りません。

ビルド実行する際には--platformフラグを用います。 このフラグにターゲットプラットフォームを指定しビルド出力します。 (たとえばlinux/amd64linux/arm64、darwin/amd64`)

When the current builder instance is backed by the docker-container driver, you can specify multiple platforms together. In this case, it builds a manifest list which contains images for all specified architectures. When you use this image in docker run or docker service, Docker picks the correct image based on the node’s platform.

You can build multi-platform images using three different strategies that are supported by Buildx and Dockerfiles:

  1. カーネルにおいて QEMU エミュレーションサポートを利用します。
  2. 同一のビルダーインスタンスを利用して、複数のネイティブなノードをビルドします。
  3. Dockerfile 内の 1 つのステージから、さまざまなアーキテクチャーに対するクロスコンパイルを行います。

QEMU is the easiest way to get started if your node already supports it (for example. if you are using Docker Desktop). It requires no changes to your Dockerfile and BuildKit automatically detects the secondary architectures that are available. When BuildKit needs to run a binary for a different architecture, it automatically loads it through a binary registered in the binfmt_misc handler.

For QEMU binaries registered with binfmt_misc on the host OS to work transparently inside containers they must be registered with the fix_binary flag. This requires a kernel >= 4.8 and binfmt-support >= 2.1.7. You can check for proper registration by checking if F is among the flags in /proc/sys/fs/binfmt_misc/qemu-*. While Docker Desktop comes preconfigured with binfmt_misc support for additional platforms, for other installations it likely needs to be installed using tonistiigi/binfmt image.

$ docker run --privileged --rm tonistiigi/binfmt --install all

Using multiple native nodes provide better support for more complicated cases that are not handled by QEMU and generally have better performance. You can add additional nodes to the builder instance using the --append flag.

Assuming contexts node-amd64 and node-arm64 exist in docker context ls;

$ docker buildx create --use --name mybuild node-amd64
mybuild
$ docker buildx create --append --name mybuild node-arm64
$ docker buildx build --platform linux/amd64,linux/arm64 .

Finally, depending on your project, the language that you use may have good support for cross-compilation. In that case, multi-stage builds in Dockerfiles can be effectively used to build binaries for the platform specified with --platform using the native architecture of the build node. A list of build arguments like BUILDPLATFORM and TARGETPLATFORM is available automatically inside your Dockerfile and can be leveraged by the processes running as part of your build.

# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" > /log
FROM alpine
COPY --from=build /log /log

High-level build options

Buildx also aims to provide support for high-level build concepts that go beyond invoking a single build command.

BuildKit efficiently handles multiple concurrent build requests and de-duplicating work. The build commands can be combined with general-purpose command runners (for example, make). However, these tools generally invoke builds in sequence and therefore cannot leverage the full potential of BuildKit parallelization, or combine BuildKit’s output for the user. For this use case, we have added a command called docker buildx bake.

The bake command supports building images from compose files, similar to docker-compose build, but allowing all the services to be built concurrently as part of a single request.

There is also support for custom build rules from HCL/JSON files allowing better code reuse and different target groups. The design of bake is in very early stages and we are looking for feedback from users. Let us know your feedback by creating an issue in the Docker buildx GitHub repository.

Docker, buildx, multi-arch