Apple Silicon上でLinux向けx86_64の環境が欲しくなったり、x86_64向けのバイナリやイメージを作らないといけなくなる事態は、稀に良くあると思う。
colimaを使うと、Aarch64なMac上であっても、かなり楽にx86_64のLinux環境とDocker環境が手に入る。
最近のcolimaには実は --arch
というオプションがあり、x86_64又はaarch64であればあっさりと環境を作ることができるようになっている。
デフォルトで入ってくる環境と区別するため --profile
オプションを明示する必要がある。
$ colima --profile colima-x64 start --arch x86_64
colimaの作ったDockerインスタンスは colima list
で一覧できる。
$ colima list PROFILE STATUS ARCH CPUS MEMORY DISK default Running aarch64 2 2GiB 60GiB colima-x64 Running x86_64 2 2GiB 60GiB
--profile
を明示して colima start
を実行すれば、ホストの docker
コマンドでアクセスするインスタンスを切り替えられる*1。defaultに戻すときはオプションなしで colima start
を打つ。
$ colima --profile colima-x64 start $ docker info Client: Context: colima-colima-x64 Debug Mode: false Server: ... OSType: linux Architecture: x86_64 ...
実際どれくらい使えるの?
とは言え、これはAarch64の上でQEMUを経由してx86_64のCPUをエミュレートしているわけで、パフォーマンスがどれくらい出るかは気になる。
以下の条件で、Goのプロジェクトのコンパイル時間を比較してみた。
環境
ホスト: macOS Monterey 12.2.1(21D62)、Apple M1 Max(10コア)、64GB memory ゲスト: x86_64/aarch64共に、 CPU 2 、メモリ 2GiB (colimaが作成するインスタンス設定のデフォルト)
ビルドの方法
ビルドする対象としてはある程度の規模がある、ほぼ純粋なGoのプロジェクトが良いため、 gopher-lua を選んだ。
まず、apt等によるネットワーク状況の影響を最小限にするため、以下のDockerfileからbaseイメージを作成し、
FROM golang:1.18-bullseye RUN apt -y update && apt -y install git make python RUN mkdir /build && git clone https://github.com/yuin/gopher-lua /build/gopher-lua
これを元に以下のDockerfileのビルドが完了する時間を測定した*2。
FROM go-build-bench-base WORKDIR /build/gopher-lua RUN make glua CMD ["file", "/build/gopher-lua/glua"]
結果
x86_64 on Apple Silicon
$ docker build --no-cache -t go-build-bench . [+] Building 14.9s (7/7) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 37B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/go-build-bench-base:latest 0.0s => [1/3] FROM docker.io/library/go-build-bench-base 0.0s => CACHED [2/3] WORKDIR /build/gopher-lua 0.0s => [3/3] RUN make glua 14.1s => exporting to image 0.4s => => exporting layers 0.4s => => writing image sha256:1b34681df8e9a8db531586e52a37f2d39e5db52de3acb19d500838f6e5b6f166 0.0s => => naming to docker.io/library/go-build-bench 0.0s
aarch64 on Apple Silicon
$ docker build --no-cache -t go-build-bench . [+] Building 1.5s (7/7) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 37B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/go-build-bench-base:latest 0.0s => [1/3] FROM docker.io/library/go-build-bench-base 0.0s => CACHED [2/3] WORKDIR /build/gopher-lua 0.0s => [3/3] RUN make glua 1.4s => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:5f5da0ae77cb42e8c0378b73f964c10b662bfd7f4988c13c474c54c961590db3 0.0s => => naming to docker.io/library/go-build-bench 0.0s
結構歴然と差が...。
10倍近い差がついてしまうため、Goなどクロスコンパイル環境が整っているのならaarch64の上でx86_64のバイナリを作る方が速い*3。例えば以下のようにDockerfileを変えてビルドしたときの結果を残す。
FROM go-build-bench-base WORKDIR /build/gopher-lua ENV GOARCH amd64 RUN make glua CMD ["file", "/build/gopher-lua/glua"]
$ docker build --no-cache -t go-build-bench . [+] Building 4.4s (7/7) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 194B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/go-build-bench-base:latest 0.0s => [1/3] FROM docker.io/library/go-build-bench-base 0.0s => CACHED [2/3] WORKDIR /build/gopher-lua 0.0s => [3/3] RUN make glua 4.3s => exporting to image 0.1s => => exporting layers 0.1s => => writing image sha256:7bc11d676123ead4f72b6d3b34d0580571b14ae751d2b4e9f71005babac59df4 0.0s => => naming to docker.io/library/go-build-bench 0.0s
基本的に「本番バイナリはGitHub Actionで/Cloud Buildで/他〜」という運用にして確実かつ高速にx64サーバ上でビルドした方がよさそう。
が、そうは言っても手元で作れた方がいい... とか、色々な非常事態的場面もあるだろう、ということで残しておく。
追記
BuildX について教えてもらった。クロスプラットフォームではこっちを使う方が良さそう。いずれにせよCPUエミュレーションなどのオーバヘッドはかかりそうだが試したい。
追記2
あと、はてブにあった通り、Rosetta 2 ( arch
コマンド)経由で仮想環境を作れるのならばそちらも試したいが、やり方が見つからずという感じなので、有識者に教えてほしい。