Dock Stay
複数アーキに対応する - multi-arch の使い方・オプション・サンプル

複数アーキに対応する - multi-arch

1 つのイメージタグに amd64 と arm64 の両方を含めると、Apple Silicon や Graviton でも同じ pull で動く。Buildx のマニフェストリストで実現する。

概念図

multi-arch diagram

構文

bash
--platform linux/amd64,linux/arm64

実例

QEMU で arm64 をエミュレーションし、単一タグで両アーキをまとめる。

bash
- 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 できる。

bash
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 倍: マニフェストリストは共通レイヤーを共有するが、言語ランタイムなど重いレイヤーはアーキ別で重複する。容量を想定より多く見積もる

関連トピック