目的
Docker のアーキテクチャ選定をしっかり理解して、ビルドエラー/実行時エラーを回避する。
状況
Docker イメージを作成する際に、CPUアーキテクチャ依存のパッケージをインストールしなければいけないことがある。
「他の人は動いているように見えるのに、自分の環境だとビルド時のパッケージのインストールに失敗する...(あるいはビルドは通っても実行時に謎のエラーになる...)」みたいな時は、パッケージが依存しているアーキテクチャと異なるアーキテクチャでビルドしている可能性がある。
その場合、ビルド時に適切なアーキテクチャを指定する必要がある。
基本的にはベースイメージのアーキテクチャが引き継がれる
まず基本的な考え方として、ビルドしたイメージのアーキテクチャはベースイメージのものと同じになる。
つまり、FROM ~~~
で指定したベースイメージのアーキテクチャが AMD64 であればビルドしたイメージも AMD64 になる。
ビルド時のマシンのアーキテクチャが考慮される
Docker はデフォルトでビルド時のマシンのアーキテクチャに対して最適なイメージを pull してくる。
多くのメジャーなイメージは、マルチプラットフォームに対応している。
例えば ubuntu の公式イメージの「OS/ARCH」欄を見ると複数のプラットフォーム用のイメージがあることが分かる。
さてそれでは、自分の macOS マシンでシンプルに ubuntu の latest を pull すると、一体なんのアーキテクチャに対応したイメージがやってくるのだろうか。
Apple Silicon の macOS のアーキテクチャは ARM64。
(Intel のチップが入っていた時代の macOS は AMD64)
$ docker pull ubuntu:latest
# イメージの情報を確認するコマンド
$ docker image inspect ubuntu:latest --format '{{.Architecture}}'
arm64
arm64 と出た。ホストマシンである macOS と同じアーキテクチャのイメージが自動で選ばれている。
つまり、マルチプラットフォームに対応したベースイメージを使って何の気無しに自分のマシンでビルドすると、自分のマシンのアーキテクチャのイメージがビルドされるということになる。
(当然、マルチプラットフォーム対応イメージだとしても、自分のマシンのアーキテクチャと同じものがなければ話は違う)
Dockerfile の中でインストールしているパッケージの依存アーキテクチャが自分のマシンと同じであれば特に問題はないが、違った場合には、ビルドがコケたり実行できなかったりするだろう。
明示的に指定することで pull
を制御する
これを明示的に指定すると、別のイメージを pull することもできる。
$ docker pull --platform linux/amd64 ubuntu:latest
# イメージの情報を確認するコマンド
$ docker image inspect ubuntu:latest --format '{{.Architecture}}'
amd64
イメージのリストには以下のように表示されており、アーキテクチャ違いのイメージが2個(arm64とamd64)あることが分かる。
$ docker images | grep ubuntu
ubuntu <none> f825f99d3d8a 5 weeks ago 101MB
ubuntu latest 59ab366372d5 5 weeks ago 78.1MB
$ docker image inspect f825f99d3d8a --format '{{.Architecture}}'
arm64
$ docker image inspect ubuntu:latest --format '{{.Architecture}}'
amd64
pull
せずに、build
コマンドで指定しても良い。
# 一旦 ubuntu のイメージをローカルから全て消した上で
# ベースイメージだけを指定した Dockerfile を用意
$ cat Dockerfile
FROM ubuntu:latest
$ docker build --platform=linux/amd64 . -t test
# ベースイメージの pull が自動で走ってビルドが完了
$ docker image inspect test --format '{{.Architecture}}'
amd64
ちゃんと AMD64 のイメージがビルドされた。
ちなみにこの build
時の --platform
の指定はあくまでもベースイメージを pull
するときの指定であって、「ARM64 のベースイメージを AMD64 のイメージに変更する」みたいなアーキテクチャの変更をするわけではない。
例えば、以下のような AMD64 のみ対応のイメージをベースイメージにして、--platform=linux/arm64
(対応していないアーキテクチャ)を指定してビルドしてみる。
$ cat Dockerfile
FROM buildkite/puppeteer:latest
$ docker build --platform=linux/arm64 . -t test
(中略)
1 warning found (use docker --debug to expand):
- InvalidBaseImagePlatform: Base image buildkite/puppeteer:latest was pulled with platform "linux/amd64", expected "linux/arm64" for current build (line 1)
さて、ビルドは成功したが、何やら warning が出ている。
Base image buildkite/puppeteer:latest was pulled with platform "linux/amd64", expected "linux/arm64" for current build (line 1)
「linux/arm64 が指定されたけど、実際には linux/amd64 のイメージが pull されたよ。」とのこと。
arm64 を指定したところで、無いものを引っ張ってくることは出来ない。
上で書いた通りで、--platform
はあくまでも pull
の指定であり、アーキテクチャの変更を行うわけではないということが分かる(そんなことはそもそも出来ない)。
ベースイメージに、必要なアーキテクチャが無い場合
これは必要なアーキテクチャがあるイメージに変えるしかない。
まとめ
- イメージのアーキテクチャはベースイメージのアーキテクチャで決まる。
-
pull
やbuild
の--platform
では、ベースイメージをpull
する際のアーキテクチャを指定できる。- あくまでも
pull
の指定なので、必ずそのアーキテクチャのイメージが作れるわけではない。
- あくまでも