Dockerfileで対象プラットフォームによって処理分岐させる
概要
マルチプラットフォーム対応のdockerイメージをビルドしていると、最終的にコンテナが動作するホストのCPUアーキテクチャによって使用させる資源をDockerfile内で分岐させたい場合があります。
今回はターゲットのプラットフォームがlinux/amd64
とlinux/arm64
だとして、検証してみた際の備忘となります(Intel macとApple silicon macのそれぞれで動作させたかった背景です)。
ポイント
- Dockerfile内でTARGETPLATFORM変数を使う
やってみた
環境
- M2 mac
- docker 20.10.24
- docker buildx v0.10.4
自動定義されるARG変数の確認
Buildxによるビルドにおいて、上記ページで記載されているTARGETPLATFORM
などのARG変数は自動で設定されるようです。
Dockerfileで使用したい場合には、ARG TARGETPLATFORM
と宣言をしておけば利用ができるとのこと。
以下のようなダミーのechoをするDockerfileとコマンドで試してみます。
FROM debian:buster
ARG TARGETPLATFORM \
TARGETARCH \
BUILDPLATFORM \
BUILDARCH
RUN echo "TARGETPLATFORM=${TARGETPLATFORM} TARGETARCH=${TARGETARCH}" && \
echo "BUILDPLATFORM=${BUILDPLATFORM} BUILDARCH=${BUILDARCH}"
$ docker buildx build --platform linux/amd64 .
結果は以下のようになりました。
--platformで指定したターゲットはamd64で、ビルドしているホストはM2 macでarm64なので、ちゃんとそれらしい値がARG変数に渡されていますね!
[+] Building 14.5s (6/6) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 576B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/debian:buster 3.9s
=> [1/2] FROM docker.io/library/debian:buster@sha256:cca6bcced970f7634197ff1821aabb452024eb437958ab98bfc146ece96969c6 10.3s
=> => resolve docker.io/library/debian:buster@sha256:cca6bcced970f7634197ff1821aabb452024eb437958ab98bfc146ece96969c6 0.0s
=> => sha256:a94073ab46f8d565f5938cc345d32f7adda10a2585e39555079da9d4ee595974 50.45MB / 50.45MB 9.4s
=> => sha256:cca6bcced970f7634197ff1821aabb452024eb437958ab98bfc146ece96969c6 984B / 984B 0.0s
=> => sha256:ebc58102f66492508f6d0f0c5164978afbe27f4a69bd3431ed8bb015c82a8b80 529B / 529B 0.0s
=> => sha256:dd20fc9536df9a9498531c65f65a6334b2dd8c199476491a4f2be95c148d9cc1 1.46kB / 1.46kB 0.0s
=> => extracting sha256:a94073ab46f8d565f5938cc345d32f7adda10a2585e39555079da9d4ee595974 0.7s
=> [2/2] RUN echo "TARGETPLATFORM=linux/amd64 TARGETARCH=amd64" && echo "BUILDPLATFORM=linux/arm64 BUILDARCH=arm64" 0.2s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:cd5066c6e1c03c8d225070c8884a1c7d1a49b93c13e25a83fbd45ddf0f127976
では--platform linux/amd64,linux/arm64
で複数指定した場合はどうなるでしょうか。
(BUILDPLATFORMとBUILDARCHは以降省きます)
結果としては以下のようにそれぞれのアーキテクチャが表示されました!
$ docker buildx create --use
nostalgic_wing
$ docker buildx build --platform linux/amd64,linux/arm64 .
[+] Building 18.4s (9/9) FINISHED
=> [internal] booting buildkit 3.4s
=> => pulling image moby/buildkit:buildx-stable-1 2.7s
=> => creating container buildx_buildkit_nostalgic_wing0 0.7s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 506B 0.0s
=> [linux/arm64 internal] load metadata for docker.io/library/debian:buster 4.4s
=> [linux/amd64 internal] load metadata for docker.io/library/debian:buster 4.4s
=> [linux/amd64 1/2] FROM docker.io/library/debian:buster@sha256:cca6bcced970f7634197ff1821aabb452024eb437958ab98bfc146ece96969c6 10.2s
=> => resolve docker.io/library/debian:buster@sha256:cca6bcced970f7634197ff1821aabb452024eb437958ab98bfc146ece96969c6 0.0s
=> => sha256:a94073ab46f8d565f5938cc345d32f7adda10a2585e39555079da9d4ee595974 50.45MB / 50.45MB 8.6s
=> => extracting sha256:a94073ab46f8d565f5938cc345d32f7adda10a2585e39555079da9d4ee595974 1.6s
=> [linux/arm64 1/2] FROM docker.io/library/debian:buster@sha256:cca6bcced970f7634197ff1821aabb452024eb437958ab98bfc146ece96969c6 9.4s
=> => resolve docker.io/library/debian:buster@sha256:cca6bcced970f7634197ff1821aabb452024eb437958ab98bfc146ece96969c6 0.0s
=> => sha256:b7c5fe8e0ac53c77142bf16686fc01d0d2b1fb2da7be5414cdf2f224ec485592 49.24MB / 49.24MB 7.8s
=> => extracting sha256:b7c5fe8e0ac53c77142bf16686fc01d0d2b1fb2da7be5414cdf2f224ec485592 1.6s
=> [linux/arm64 2/2] RUN echo "TARGETPLATFORM=linux/arm64 TARGETARCH=arm64" 0.2s
=> [linux/amd64 2/2] RUN echo "TARGETPLATFORM=linux/amd64 TARGETARCH=amd64" 0.1s
WARNING: No output specified with docker-container driver. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load
変数の値による分岐
前項で確認した変数を使えば、プラットフォームごとに適したバイナリのインストールなどがDockerfileで行えそうです。
ただ、インストールしたいパッケージ名のアーキテクチャ箇所がそのままTARGETPLATFORMの値になっているとかであればwget linux-${TARGETPLATFORM}.tar.gz
などとシンプルに記載できますが、x86_64とかaarch64とかになっていると変換が必要になります。
Dockerfileで分岐構文などは提供されていないため、私はシェル変数にcase文の結果を代入する方法で対応してみました。
例えば以下のように、TARGETPLATFORMの値をcaseで分岐させた結果をPLATFORMというシェル変数に代入して使用する感じです。
FROM debian:buster
ARG TARGETPLATFORM
RUN PLATFORM=$( \
case ${TARGETPLATFORM} in \
linux/amd64 ) echo "x86_64";; \
linux/arm64 ) echo "aarch_64";; \
esac \
) && \
# PLATFORMの値を使用したファイルを作成
touch ${PLATFORM} && \
# 作成したファイルがあるか確認(存在しなければ$?=2となりビルドエラーとなる)
[ -f ${PLATFORM} ]
$ docker buildx build --platform linux/amd64,linux/arm64 .
[+] Building 19.1s (8/8) FINISHED
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 761B 0.0s
=> [linux/arm64 internal] load metadata for docker.io/library/debian:buster 3.2s
=> [linux/amd64 internal] load metadata for docker.io/library/debian:buster 3.7s
=> [linux/arm64 1/2] FROM docker.io/library/debian:buster@sha256:cca6bcced970f7634197ff1821aabb452024eb437958ab98bfc146ece96969c6 15.3s
=> => resolve docker.io/library/debian:buster@sha256:cca6bcced970f7634197ff1821aabb452024eb437958ab98bfc146ece96969c6 0.0s
=> => sha256:b7c5fe8e0ac53c77142bf16686fc01d0d2b1fb2da7be5414cdf2f224ec485592 49.24MB / 49.24MB 14.2s
=> => extracting sha256:b7c5fe8e0ac53c77142bf16686fc01d0d2b1fb2da7be5414cdf2f224ec485592 1.1s
=> [linux/amd64 1/2] FROM docker.io/library/debian:buster@sha256:cca6bcced970f7634197ff1821aabb452024eb437958ab98bfc146ece96969c6 15.1s
=> => resolve docker.io/library/debian:buster@sha256:cca6bcced970f7634197ff1821aabb452024eb437958ab98bfc146ece96969c6 0.0s
=> => sha256:a94073ab46f8d565f5938cc345d32f7adda10a2585e39555079da9d4ee595974 50.45MB / 50.45MB 14.0s
=> => extracting sha256:a94073ab46f8d565f5938cc345d32f7adda10a2585e39555079da9d4ee595974 1.1s
=> [linux/amd64 2/2] RUN PLATFORM=$( case linux/amd64 in linux/amd64 ) echo "x86_64";; linux/arm64 ) echo "aarch_64";; esac ) && touch 0.2s
=> [linux/arm64 2/2] RUN PLATFORM=$( case linux/arm64 in linux/amd64 ) echo "x86_64";; linux/arm64 ) echo "aarch_64";; esac ) && touch 0.0s
WARNING: No output specified with docker-container driver. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load
(もっと良い方法があるよ!とかでしたら是非教えていただきたいです🙇)
それではこのcase分岐を使って、protoc(v3.15.8)のバイナリをダウンロードしてみます。
以下のようにDockerfileを作成し、ビルドしてdockerhubにpushします(ビルド結果は記載省略)。
FROM debian:buster
ARG TARGETPLATFORM
RUN apt update && apt install -y unzip wget
RUN PLATFORM=$( \
case ${TARGETPLATFORM} in \
linux/amd64 ) echo "x86_64";; \
linux/arm64 ) echo "aarch_64";; \
esac \
) && \
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.15.8/protoc-3.15.8-linux-${PLATFORM}.zip && \
unzip protoc-3.15.8-linux-${PLATFORM}.zip -d protoc3 && \
mv protoc3/bin/* /usr/local/bin/ && \
mv protoc3/include/* /usr/local/include/
$ docker buildx build --platform linux/amd64,linux/arm64 -t ytdrep/protoc-platform-test:latest --push .
pushしたイメージを、linux/arm64とlinux/amd64それぞれで起動してみます。
適切にprotocが動作すればokです。
$ docker run -it --rm ytdrep/protoc-platform-test:latest bash
root@7e6d7d96a739:/# uname -m
aarch64
root@7e6d7d96a739:/# protoc --version
libprotoc 3.15.8
$ docker run -it --rm --platform linux/amd64 ytdrep/protoc-platform-test:latest bash
root@722928a0be8e:/# uname -m
x86_64
root@722928a0be8e:/# protoc --version
libprotoc 3.15.8
問題なさそうですね!
あとがき
GitHub Actionsでbuildx使用してマルチプラットフォームビルドした場合も、同じようなDockerfileの使い方ができるか確認して追記したいと思います。
Discussion