3行要約
- Docker Buildxを使うとステージごとに実行するプラットフォームを変えられる
- のでビルドはネイティブマシンで実行して、実行するイメージを作るところだけをQEMUを使うと良さそう
- Goだとクロスコンパイルができるのでネイティブマシンでビルドをすることにした
目次です。
イントロ
マルチプラットフォームイメージ使っていますか?
Dockerが提供するイメージはマルチプラットフォームイメージに対応していて、例えばUbuntu*1では次のプラットフォームで同じタグを使うことができます。
こうすることで別のアーキテクチャのマシンで同じタグのコンテナを動かすことができます。嬉しい。
Dockerを用いる場合、Docker Buildxを使って複数のプラットフォームに対応したイメージを作ることができます。 しかしGitHub Actionsでコンテナイメージを作成するときは、GitHub Actionsのランナーがamd64のものしか存在しないため別のアーキテクチャのイメージを作るときはQEMUを使うことになります。
ということでイメージのビルドを高速にしたいならばQEMUをなるべく使わない方向に持っていくのが良さそうです。 実際に今回取った手法だとQEMUの使用を最小限に押さえることができます。
ステージごとに実行する環境を変える
ちゃんと公式ドキュメントにも記述があります。
Using a stage in Dockerfile to cross-compile to different architectures
とあります。
そして続くドキュメントには以下の例が掲載されています。
# syntax=docker/dockerfile:1 FROM --platform=$BUILDPLATFORM golang:alpine AS build ARG TARGETPLATFORM ARG BUILDPLATFORM RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" > /log FROM alpine COPY --from=build /log /log # コードブロック内は https://docs.docker.com/build/building/multi-platform/ より引用
FROM --platform=$BUILDPLATFORM
というのがミソで、platformの指定をすることによりQEMUの使用を回避することができます。
完成品
FROM --platform=$BUILDPLATFORM golang:1.21 AS builder WORKDIR /app ARG TARGETOS ARG TARGETARCH ENV GOOS=${TARGETOS} ENV GOARCH=${TARGETARCH} COPY . . RUN go build -o app . FROM gcr.io/distroless/base-debian12:nonroot AS runner COPY --from=builder /app/app /app ENTRYPOINT ["/app"]
ドキュメントにあった例をgoのアプリケーションに適用しました。そうするとこのようなDockerfileになります。
goではビルド時にGOOS
, GOARCH
環境変数を指定することにより他環境で動くバイナリを生成することができます。今回はその仕組みを活用しました。
終わりに
QEMU をできる限り使わずに別プラットフォームで動くコンテナイメージを作成する方法を紹介しました。
この方法だとイメージの作成時間を短縮できます。
今回はgoの例を上げましたが、Rustのようなクロスコンパイルが出来る言語やjavaやscalaのようなjvmで動く言語で書かれたアプリケーションなら同じように解決できるのではないかと考えています。