複数アーキに対応する - multi-arch
1 つのイメージタグに amd64 と arm64 の両方を含めると、Apple Silicon や Graviton でも同じ pull で動く。Buildx のマニフェストリストで実現する。
概念図
構文
--platform linux/amd64,linux/arm64実例
QEMU で arm64 をエミュレーションし、単一タグで両アーキをまとめる。
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ghcr.io/acme/app:latest
cache-from: type=gha
cache-to: type=gha,mode=maxローカルの buildx からも同じ指定で push できる。
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ghcr.io/acme/app:latest \
--push .マニフェストリストの仕組み
OCI/Docker の仕様には「マニフェストリスト」(別名: fat manifest)があります。
1 つのタグが複数のアーキ向けマニフェストを束ねる目次として振る舞い、クライアントは pull 時に自分のプラットフォーム(例: linux/arm64)を見て適切な子マニフェストを選びます。
つまり ghcr.io/acme/app:latest を Mac(arm64)と Linux(amd64)から同じコマンドで pull すると、それぞれに適合したバイナリ入りイメージが降りてきます。
利用者は「どのアーキか」を意識する必要がありません。
QEMU とクロスビルドの選択
ホストが amd64 しかない Runner 上で arm64 イメージを作るには 2 つ手段があります。
- QEMU エミュレーション:
docker/setup-qemu-actionで binfmt_misc に QEMU を登録し、arm64 バイナリをエミュレーションしながら実行する。何も考えなくていいが、ネイティブより 3〜10 倍遅くなることがある - クロスコンパイル: Go / Rust / C++ のように
GOOS/GOARCHや target triple で明示的に arm64 向けバイナリを吐き、ベースイメージだけ--platform=$TARGETPLATFORMで切り替える。ビルドは速いがソース側の対応が必要
Runner が ARM ネイティブ(GitHub Actions の arm64 Runner や self-hosted の Graviton 等)になる場合は、そもそも QEMU が不要でさらに高速です。
コストと速度のトレードオフで選びます。
Dockerfile の書き方(TARGETPLATFORM)
マルチアーキビルドでは、Dockerfile 側がアーキ情報を受け取れるように書くと安全です。
BuildKit は $TARGETPLATFORM / $TARGETARCH / $TARGETOS などを自動的に注入します。
FROM --platform=$BUILDPLATFORM golang:1.22 AS builder
ARG TARGETOS
ARG TARGETARCH
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
go build -o /out/app ./cmd/app
FROM --platform=$TARGETPLATFORM gcr.io/distroless/static-debian12
COPY --from=builder /out/app /app
ENTRYPOINT ["/app"]
この書き方だと、ビルドは常にホストアーキ($BUILDPLATFORM)のネイティブ速度で走り、ターゲットアーキ向けのバイナリだけをクロスで吐けるため、QEMU を使う場合でもオーバーヘッドを最小化できます。
ハマり集
platformsを指定しただけで速くはならない: ソース側がネイティブでしかビルドできない言語(一部の古い Python ホイール等)は結局 QEMU に落ちる- マニフェストリストが作られていない: 単一アーキだけ push し、タグを上書きしただけの状態。pull 側が「そのアーキ向けのマニフェストがない」エラーになる
- Apple Silicon ユーザーだけ動かない: ローカルの
docker runはエミュレーションしてくれるが、arm64 イメージが存在しない場合は警告と共に amd64 版が走る。本番環境が arm64 だと一気に壊れる - イメージサイズが 2 倍: マニフェストリストは共通レイヤーを共有するが、言語ランタイムなど重いレイヤーはアーキ別で重複する。容量を想定より多く見積もる
関連トピック
GitHub Actions- GitHub Actions はリポジトリのイベントに応じてワークフローを実行する CI/CD サービス。`docker/build-push-action` を組み合わせれば、数十行の YAML でビルドとプッシュまで自動化できる。 buildx cache- BuildKit のキャッシュを CI で再利用すると、ビルド時間を大幅に短縮できる。GitHub Actions の `gha` キャッシュやレジストリ型キャッシュを使い分ける。 registry push- ビルドしたイメージはレジストリに push して初めて本番で使える。GHCR・ECR・GAR・Docker Hub など目的地ごとに認証・命名・保管ポリシーが異なる。 