Docker は マルチ CPU アーキテクチャ をサポートしているので、docker image pull
すると、イメージ側が対応していれば、同じイメージの同じタグを指定しても、環境(OS や CPU アーキテクチャ)に応じて適切なイメージが勝手に取得されます。
で、今回、マルチ CPU アーキテクチャに対応したイメージを、実際に x64
な Windows 環境だけで意外とサクッとビルドできたので、そのお話。
マルチ CPU アーキテクチャ
例えば alpine:latest
を pull
して inspect
すると、手元の Windows 環境(x64
)では次のように amd64
が降ってくるのに対して、
> docker pull alpine:latest
> docker inspect alpine:latest
[
{
"Id": "sha256:f70734b6a266dcb5f44c383274821207885b549b75c8e119404917a61335981a",
"RepoTags": [
"alpine:latest"
],
"RepoDigests": [
"alpine@sha256:9a839e63dad54c3a6d1834e29692c8492d93f90c59c978c1ed79109ea4fb9a54"
],
...
"Architecture": "amd64",
"Os": "linux",
"Size": 5612304,
"VirtualSize": 5612304,
...
}
]
同じことを Raspberry Pi にインストールした Ubuntu(arm64
)で実行すると、CPU が ARM アーキテクチャなので正しく arm64
が降ってきます。
$ docker pull alpine:latest
$ docker inspect alpine:latest
[
{
"Id": "sha256:c20d2a9ab6869161e3ea6d8cb52d00be9adac2cc733d3fbc3955b9268bfd7fc5",
"RepoTags": [
"alpine:latest"
],
"RepoDigests": [
"alpine@sha256:9a839e63dad54c3a6d1834e29692c8492d93f90c59c978c1ed79109ea4fb9a54"
],
...
"Architecture": "arm64",
"Os": "linux",
"Size": 5357453,
"VirtualSize": 5357453,
...
}
]
Docker Hub で見ると、ひとつのタグに対してアーキテクチャごとに別々のイメージが紐づけられている様子がわかります。

今回は、つまり、これを自分で作りたいね、ということです。
困ったこと
Docker Hub にあるイメージのうち、公式に展開されているものは、もともとこのような仕組みで(または別のタグ、別のイメージとして)複数の CPU アーキテクチャに対応してくれているものが多いのですが、野良イメージだとそうでない場合があります。
で、今回、実際に amd64
環境で便利に使っていたコンテナを arm64
で動かそうとしたら動かせなかった…… という事があり、そんなわけで自前でビルドすることにしました。
そのイメージの Dockerfile
は公開されていたので、Raspberry Pi 上で docker build .
すれば使えはするのですが、ビルドのためだけにいちいちそのアーキテクチャのプラットフォームを用意するのも長期的にみると相当イケてないでしょうということで、21 世紀だしエミュレートしてどうにかできるでしょ、みたいな。
方法
さっきのリンク先 に Buildx
というのが書いてあり、それでできるみたいです。現時点では Experimental な機能 なので、覚悟して使いましょう。
Docker Desktop for Windows を使う場合、設定画面の Command Line
から、Enable experimental features
を有効にする必要があります。

これで docker buildx
コマンドが使えるようになるので、あとは さっきのリンク先 の手順通りです。
最初はデフォルトのビルダ default
が指定されていますが、これだとマルチ CPU の機能は対応しておらず、multiple platforms feature is currently not supported for docker driver
と怒られます。
> docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
default * docker
default default running linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
> docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t kurokobo/demo:latest .
multiple platforms feature is currently not supported for docker driver. Please switch to a different driver (eg. "docker buildx create --use")
ので、BuildKit ベースの新しいビルダを作って、それを使うように設定します。ls
したときの *
がアクティブなやつです。
> docker buildx create --name multipfbuilder --use
multipfbuilder
> docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
multipfbuilder * docker-container
multipfbuilder0 npipe:////./pipe/docker_engine inactive
default docker
default default running linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
あとは Dockerfile
のあるディレクトリを指定してビルドするだけです。--push
を指定して、できたイメージは直接リポジトリにつっこみます。初回実行時は BuildKit のコンテナイメージの取得が最初に入ります。
> docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t kurokobo/demo:latest --push .
[+] Building 59.5s (13/13) FINISHED
=> [internal] booting buildkit 16.1s
=> => pulling image moby/buildkit:buildx-stable-1 14.5s
=> => creating container buildx_buildkit_multipfbuilder0 1.6s
...
=> [linux/amd64 internal] load metadata for docker.io/library/alpine:3.9 13.1s
=> [linux/arm/v7 internal] load metadata for docker.io/library/alpine:3.9 10.2s
=> [linux/arm64 internal] load metadata for docker.io/library/alpine:3.9 13.1s
=> [linux/amd64 1/2] FROM docker.io/library/alpine:3.9@sha256:414e0518bb9228d35e4cd5165567fb91d26c6a214e9c95899e 9.8s
...
=> [linux/arm64 1/2] FROM docker.io/library/alpine:3.9@sha256:414e0518bb9228d35e4cd5165567fb91d26c6a214e9c95899 11.8s
...
=> [linux/arm/v7 1/2] FROM docker.io/library/alpine:3.9@sha256:414e0518bb9228d35e4cd5165567fb91d26c6a214e9c95899 9.5s
...
=> exporting to image 14.4s
=> => exporting layers 0.4s
=> => exporting manifest sha256:2ce6c278a94742c9510b15369b72bf62681741e52074c0b11c4c1da8706b1417 0.0s
=> => exporting config sha256:9913143b883435a3a206777147d7c9aaac67a50e4538b79283eb1dca6a3ef633 0.0s
=> => exporting manifest sha256:5935a9dde92945f9e4f601cc548918de5076856f7bc06b2137bf0767c5d19978 0.0s
=> => exporting config sha256:93d0264837f8f039cd002e842511276aaf9ff3355dea4f7171ce6a97b4c0264f 0.0s
=> => exporting manifest sha256:e023fb745782f5d2eeda3a8dd80fc93ca0f7051a78ecd03a062d8595473ed7b1 0.0s
=> => exporting config sha256:f5f2bad0db136c6f06bf2d4244814660cf229e66bf9dd18eeb47e958c6033cd6 0.0s
=> => exporting manifest list sha256:9a058a80760a142ff335305a438dfbb0eb640d910a01e9ee8a319ec1faa00a85 0.0s
=> => pushing layers 6.8s
=> => pushing manifest for docker.io/kurokobo/demo:latest 7.0s
--push
を指定しないと、docker images
にも表示されませんでした。BuildKit 内に キャッシュができるだけのようです。
ビルドしてそのまま使えるようにするには --load
を指定すればよさそう…… なのですが、現状、--platform
に複数の引数を指定している場合は --load
はコケる みたいです。実際、コケました。
BuildKit 自体の機能を正しく使ってキャッシュをどうのこうのとかきちんと何かすればもうすこしいろいろできるのかもですが、まだよくわからんです。
というわけでできました。

Windows と Raspberry Pi で実際に pull
すると、別のイメージが降ってくるようになりました。かんぺきです。
なお、できたあとも、手元の Docker ホストには、BuildKit のコンテナ(とそれ用のイメージ)が起動したままで残るようです。使わなくなったら殺そう。
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
moby/buildkit buildx-stable-1 f2a88cb62c92 2 weeks ago 82.8MB
> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9b06292f089c moby/buildkit:buildx-stable-1 "buildkitd" 2 minutes ago Up 2 minutes buildx_buildkit_multipfbuilder0
応用
GitHub Actions などと組み合わせれば、Dockerfile
をコミットしたらこれでビルドして Docker Hub へ…… などもできますね。