docker-buildxとmulti-platform build周りについてまとめ
最近docker buildxを使ったmulti-platform build周りについての知見がある程度溜まってきたので必要そうな情報をまとめておく。
buildx自体が実際に使うとハマりどころが多いので、すんなりと納得できるような文章がかけてないとは思うけど、実際に触る人がハマったり疑問に思ったりする内容の穴埋めはある程度できてるとは思ってる。
ちなみにこの記事を書いてる時点のdocker-buildxの最新バージョンがv0.9.1なので、貼ってあるbuildxのリンクについては基本このバージョンのものになる。
docker-buildxってなに?
リポジトリを見ると
docker buildのようなUI
コンテナードライバーによる完全な BuildKit 機能
複数のビルダーインスタンスのサポート
クロスプラットフォーム イメージのマルチノードビルド
Compose ビルドのサポート
高レベルのビルド構造 (bake)
コンテナー内ドライバーのサポート (Docker と Kubernetes の両方)
と書かれていて「?」ってなってしまうけど、一般的な利用用途としてはコンテナイメージを作成する際にamd64アーキテクチャ向けのイメージだけじゃなくて、arm向けのイメージとかも作りたいときに使うものだと思えばOKだと理解してる。
詳しく知りたい人は下記のリポジトリのドキュメントを読むと良さそう。
インストール方法
インストール方法については、このリンク先に書いてある通りDocker Desktopを使っていれば始めから入っているらしい。
自分の場合はLinux環境で動かすことが多かったので $HOME/.docker/cli-plugins
に https://github.com/docker/buildx/releases/latest にあるバイナリを docker-buildx
として配置すればOK。
設置すれば docker buildx
サブコマンドが使えるようになる。
なのでLinux環境に手動インストールするには下記のようなコマンドを実行すればインストール完了
$ mkdir -p $(HOME)/.docker/cli-plugins \
&& curl -o $(HOME)/.docker/cli-plugins/docker-buildx \
https://github.com/docker/buildx/releases/download/v0.9.1/buildx-v0.9.1.linux-amd64 \
&& chmod +x $(HOME)/.docker/cli-plugins/docker-buildx
また、buildxにはイメージビルドを行う際に複数のドライバが選択できるようになっている。
しかし、デフォルトだと docker
ドライバが選択されているが、これではmulti-platform buildを行うことができないため
$ docker buildx create --use
コマンドを実行して docker-container
ドライバを利用するためのインスタンスを作成する必要がある。
(普通にmulti-platformなコンテナイメージの作成を行いたいだけであれば多分 kubernetes
や remote
のドライバを使うことは無い気がしてる)
QEMU
buildxを使って例えばamd64環境でarm向けのイメージを作成する場合は、buildx本体以外にもQEMUというエミュレータをインストールしておく必要があるらしい。
QEMUについて詳しく知りたい人は下記を読むと良さそうだけど、多分buildxについては下記の記事にある user mode emulation
を使ってarmなどのCPUアーキテクチャをエミュレーションを行うために利用しているっぽい。
QEMUのインストール方法は下記のコマンドでできる。
$ docker run --privileged --rm tonistiigi/binfmt --install all
一部環境のエミュレータだけで十分な場合は下記のように指定できる。
$ docker run --privileged --rm tonistiigi/binfmt --install linux/amd64,linux/arm64/v8
上記ツールは下記にリポジトリがあるので興味がある人は中を見てみると良さそう。
インストール方法まとめ
Docker Desktopを利用している場合
Docker Desktopを利用している場合は特に設定は不要なので、 docker-container
ドライバ用のインスタンスを作成するだけでOK。
$ docker buildx create --use
手動インストールする場合
Dockerをすでにインストールしている前提で下記コマンドを実行する。
(インストールするdocker-buildxバイナリは自分の環境向けのものに書き換えて実行する)
$ mkdir -p $(HOME)/.docker/cli-plugins \
&& curl -o $(HOME)/.docker/cli-plugins/docker-buildx \
https://github.com/docker/buildx/releases/download/v0.9.1/buildx-v0.9.1.linux-amd64 \
&& chmod +x $(HOME)/.docker/cli-plugins/docker-buildx
$ docker buildx create --use
$ docker run --privileged --rm tonistiigi/binfmt --install all
multi-platform向けのDockerfileの書き方
構築するイメージによっては特に意識することは無い場合もあるが、例えばGoのようにバイナリをビルドして、それを成果物のコンテナにインストールしたい場合だと下記のような書き方をすることになる。
FROM golang:1.18-buster AS build-env
ARG TARGETARCH
COPY . /workdir
WORKDIR /workdir
RUN GOARCH=${TARGETARCH} go build -o output .
FROM ubuntu:18.04
COPY /workdir/output /output
ENTRYPOINT ["/output"]
上記の例だと build-env
にイメージで TARGETARCH
アーキテクチャ向けのバイナリをbuildして、それを ubuntu:18.04
のイメージにインストールしたコンテナをビルドすることになる。
上記Dockerfileに対して下記のコマンドで指定したmulti-platform毎のイメージをbuild~pushできる。
$ docker buildx build --no-cache --push \
--platform linux/amd64,linux/arm64/v8 \
-t some-image:latest \
.
また TARGETARCH
や BUILDPLATFORM
などはbuildxが自動的に設定する変数になるが、これらは下記のリンクで一覧がまとめられている。
また下記のドキュメントも参考になると思う。
docker-buildxを使ったビルド
上記の docker buildx build
の例でも --push
オプションを付けて実行しているが、オプション名の通り上記コマンドを実行するとイメージのpushも同時に実行されることになる。
ただちょっとbuildを試したいだけのときに単純に --push
オプションを外しても、今度はbuild自体が実行されないという問題があり、こんな感じで docker buildx build
コマンドは通常の docker build
に比べてクセが少し強いので docker buildx build
を使ったbuildのためのコマンドのパターンをいくつか紹介する
ローカル向けビルド
1つ目は下記のように --local
オプションを付けて実行するパターン。
$ docker buildx build --no-cache --local -t some-image:latest .
これを実行すると実行結果のイメージがローカルのレジストリに保存されるので docker run some-image:latest
のように実行することができる。
ただし --local
オプションを付けて実行した場合は --platform linux/amd64,linux/arm64/v8
のようなオプションを付けたmulti-platform buildを行うことができない。
そのため --local
オプションを付けて実行するのはローカルでの開発環境向けで実行するケースが多くなると思われる。
手元でbuild ~ イメージを実行して動作確認を行う、といったケースではこの方法でイメージをbuildするのが良さそうに思える。
build ~ push
buildしたイメージを実際にpushまで行う場合では
$ docker buildx build --no-cache \
--push \
--platform linux/amd64,linux/arm64/v8 \
-t some-image:latest \
.
のように --push
オプションを付けて実行すればmulti-platformなイメージのbuild~pushができる。
例えばDokcer Hubにpushした場合は下記画像のように OS/ARCH
のところに複数のイメージが登録されていることが確認できる。
multi-platform buildしたイメージをpushしたくない場合
例えばmulti-platform buildがちゃんとできるのかを確認したい場合などは
$ docker buildx build --no-cache \
-o type=tar,dest=some-image.tar \
--platform linux/amd64,linux/arm64/v8 \
-t some-image:latest \
.
などのようにtarで成果物を出力するように設定しても実行が可能なようなので、ローカルで動作確認を行いたい場合はこんな感じで対応している。
このあたりのtipsとかいい感じの方法を知ってる人がいれば是非教えてもらいたい。
ちなみに docker buildx build
のオプションなどをWebで確認したい場合は下記で見ることができる。
Dockerfile & buildまとめ
-
docker buildx build
multi-platform向けのイメージ操作を行う関係かbuild ~ pushがまとめて行われる仕組みになっている -
--platform linux/amd64,linux/arm64/v8
のように--platform
をつけることでmulti-platform build対象のプラットフォームを設定可能 - ローカル向けにビルドする場合は
--local
オプションを使えば良いがmulti-platform buildができないことに注意 - multi-platform buildを行いたい場合は
--push
オプションを付けてpushまで行うか-o type=tar,dest=some-image.tar
などをつけてbuildxがmulti-platform buildを行ってくれる条件を整えてあげる必要あり - buildxを利用した際にDockerfileで利用できる変数がここのリンクにあるので生成するバイナリなども対象のplatform向けに生成するようにDockerfileを書く必要あり
イメージタグを設定する場合
multi-platformなイメージにタグを設定する場合 docker tag
コマンドだと、実行している環境向け(amd64とかarmとか)のイメージにしかタグを設定することができないという問題があり、別アプローチを取る必要があるためこれを紹介する。
docker buildx build
でタグを設定する
docker buildx build
では下記のように -t
オプションで複数のタグを設定することができるので、複数のタグを設定する必要がある場合はこのように設定するのがおそらく一般的なのだと理解している。
$ docker buildx build --no-cache --push \
--platform linux/amd64,linux/arm64/v8 \
-t some-image:latest \
-t some-image:v1.0.0 \
.
docker buildx imagetools create
でタグを設定する
docker buildx imagetools create
というmulti-platformイメージ向けにタグを設定するサブコマンドがあるのでこれを利用するという方法もある。
この方法だと一度作ってからpushしたイメージに後からタグを追加できるので、後から追加する必要がある場合はこのコマンドを利用することになる。
(というよりもこの他の方法を知らないので、知ってる人いたら教えて)
$ docker buildx imagetools create --tag some-image:latest some-image:v1.0.0
のように
-
--tag
オプションにベースとなるイメージタグを設定 - 引数に新規に設定したいイメージタグを設定
することで新たにタグを設定 ~ pushまでを実行してくれる。
Github Actionsでmulti-platform buildイメージをpushする
Github Actionsで実際にイメージをpushする際はGithub Actionsの各Actionを組み合わせるパターンだとこんな感じに書くことができる
name: ci
on:
push:
branches:
- 'main'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
with:
push: true
tags: user/app:latest
上記で紹介している各Actionのリポジトリはそれぞれ下記にある
- docker/setup-qemu-action@v2: https://github.com/docker/setup-qemu-action
- docker/setup-buildx-action@v2: https://github.com/docker/setup-buildx-action
- docker/login-action@v2: https://github.com/docker/login-action
- docker/build-push-action@v2: https://github.com/docker/build-push-action
各アクションでそれぞれどんなことを行っているかについては下記のようになる
docker/setup-qemu-action
他のActionも含めてサクッと眺めるくらいなら簡単なので軽く見ていると良いと思うけど、(オプションで若干変わるけど)要は下記のコマンドを実行しているだけ。
$ docker run --privileged --rm tonistiigi/binfmt --install all
docker/setup-buildx-action
これもだいたい下記のコマンド実行しているのとあんまり変わらない
$ mkdir -p $(HOME)/.docker/cli-plugins \
&& curl -o $(HOME)/.docker/cli-plugins/docker-buildx \
https://github.com/docker/buildx/releases/download/v0.9.1/buildx-v0.9.1.linux-amd64 \
&& chmod +x $(HOME)/.docker/cli-plugins/docker-buildx
$ docker buildx create --use
例えばインストール処理はこんな感じ
docker/login-action
これに至ってはほぼ docker login
してるだけ(ECRサポートとかもしてるみたいだけど)
docker/build-push-action
これもほぼ docker buildx build
してるだけ
Github Actionsまとめ
ということで各Actionはそれぞれかなり単純なコマンドを実行してるだけなので、Actions使っても良いし柔軟に設定したりローカルでも実行できるようにしたい場合はMakefileなどでタスクを書いてそれを実行するのでも良いと思う。
このあたりはどんな感じに設定したいかってことと、あとは趣味の話しだったりするので、便利な範囲でそれぞれ好きに使えば良さげ。
buildx周りの情報の調べ方
- ブログ記事を探す: 公式情報がまとまってなかったり少なかったりするので有用
- 公式ドキュメントを見る: buildxについてはあまり触れられてないので公式ドキュメントだけだと厳しい
- buildxのリポジトリ: buildxについてはこのリポジトリが一番情報が多い気がするので基本ここを見るのが良いと思う(一部は公式ドキュメントにしか書いてないこともある)
- BuildKitのリポジトリ: buildxはBuildKitを利用しているのでBuildKit側の情報が役に立ったりもした
まとめ
multi-platformなイメージの作成・管理のために普通にdocker使うのと比べると色々と制約が増えているので慣れるまでには少し時間がかかる印象。
(buildx周りのドキュメントもあまり充実していないし、使ってる人も少ないので調べるのもいちいち時間かかる)
なのでmulti-platformなイメージ作成の要件とかが無いのであれば普通に今まで通り docker build
を使った方が楽だと思った。
あと全然関係ないけど、buildxとかBuildKitのリポジトリを見てて(当たり前っちゃ当たり前なのかもだけど)Akihiro Sudaさんのcommitがちょいちょい出てきて「さすがだー」って気持ちになった。
そしてBuildKitは結構内部実装に関する解説資料があるんだけど、buildxはそこらへんが全然無いのでどっかに資料ないかなーと思ってる。
オマケ
調べる過程のメモとか消化しきれてないことのメモです。
BuildKit内部アーキテクチャ
上記スライドにBuildKitのアーキテクチャが解説されてたのでちょっと興味持った。
今度暇なときがあれば見てみたい。
また下記ページでライフサイクルが図で紹介されている。
LLB
アーキテクチャのスライドでも出てきてたけどBuildKitはDockerfileのデータなどをLLBという中間データフォーマット?に変換して処理をするような仕組みらしいので、これも暇があればこのあたりの仕組みを調べてみたい。
でDockerfileがLLBに変換する処理について解説されているっぽい。
tonistiigi/xx
これがBuildKitの内部で使われてるってことでいいんだろうかーとかそもそもこれって何なのかとかが調べきれてなくて消化不良な感じ。
その他参考リンク
Discussion