目次

多平台イメージ(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/arm64Apple 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日