1. はじめに
株式会社オークファン 開発部の @ngok です。
昨今、M1 Macの登場やAWS Gravitonなどの低価格なインスタンスの利用が可能になったことにより ARMアーキテクチャのプロセッサが普及しました。
ただ、依然としてWindowsのPCにおいてはIntelやAMD製のx86アーキテクチャのプロセッサを使用することが多く、異なるCPUアーキテクチャにも対応した開発を求められることが多くなりつつあります。
本記事では x86のWindowsPCで、x86にもARMにも対応したマルチアーキテクチャのdockerイメージをビルドする方法を記載します。
また、マルチアーキテクチャイメージはdockerリポジトリにプッシュする必要がありますが、社内で使うことを想定してAWSのECRを使用します。
2. 経緯
オークファンでは社員に貸与するPCについて、WindowsまたはMacを社員が選択が可能となっています。
開発部でもWindowsのPCを使用している人もいればMacを使用している人もいて、それぞれに対応した開発環境が必要になっています。
開発環境にしばしば利用していた docker は、各人の使用OSに依存せずに利用でき便利でした。。。
そう、 M1 Mac が登場するまでは!!
基本的にdockerイメージをビルドしたマシンのCPUアーキテクチャ用にイメージがビルドされます。
WindowsPCのほとんどはIntelやAMD製のx86のCPUアーキテクチャですが、 M1のCPUを使用したMacでは ARMのCPUアーキテクチャとなります。
そのため、WindowsのPCでビルドしたイメージをM1 Macで起動しようとした場合エラーになり、その逆もしかりでした。
チーム内でのdockerイメージの共有が難しくなってしまいました。
Mac勢とWindows勢の隔たりが厚くなった瞬間でした。
3. 前提
- ホストOSにはWindows10を使用します
- PCのCPUはx86系です
- ビルド環境はWSL2 Ubuntu20.04を使用します
- docker をインストールしておきます
- docker buildx をインストールしておきます
- ECRにリポジトリを作成しておきます
- ビルドするイメージのDockerFileは任意で作成しておきます
4. マルチアーキテクチャイメージのビルド
buildx のビルダーを作成
マルチアーキテクチャイメージをビルドするにはbuildxコマンドで使用するビルダーを設定する必要があります。
ビルダーに ARMアーキテクチャに対応したエミュレータ(qemu)を設定します。
- buildxに登録されているビルダーを確認する。
linux/arm64などのARMアーキテクチャがPLATFORMSにないことを確認します。
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
default docker
default default running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386
- ARM実行可能ファイルを登録する。実行後に
linux/arm64
などのARMアーキテクチャのPLATFORMSが追加されていることを確認します。
最新の qumuイメージを使用します。
$ docker run --rm --privileged docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
default docker
default default running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386, linux/arm64, linux/ppc64le, linux/s390x, linux/arm/v7, linux/arm/v6
- 新規のビルダーインスタンスを作成します。
$ docker buildx create --name mybuilder
mybuilder
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
mybuilder docker-container
mybuilder tcp://localhost:2375 inactive
default * docker
default default running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386, linux/arm64, linux/ppc64le, linux/s390x, linux/arm/v7, linux/arm/v6
- 使用するビルダーを作成したビルダーに切り替えます。
$ docker buildx use mybuilder
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
mybuilder * docker-container
mybuilder tcp://localhost:2375 inactive
default docker
default default running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386, linux/arm64, linux/ppc64le, linux/s390x, linux/arm/v7, linux/arm/v6
- ビルダーインスタンスを起動します。
$ docker buildx inspect --bootstrap
[+] Building 1.1s (1/1) FINISHED
=> [internal] booting buildkit 1.1s
=> => starting container buildx_buildkit_mybuilder0 1.1s
Name: mybuilder
Driver: docker-container
Last Activity: 2023-11-02 08:27:49 +0000 UTC
Nodes:
Name: mybuilder0
Endpoint: tcp://localhost:2375
Status: running
Buildkit: v0.12.3
Platforms: linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
Labels:
org.mobyproject.buildkit.worker.executor: oci
org.mobyproject.buildkit.worker.hostname: 0a171ebcd99e
org.mobyproject.buildkit.worker.network: host
org.mobyproject.buildkit.worker.oci.process-mode: sandbox
org.mobyproject.buildkit.worker.selinux.enabled: false
org.mobyproject.buildkit.worker.snapshotter: overlayfs
GC Policy rule#0:
All: false
Filters: type==source.local,type==exec.cachemount,type==source.git.checkout
Keep Duration: 48h0m0s
Keep Bytes: 488.3MiB
GC Policy rule#1:
All: false
Keep Duration: 1440h0m0s
Keep Bytes: 24.21GiB
GC Policy rule#2:
All: false
Keep Bytes: 24.21GiB
GC Policy rule#3:
All: true
Keep Bytes: 24.21GiB
- ビルダーの一覧を表示して、ステータスが
running
になっていることを確認します。
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
mybuilder * docker-container
mybuilder0 tcp://localhost:2375 running v0.12.3 linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
default docker
default default running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386, linux/arm64, linux/ppc64le, linux/s390x, linux/arm/v7, linux/arm/v6
- ビルダーのイメージとコンテナの一覧を確認します。
$ docker image ls | grep buildkit
moby/buildkit buildx-stable-1 ee33f441bff7 2 weeks ago 172MB
$ docker container ls | grep buildkit
0a171ebcd99e moby/buildkit:buildx-stable-1 "buildkitd" 6 days ago Up 2 minutes buildx_buildkit_mybuilder0
dockerイメージのビルドとプッシュ
x86とARMの両方に対応したイメージをビルドしてECRにプッシュします。
イメージのビルドと同時にプッシュをします。
これは、ローカルのdockerには自身のアーキテクチャに合ったイメージのみ保持することが可能で、マルチアーキテクチャイメージを持つことが出来ないためです。
- ECRに接続できるようにログインします。(ECRのコンソール画面にあるプッシュ方法に記載がある内容です)
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin {AWSのアカウントID}.dkr.ecr.ap-northeast-1.amazonaws.com
- ビルドしてECRにプッシュします。
$ cd {DockerFileのあるディレクトリ}
$ docker buildx build --platform linux/amd64,linux/arm64 -t {AWSのアカウントID}.dkr.ecr.ap-northeast-1.amazonaws.com/{ECRのレジストリ名}:{タグ名} --push .
- AWSのコンソールでECRのイメージ一覧を表示してプッシュを確認します。(
アーティファクト・タイプ
にimage index
で追加されていること)
5. 動作確認
ビルドしたイメージをプルして起動
上記の手順でビルドしたイメージはローカルには残っていないので、一度プルしてから起動する必要があります。
ECRにプッシュしたマルチアーキテクチャ対応イメージをプルします。
- ECRに接続できるようにログインします。(ECRのコンソール画面にあるプッシュ方法に記載がある内容です)
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin {AWSのアカウントID}.dkr.ecr.ap-northeast-1.amazonaws.com
- ECRからイメージをプルします。
$ docker pull {AWSのアカウントID}.dkr.ecr.ap-northeast-1.amazonaws.com/{ECRのレジストリ名}:{タグ名}
- 取得したイメージを起動します
$ docker run {AWSのアカウントID}.dkr.ecr.ap-northeast-1.amazonaws.com/{ECRのレジストリ名}:{タグ名}
6. 補足
マルチアーキテクチャイメージをプルして再度プッシュするとマルチアーキテクチャに対応しなくなる
マルチアーキテクチャイメージをプルすると、ローカルに保持されるイメージは使用しているPCのCPUアーキテクチャに合ったイメージのみとなります。
そのため、再度プッシュしてしまうとマルチアーキテクチャイメージではなくなってしまいます。
(例)
x86マシンでプルしたイメージを再度プッシュするとx86マシンでしか扱えないイメージとなる
ECRに登録済みのイメージにタグを追加
ECRに既に登録してあるイメージに別のタグを追加したい場合があると思います。
特定のアーキテクチャに対応したイメージであれば、プルした後に別のタグを設定してプッシュすることでタグを追加することが可能です。
しかし、マルチアーキテクチャイメージでは一度プルしてしまうとマルチアーキテクチャイメージではなくなるため、その手順では行えません。
そのため、ECRに登録されているイメージに対してはタグだけをリポジトリに直接設定する必要があります。
プルをせずにタグだけを追加する方法を記載します。
前提条件
- AWS CLIがインストール済みであること
手順
- 対象イメージのmanifest情報を
batch-get-image
コマンドを使用して取得します。
後の手順で使用するので変数に入れておきます。
$ MANIFEST=$(aws ecr batch-get-image --repository-name {ECRのリポジトリ名} --image-ids imageTag={対象のタグ名} --output text --query images[].imageManifest
$ echo $MANIFEST
- 追加するタグを
put-image
コマンドでタグを追加します。
$ aws ecr put-image --repository-name {ECRのリポジトリ名} --image-tag {新しいタグ名} --image-manifest "$MANIFEST"
-
describe-images
コマンドでリポジトリ情報を取得してタグが追加されたことを確認します。(AWSのコンソールからECRのイメージ一覧を確認してもいいです)
$ aws ecr describe-images --repository-name {ECRのリポジトリ名}
7. おわりに
オークファンではECRやECSなど、コンテナ技術を利用したシステム構築を行っています。
そんな中dockerは中心技術になります。
時代や環境に適応した形でキャッチアップを続けていたらと思っています。