目次
多平台イメージ(amd64 と arm64)のビルド:docker buildx 入門
問題:exec format error
Apple Silicon(M シリーズチップ)の Mac でイメージをビルドし、一般的なクラウドホストにプッシュして実行すると、コンテナは起動直後に終了します。
exec /app: exec format error
原因は CPU アーキテクチャの違いです。Apple Silicon Mac ではデフォルトで arm64 イメージがビルドされますが、多くのクラウドホストは amd64(x86_64)です。プログラムは特定の CPU 用の機械語にコンパイルされるため、別の CPU では読み取れず、このエラーが発生します。後ほど実際に再現します。
よく使われるプラットフォーム
| プラットフォーム | よく使われるデバイス |
|---|---|
linux/amd64 | ほとんどのクラウドホスト、Intel / AMD のコンピューター |
linux/arm64 | Apple Silicon Mac、AWS Graviton、Google Axion、Raspberry Pi 4/5(64 ビット OS) |
linux/arm/v7 | 旧型 Raspberry Pi(32 ビット OS) |
多平台イメージとは
公式イメージ(例:nginx、node)は多平台イメージです。同じ名前と tag の下に、複数のアーキテクチャ向けのバージョンが格納されています。nginx:1.30-alpine が対応するプラットフォームを確認します。
$ docker buildx imagetools inspect nginx:1.30-alpine | grep "Platform:" | grep -v unknown
Platform: linux/amd64
Platform: linux/arm/v6
Platform: linux/arm/v7
Platform: linux/arm64/v8
Platform: linux/386
Platform: linux/ppc64le
Platform: linux/riscv64
Platform: linux/s390x
同じ nginx:1.30-alpine に 8 種類のアーキテクチャが含まれています。docker pull 時に Docker は現在のホストに合うバージョンを自動的に選ぶため、同じ docker run nginx:1.30-alpine を Mac でもクラウドホストでも実行できます。独自のイメージでも同じことができます。
サンプルの準備:Go の Web サービス
実行中のアーキテクチャを表示する Go プログラムを使って説明します。フォルダー goapp:
goapp/
├── go.mod
├── main.go
└── Dockerfile
go.mod:
module example.com/server
go 1.27
main.go:
package main
import (
"fmt"
"net/http"
"runtime"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from %s/%s\n", runtime.GOOS, runtime.GOARCH)
})
fmt.Printf("Listening on :8080 (%s/%s)\n", runtime.GOOS, runtime.GOARCH)
http.ListenAndServe(":8080", nil)
}
Dockerfile:
FROM --platform=$BUILDPLATFORM golang:1.27 AS build
ARG TARGETOS TARGETARCH
WORKDIR /src
COPY . .
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o /out/app .
FROM gcr.io/distroless/static-debian13
COPY --from=build /out/app /app
ENTRYPOINT ["/app"]
これはクロスコンパイルを行う Dockerfile です。重要なのは、BuildKit が自動的に提供する次の引数です。
| 引数 | 意味 | この例での値 |
|---|---|---|
BUILDPLATFORM | ビルドを実行するマシンのプラットフォーム | linux/amd64 |
TARGETPLATFORM | 出力するイメージのプラットフォーム | linux/amd64 または linux/arm64 |
TARGETOS / TARGETARCH | ターゲットプラットフォームの OS とアーキテクチャ | linux / amd64 または arm64 |
FROM --platform=$BUILDPLATFORM golang:1.27:コンパイル段階は常にビルドマシン自体のアーキテクチャで実行されるため、ネイティブの速度で動作します。GOOS=$TARGETOS GOARCH=$TARGETARCH:Go コンパイラーにターゲットアーキテクチャ用の実行ファイルを直接生成させます。- 最後の段階は
COPYのみで、プログラムを実行しないため、ターゲット CPU のエミュレーションは不要です。
buildx で 2 つのプラットフォームを一度にビルドする
docker buildx は BuildKit を使用するビルドコマンドです。Docker Desktop と Linux の docker-buildx-plugin には、どちらも標準で含まれています。まず、現在の builder を確認しましょう。
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
default* docker
\_ default \_ default running v0.34.0 linux/amd64 (+3)
Docker 29 では containerd イメージストレージがデフォルトで使われるため、デフォルトの docker builder でマルチプラットフォームイメージをビルドし、ローカルに保存できます。--platform で複数のプラットフォームを指定します。
$ docker buildx build --platform linux/amd64,linux/arm64 -t goapp:1.0 .
[+] Building 106.9s (16/16) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 304B 0.0s
=> [linux/arm64 internal] load metadata for gcr.io/distroless/static-debian13:latest 2.0s
=> [linux/amd64 internal] load metadata for gcr.io/distroless/static-debian13:latest 1.8s
=> [linux/amd64 internal] load metadata for docker.io/library/golang:1.27 1.5s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [linux/amd64 build 1/4] FROM docker.io/library/golang:1.27@sha256:e432b43af23a9328d56a7c499be0476810aa344acbcf65fc7c455d4ff5a40602 19.3s
=> => resolve docker.io/library/golang:1.27@sha256:e432b43af23a9328d56a7c499be0476810aa344acbcf65fc7c455d4ff5a40602 0.0s
=> => sha256:a6132307e9f8de84712b13dc48449cc6a8a80bc26e35b5c9dd04a6df2b23e85e 102.41MB / 102.41MB 3.3s
=> => sha256:b2b6413ae8f393d11d32262c8dc6bd053dbaea3d074f95edd5605d3eb116e3c2 71.15MB / 71.15MB 2.5s
=> => sha256:801401a9a8ba22fe48ef647d8eed0d6d8adc41baabf2fae9057ff38b2622f35a 127B / 127B 0.3s
=> => sha256:4dc3950125ebc9ed195e1101fc6c02e2fce113ba0f53bfb05b2cdf58747df9af 67.81MB / 67.81MB 2.6s
=> => sha256:62fe2e747f69f2f414c7e9eb1553155cf0a658398f0462557f458821dce2b29d 25.64MB / 25.64MB 1.5s
=> => sha256:4846f29007305297af653c57bf9b60ba8f676a38695c68d98460063442581671 49.39MB / 49.39MB 1.4s
=> => extracting sha256:4846f29007305297af653c57bf9b60ba8f676a38695c68d98460063442581671 2.7s
=> => extracting sha256:62fe2e747f69f2f414c7e9eb1553155cf0a658398f0462557f458821dce2b29d 0.9s
=> => extracting sha256:4dc3950125ebc9ed195e1101fc6c02e2fce113ba0f53bfb05b2cdf58747df9af 3.1s
=> => extracting sha256:a6132307e9f8de84712b13dc48449cc6a8a80bc26e35b5c9dd04a6df2b23e85e 4.3s
=> => extracting sha256:b2b6413ae8f393d11d32262c8dc6bd053dbaea3d074f95edd5605d3eb116e3c2 4.9s
=> => extracting sha256:801401a9a8ba22fe48ef647d8eed0d6d8adc41baabf2fae9057ff38b2622f35a 0.0s
=> => extracting sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1 0.0s
=> [linux/amd64 stage-1 1/2] FROM gcr.io/distroless/static-debian13:latest@sha256:58133991db06659feaabe0f4e97a35cebf15ef4ea08f8a4c6d2ee5f75e4a 4.3s
=> => resolve gcr.io/distroless/static-debian13:latest@sha256:58133991db06659feaabe0f4e97a35cebf15ef4ea08f8a4c6d2ee5f75e4aa6a0 0.0s
=> => sha256:c4bc6f35ff5e25aa1afe7a148a0b6f10a760f54e298f86a4a4d44b91f1afbfab 143.34kB / 143.34kB 1.0s
=> => sha256:2cc7ee286bf3a9e6af5f71756d7fc8e22e23ce65fec9047d774511ffcef79fa8 124.53kB / 124.53kB 0.9s
=> => extracting sha256:2cc7ee286bf3a9e6af5f71756d7fc8e22e23ce65fec9047d774511ffcef79fa8 0.1s
=> => extracting sha256:c172f21841dff4c8cf45cde46589c1c2616cefe7e819965e92e6d3475c428aa0 0.1s
=> => extracting sha256:218cf840d0d95a86231eb9d33d26ac8a520a4299c6096cb70a0c2fb2a87c9e0a 0.3s
=> => extracting sha256:f6069939f718601b38d25ec5ed5631ed047e4da24acec2e252f8f29d251be718 0.2s
=> => extracting sha256:d6b1b89eccacc15c2420b2776d72c1dae334a00805ed9af54bf2f71e4d536f28 0.0s
=> => extracting sha256:2780920e5dbfbe103d03a583ed75345306e572ec5a48cb10361f046767d9f29a 0.0s
=> => extracting sha256:7c12895b777bcaa8ccae0605b4de635b68fc32d60fa08f421dc3818bf55ee212 0.0s
=> => extracting sha256:3214acf345c0cc6bbdb56b698a41ccdefc624a09d6beb0d38b5de0b2303ecaf4 0.0s
=> => extracting sha256:52630fc75a18675c530ed9eba5f55eca09b03e91bd5bc15307918bbc1a7e7296 0.0s
=> => extracting sha256:dd64bf2dd177757451a98fcdc999a339c35dee5d9872d8f4dc69c8f3c4dd0112 0.0s
=> => extracting sha256:b839dfae01f66e15c6a8b63520557ed315bdfe036342fa7a0c537259f10d7a9a 0.0s
=> => extracting sha256:ebddc55facdc6b1f7e0f30816a5fc7cc62f38abdf76c0a8b0a0ce52085754795 0.1s
=> => extracting sha256:c4bc6f35ff5e25aa1afe7a148a0b6f10a760f54e298f86a4a4d44b91f1afbfab 0.1s
=> [linux/arm64 stage-1 1/2] FROM gcr.io/distroless/static-debian13:latest@sha256:58133991db06659feaabe0f4e97a35cebf15ef4ea08f8a4c6d2ee5f75e4a 4.4s
=> => resolve gcr.io/distroless/static-debian13:latest@sha256:58133991db06659feaabe0f4e97a35cebf15ef4ea08f8a4c6d2ee5f75e4aa6a0 0.0s
=> => sha256:b839dfae01f66e15c6a8b63520557ed315bdfe036342fa7a0c537259f10d7a9a 351B / 351B 0.3s
=> => sha256:ebddc55facdc6b1f7e0f30816a5fc7cc62f38abdf76c0a8b0a0ce52085754795 311B / 311B 0.3s
=> => sha256:6ed1ef0cb1159b9c93a24ceb88b987c6b3a8adff7160940c7c5e6bfc001f1766 143.34kB / 143.34kB 1.7s
=> => sha256:dd64bf2dd177757451a98fcdc999a339c35dee5d9872d8f4dc69c8f3c4dd0112 80B / 80B 0.8s
=> => sha256:52630fc75a18675c530ed9eba5f55eca09b03e91bd5bc15307918bbc1a7e7296 162B / 162B 0.5s
=> => sha256:3214acf345c0cc6bbdb56b698a41ccdefc624a09d6beb0d38b5de0b2303ecaf4 123B / 123B 0.3s
=> => sha256:7c12895b777bcaa8ccae0605b4de635b68fc32d60fa08f421dc3818bf55ee212 188B / 188B 0.3s
=> => sha256:2780920e5dbfbe103d03a583ed75345306e572ec5a48cb10361f046767d9f29a 67B / 67B 0.4s
=> => sha256:d6b1b89eccacc15c2420b2776d72c1dae334a00805ed9af54bf2f71e4d536f28 32.09kB / 32.09kB 0.7s
=> => sha256:f6069939f718601b38d25ec5ed5631ed047e4da24acec2e252f8f29d251be718 254.67kB / 254.67kB 1.1s
=> => sha256:218cf840d0d95a86231eb9d33d26ac8a520a4299c6096cb70a0c2fb2a87c9e0a 288.06kB / 288.06kB 1.8s
=> => sha256:c172f21841dff4c8cf45cde46589c1c2616cefe7e819965e92e6d3475c428aa0 12.68kB / 12.68kB 0.3s
=> => sha256:401d177fd1e8a1e0d7a48139788d026e9a2e919ccb122bedd110ac7cf351f53e 124.50kB / 124.50kB 1.1s
=> => extracting sha256:401d177fd1e8a1e0d7a48139788d026e9a2e919ccb122bedd110ac7cf351f53e 0.1s
=> => extracting sha256:c172f21841dff4c8cf45cde46589c1c2616cefe7e819965e92e6d3475c428aa0 0.0s
=> => extracting sha256:218cf840d0d95a86231eb9d33d26ac8a520a4299c6096cb70a0c2fb2a87c9e0a 0.2s
=> => extracting sha256:f6069939f718601b38d25ec5ed5631ed047e4da24acec2e252f8f29d251be718 0.3s
=> => extracting sha256:d6b1b89eccacc15c2420b2776d72c1dae334a00805ed9af54bf2f71e4d536f28 0.0s
=> => extracting sha256:2780920e5dbfbe103d03a583ed75345306e572ec5a48cb10361f046767d9f29a 0.0s
=> => extracting sha256:7c12895b777bcaa8ccae0605b4de635b68fc32d60fa08f421dc3818bf55ee212 0.0s
=> => extracting sha256:3214acf345c0cc6bbdb56b698a41ccdefc624a09d6beb0d38b5de0b2303ecaf4 0.0s
=> => extracting sha256:52630fc75a18675c530ed9eba5f55eca09b03e91bd5bc15307918bbc1a7e7296 0.0s
=> => extracting sha256:dd64bf2dd177757451a98fcdc999a339c35dee5d9872d8f4dc69c8f3c4dd0112 0.0s
=> => extracting sha256:b839dfae01f66e15c6a8b63520557ed315bdfe036342fa7a0c537259f10d7a9a 0.0s
=> => extracting sha256:ebddc55facdc6b1f7e0f30816a5fc7cc62f38abdf76c0a8b0a0ce52085754795 0.0s
=> => extracting sha256:6ed1ef0cb1159b9c93a24ceb88b987c6b3a8adff7160940c7c5e6bfc001f1766 0.1s
=> [internal] load build context 0.0s
=> => transferring context: 735B 0.0s
=> [linux/amd64 build 2/4] WORKDIR /src 0.1s
=> [linux/amd64 build 3/4] COPY . . 0.0s
=> [linux/amd64 build 4/4] RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o /out/app . 83.3s
=> [linux/amd64->arm64 build 4/4] RUN GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o /out/app . 83.8s
=> [linux/amd64 stage-1 2/2] COPY --from=build /out/app /app 0.0s
=> [linux/arm64 stage-1 2/2] COPY --from=build /out/app /app 0.1s
=> exporting to image 1.4s
=> => exporting layers 0.9s
=> => exporting manifest sha256:bae3f4b45be6e6c37bbe5430c1fd68cf98974b82dd312b81fcf568abb321584c 0.0s
=> => exporting config sha256:795e98f14dcf24dbcc9a133515ee54505a800a4ebf3f2408d89441c3a166c468 0.0s
=> => exporting attestation manifest sha256:379134f8a7c50a77e0bf19cbd9593a884daeac7eb2b7c852ccfa8f81ce7bdd34 0.0s
=> => exporting manifest sha256:d6cd4f43dd3d332129c7b6b9f6720c6da06e070125860d2ab0661e15d251133a 0.0s
=> => exporting config sha256:d61c8d18abcb388876ad82cb6009d9bd18754e6e277ac6822c697d407243594a 0.0s
=> => exporting attestation manifest sha256:2f4085c9f5da45df6153b09141bf7db573806a93a2ed81c64c2ec75e49bfa8e6 0.0s
=> => exporting manifest list sha256:dc8d3f71a9f7adb0de32dd59651adc81dab5b78612cdc7eccc39a5f73608d37e 0.0s
=> => naming to docker.io/library/goapp:1.0 0.0s
=> => unpacking to docker.io/library/goapp:1.0 0.4s
ビルドの過程を確認します。
- 各ステップの前に、
[linux/amd64 build 4/4]のようにプラットフォームが表示されます。 - arm64 のコンパイルステップは
[linux/amd64->arm64 build 4/4]と表示されます。これは amd64 マシン上で arm64 向けにコンパイルしていることを示します。これがクロスコンパイルです。 - 2 つのプラットフォームの
go buildは並列に実行されます。
docker images --tree で結果を確認します(--tree オプションは新しいバージョンの Docker でのみ使えます)。
$ docker images --tree goapp:1.0
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
goapp:1.0 dc8d3f71a9f7 25.5MB 11.1MB
├─ linux/amd64 bae3f4b45be6 20.2MB 5.77MB
└─ linux/arm64 d6cd4f43dd3d 5.36MB 5.36MB
goapp:1.0 の下には linux/amd64 と linux/arm64 の 2 つのバージョンがあります。arm64 バージョンの DISK USAGE が小さいのは、ローカルで実行しないため、展開されていないからです。
古いバージョンの Docker を使っている場合や、containerd イメージストレージが有効でない場合、デフォルトの builder ではマルチプラットフォームイメージを保存できません。その場合は、まず
docker-containerドライバーを使う builder を作成します。docker buildx create --name multi --driver docker-container --useを実行し、ビルド時に--pushを追加して Registry に直接プッシュしてください。
実測:異なるアーキテクチャで実行する
amd64 ホストで実行すると、Docker は自動的に amd64 版を選択します。
$ docker run -d --name goapp -p 8080:8080 goapp:1.0
2d1485d9ba48cf1b87983ff4d797699dde128295880f1fe965f24801da611ac4
$ curl -s http://localhost:8080/
Hello from linux/amd64
$ docker logs goapp
Listening on :8080 (linux/amd64)
$ docker rm -f goapp
goapp
プログラムは自身が linux/amd64 であることを報告します。
arm64 版を強制的に実行すると、冒頭のエラーが再現します。
$ docker run --rm --platform linux/arm64 goapp:1.0
exec /app: exec format error
$ docker run --rm --platform linux/arm64 alpine:3.23 uname -m
Unable to find image 'alpine:3.23' locally
3.23: Pulling from library/alpine
Digest: sha256:85fe1e81d6758c208f3e1eed4338a1997e19d4be002d4dd32d3100c9a8c010a0
Status: Image is up to date for alpine:3.23
exec /bin/uname: exec format error
- amd64 ホスト上の arm64 実行ファイルでは、
exec format errorが発生します。 - 公式の
alpine:3.23も同様です。Docker は arm64 版をダウンロードしましたが、ホストの CPU では実行できません。
docker image inspect でイメージのアーキテクチャを確認できます。--platform を指定すると、指定したプラットフォームのバージョンを確認できます。
$ docker image inspect --format "{{.Os}}/{{.Architecture}}" goapp:1.0
linux/amd64
$ docker image inspect --platform linux/arm64 --format "{{.Os}}/{{.Architecture}}" goapp:1.0
linux/arm64
$ uname -m
x86_64
QEMU エミュレーターが必要になる場合
上記のクロスコンパイルでエミュレーションが不要なのは、ビルド中にターゲットプラットフォームのプログラムを一切実行しないためです。Dockerfile でターゲットプラットフォームのイメージ内のコマンド(RUN)を実行する場合は、事情が異なります。
FROM alpine:3.23
RUN apk add --no-cache curl
$ docker buildx build --platform linux/arm64 -t arm-test .
[+] Building 0.5s (5/5) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 82B 0.0s
=> [internal] load metadata for docker.io/library/alpine:3.23 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/2] FROM docker.io/library/alpine:3.23@sha256:85fe1e81d6758c208f3e1eed4338a1997e19d4be002d4dd32d3100c9a8c010a0 0.1s
=> => resolve docker.io/library/alpine:3.23@sha256:85fe1e81d6758c208f3e1eed4338a1997e19d4be002d4dd32d3100c9a8c010a0 0.0s
=> => extracting sha256:ace1621be7ff15b54252f68393ac33181df7f3e095e36a5d9a9892031b357d31 0.1s
=> ERROR [2/2] RUN apk add --no-cache curl 0.3s
------
> [2/2] RUN apk add --no-cache curl:
0.250 exec /bin/sh: exec format error
------
Dockerfile:2
--------------------
1 | FROM alpine:3.23
2 | >>> RUN apk add --no-cache curl
3 |
--------------------
ERROR: failed to build: failed to solve: process "/bin/sh -c apk add --no-cache curl" did not complete successfully: exit code: 255
RUN apk add は arm64 の Alpine で /bin/sh を実行する必要がありますが、amd64 ホストでは実行できず、同じ exec format error が発生します。この場合、amd64 ホストで arm64 プログラムを実行できるようにする QEMU エミュレーターが必要です。
-
Docker Desktop(Windows、macOS):QEMU が組み込まれているため、設定は不要です。
--platformを指定してそのままビルドできます。 -
Linux:次のコマンドを実行してインストールします。
docker run --privileged --rm tonistiigi/binfmt --install allこのコマンドはホストカーネルにエミュレーターを登録し、ホスト全体に影響します(再起動すると無効になります)。自分で管理しているマシンで実行してください。このチュートリアルのテスト環境にはインストールされていないため、上記のエラーが再現しました。
エミュレーションはネイティブ実行より数倍遅くなることがあります。特に、コンパイルが必要な手順(たとえば npm ci でのネイティブモジュールのコンパイルや、pip install での C 拡張のコンパイル)では顕著です。そのため、クロスコンパイルできる言語(Go、Rust)では、できるだけクロスコンパイルを使いましょう。その他の言語では、CI でネイティブの arm64 実行環境を使うことも検討できます。
マルチプラットフォームイメージを push する
Registry に push すると、両方のプラットフォームがまとめてアップロードされます。ここではローカルの Registry(registry:3。詳しくはDocker Hub に push するを参照)を使って説明します。
$ docker tag goapp:1.0 localhost:5000/goapp:1.0
$ docker push localhost:5000/goapp:1.0
The push refers to repository [localhost:5000/goapp]
49af9741d5c4: Pushed
2cc7ee286bf3: Pushed
c172f21841df: Pushed
7c12895b777b: Pushed
44136fa355b3: Pushed
f64822ab576c: Pushed
218cf840d0d9: Pushed
f6069939f718: Pushed
d6b1b89eccac: Pushed
2780920e5dbf: Pushed
3214acf345c0: Pushed
52630fc75a18: Pushed
dd64bf2dd177: Pushed
b839dfae01f6: Pushed
ebddc55facdc: Pushed
c4bc6f35ff5e: Pushed
3febe656e709: Pushed
6ed1ef0cb115: Pushed
7f6c568ae1e4: Pushed
401d177fd1e8: Pushed
1.0: digest: sha256:dc8d3f71a9f7adb0de32dd59651adc81dab5b78612cdc7eccc39a5f73608d37e size: 1609
docker buildx imagetools inspect を使って、Registry 上の manifest を確認します。
$ docker buildx imagetools inspect localhost:5000/goapp:1.0
Name: localhost:5000/goapp:1.0
MediaType: application/vnd.oci.image.index.v1+json
Digest: sha256:dc8d3f71a9f7adb0de32dd59651adc81dab5b78612cdc7eccc39a5f73608d37e
Manifests:
Name: localhost:5000/goapp:1.0@sha256:bae3f4b45be6e6c37bbe5430c1fd68cf98974b82dd312b81fcf568abb321584c
MediaType: application/vnd.oci.image.manifest.v1+json
Platform: linux/amd64
Name: localhost:5000/goapp:1.0@sha256:d6cd4f43dd3d332129c7b6b9f6720c6da06e070125860d2ab0661e15d251133a
MediaType: application/vnd.oci.image.manifest.v1+json
Platform: linux/arm64
Name: localhost:5000/goapp:1.0@sha256:379134f8a7c50a77e0bf19cbd9593a884daeac7eb2b7c852ccfa8f81ce7bdd34
MediaType: application/vnd.oci.image.manifest.v1+json
Platform: unknown/unknown
Annotations:
vnd.docker.reference.digest: sha256:bae3f4b45be6e6c37bbe5430c1fd68cf98974b82dd312b81fcf568abb321584c
vnd.docker.reference.type: attestation-manifest
Name: localhost:5000/goapp:1.0@sha256:2f4085c9f5da45df6153b09141bf7db573806a93a2ed81c64c2ec75e49bfa8e6
MediaType: application/vnd.oci.image.manifest.v1+json
Platform: unknown/unknown
Annotations:
vnd.docker.reference.digest: sha256:d6cd4f43dd3d332129c7b6b9f6720c6da06e070125860d2ab0661e15d251133a
vnd.docker.reference.type: attestation-manifest
- 最上位は image index(
application/vnd.oci.image.index.v1+json)です。これはマルチプラットフォームイメージの「目録」です。 - その下に
linux/amd64とlinux/arm64の2つのイメージがあります。 - さらに、2つの
unknown/unknownは BuildKit が自動生成した attestation(ビルド元の証明)で、それぞれのプラットフォームに対応します。実行可能なイメージではありません。
Docker Hub に push する場合もコマンドは同じで、名前を あなたのアカウント/goapp:1.0 に変えるだけです。以降、どのアーキテクチャのホストでも docker pull を実行すると、対応するバージョンを取得できます。
ターゲットプラットフォームだけをビルドする
Apple Silicon 搭載 Mac で amd64 サーバー向けのイメージをビルドするだけなら、マルチプラットフォームにする必要はありません。ターゲットプラットフォームを指定します。
docker build --platform linux/amd64 -t yourname/myapp:1.0 .
docker push yourname/myapp:1.0
Docker Desktop は内蔵の QEMU で amd64 をエミュレートします。ネイティブ環境より遅くなりますが、正常に動作します。
まとめ
- イメージはホストの CPU アーキテクチャに対応している必要があります。対応していない場合は
exec format errorが発生します。 - マルチプラットフォームイメージは index で、その下に各プラットフォーム用のバージョンがあります。
FROM --platform=$BUILDPLATFORMとTARGETOS/TARGETARCHを使ったクロスコンパイルでは、エミュレーションは不要です。- Docker Desktop には QEMU が組み込まれています。Linux では binfmt をインストールする必要があります。
練習問題
練習 1:docker buildx imagetools inspect を使って、postgres:18 がどのプラットフォームに対応しているか確認してください。
解答
docker buildx imagetools inspect postgres:18 | grep Platform: | grep -v unknown
unknown/unknown はビルド証明(attestation)であり、実行可能なイメージではないため除外します。
よくある質問
Mac は Apple Silicon ですが、クラウドホストにデプロイするときは何に注意すればよいですか?
ビルド時に --platform linux/amd64 を指定するか、多平台イメージをビルドしてください。そうしないとサーバーで exec format error が発生します。
多平台ビルドには必ず QEMU が必要ですか?
ビルド中にターゲットプラットフォーム上でプログラムを実行する必要がある場合(例:RUN npm ci)に限り必要です。Go や Rust のようにクロスコンパイルできる言語では不要です。
イメージがどのプラットフォームに対応しているか、どうすれば分かりますか?
docker buildx imagetools inspect イメージ を実行します。この章では nginx を例に 8 種類のプラットフォームを示します。
更新日 2026年10月14日