M1とDocker for Macの組み合わせが遅い1。遅すぎる。ただでさえDocker for Macは遅いのにM1と組み合わせることで更に悪化する。大枚はたいて買ったM1 Proでアプリケーションのベンチマークとったら (3年前の) Intel Macbook Proの構成より遅くて衝撃を受けた。そしてDocker for Macを削除した。
検討したツール
dockerがないと仕事中やることないので代替ツールを探した。
ツール名 | 仕組み | メリット | デメリット | Watch | Star | Issues |
---|---|---|---|---|---|---|
Podman | デーモンを必要とせず、コンテナを安全に稼働させることができる。 | インストールが容易。 | 一部のdockerコマンド (networkなど) をサポートしていない。 | 178 | 11.2k | 190 |
Minikube | HyperkitやVirtualBoxの中で動かす (別途セットアップが必要)。 | Kubernetesを動かすことができる。 | KubernetesはDockerを非推奨化してるので将来的には使えなくなるかも。 | 468 | 22.5k | 607 |
Multipass | デフォルトでHyperKit、QEMU/KVMなどのハイパーバイザー経由で起動できる。 | クロスプラットフォーム (Linux/Mac/Windows) に対応。 | (未調査) | 77 | 3.8k | 327 |
Lima | Docker CLI + Docker REST APIでLima (VM) 上のDocker Engineを操作する。 | Docker for Macに近い構成。 | セットアップに少し手間がかかる。 | 48 | 5.1k | 62 |
今困っているのはローカル開発環境、かつ従来とセットアップ構成をなるべく変えたくないという理由から、比較的実績のあるLimaを検証対象とした。
調査
セットアップ方法は割愛。検証環境は次の通り。
- Docker Desktop 4.2.0
- Docker version 20.10.10
- 検証マシン
- MacBook Pro 2019 16GB RAM 8 Intel Core i9 2.4Ghz (16インチ)
- MacBook Pro 2021 16GB RAM 10 M1 Pro 10 Core (14インチ)
ネイティブ実行
普段からよく使うTerraformのplan実行速度を比較した。操作対象リソースはAmazon ECSのサービス構築。評価対象リソースは約30ほど。
まずはネイティブでplan実行した結果。
- Intel Core i9
- 9.761s
- Apple M1 Pro
- 9.302s
僅差だけどこれは想定内。
Docker for Mac
次に hashicorp/terraform を利用してDocker for Macからplanを実行。
- Intel Core i9
- 13.898s
- Apple M1 Pro
- 33.557s
遅い。特にM1。
ただしこれには理由があって、hashicorpが公開しているイメージは現状AMD64版しかないため、Dockerビルド時に --platform linux/amd64
を指定してエミュレートした。
試しに次のようなDockerfileを書いて自前ビルド (ARMビルド) したら15.980sまで短くできた。それでもIntelより遅い。
FROM golang:alpine
ENV TERRAFORM_VERSION=1.0.11
RUN apk add --update git bash openssh
ENV TF_DEV=true
ENV TF_RELEASE=true
WORKDIR $GOPATH/src/github.com/hashicorp/terraform
RUN git clone https://github.com/hashicorp/terraform.git ./ && \
git checkout v${TERRAFORM_VERSION} && \
/bin/bash scripts/build.sh
WORKDIR $GOPATH
ENTRYPOINT ["terraform"]
Limaで実行
結果。
- Intel Core i9
- 13.780s
- Apple M1 Pro
- 9.733s
Intel版はDocker for Macとさほど実行速度は変わらないけど、M1 Proだとネイティブに近い速度までパフォーマンスが改善した。
後でググったら同じようなことが Improve Docker performance on macOS by 20x に書かれてた。"I ran benchmarks so you don’t have to." と書かれてる。車輪の再発明感。
結果をまとめると下表のようになる。
実行方法 | CPU | Docker platform | Total |
---|---|---|---|
Native | Intel Core i9 | 9.761s | |
Apple M1 Pro | 9.302s | ||
Docker for Mac | Intel Core i9 | amd64 | 13.898s |
Apple M1 Pro | amd64 | 33.557s | |
Apple M1 Pro | arm64 | 15.980s | |
Lima | Intel Core i9 | amd64 | 13.780s |
Apple M1 Pro | arm64 | 9.733s |
今回の検証ではDocker for Macの比較でIntelよりM1の方が1.14倍遅い結果に 2。
以前よりDocker for MacはファイルI/Oの問題が指摘されてたが、M1版はエミュレータにQemuが使用されているため余計遅くなってるという話も出てる 3。
M1 was running arm images, but as far as I know Docker Desktop on M1 use Qemu which is super slow anyway.
今回はLimaで検証したが、恐らく他のツールでも同様の結果になると思われる。
結論
brew uninstall docker --cask
トラブルシューティング
Limaを構築する上でハマったポイント。
Lima起動時にFATA[0075] exiting, status={Running:false Degraded:false Exiting:true Errors:[] SSHLocalPort:0} (hint: see "/Users/xxx/.lima/default/ha.stderr.log")エラー
/Users/xxx/.lima/default/ha.stderr.log
を確認すると、Could not set up host forwarding rule 'tcp:127.0.0.1:60022-:22
というエラーが残っていた。ポート関連かと思い使用中のポート一覧を確認するとqemuが利用していた。
恐らくLimaの検証で起動や削除を繰り返していて残骸が残っていたものと思われる。
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
qemu-syst 10256 naomichi 19u IPv4 0xc863156b9180ca1d 0t0 TCP localhost:60022 (LISTEN)
qemu-syst 10256 naomichi 24u IPv4 0xc863156b9180f4dd 0t0 TCP localhost:60022->localhost:50701 (ESTABLISHED)
qemu-syst 10256 naomichi 64u IPv4 0xc863156b95a34a1d 0t0 TCP localhost:60022->localhost:59842 (ESTABLISHED)
ssh 10268 naomichi 3u IPv4 0xc863156b91815f5d 0t0 TCP localhost:50701->localhost:60022 (ESTABLISHED)
ssh 31247 naomichi 3u IPv4 0xc863156b959fd4bd 0t0 TCP localhost:59842->localhost:60022 (ESTABLISHED)
対象プロセスをKillして解決した。
Docker実行時にUnable to write the module manifest file: open xxx: read-only file systemエラー
マウントしたディレクトリはデフォルトで読み取り専用になる。Lima設定ファイルの mounts
に書き込み可能なパスを追加して再起動すれば良い。
mounts:
- location: "/foo/bar"
writable: true
Docker実行時にerror getting credentials - err: exec: "docker-credential-desktop": executable file not found in $PATH, out: `` エラー
~/.docker/config.json
を削除。
$ rm ~/.docker/config.json
ネットワーク通信が不安定になる
コンテナの内部からインターネットへの通信 (APIの呼び出しなど) を繰り返してると、通信が不安定になることがあった。LimaはデフォルトでホストのDNSを参照するが、明示的にDNSを指定することで安定した。
useHostResolver: false
dns:
- 1.1.1.1
- 1.0.0.1
ソケット通信を行うアプリケーションが起動しない
Rails起動時に Errno::EPERM: Operation not permitted - connect(2) for /app/tmp/sockets/puma.sock
といったエラーが発生した。どうやら現時点 (2021年11月現在) ボリュームマウントで共有したディレクトリ内でsockファイルは配置できない模様。
puma.sock
ファイルをマウント領域の外に配置することでRailsを起動することができた。
# /appはマウントディレクトリに指定しており、起動しようとするとエラーが出る
# bind "unix:///app/tmp/sockets/puma.sock"
bind "unix:///var/run/puma.sock"
ホスト側のファイル変更がコンテナに反映されるのに時間がかる
バインドマウントでホスト側のディレクトリをコンテナと共有する際、ホスト側で変更した変更がコンテナへ反映されるまでに1-2秒かかることがある。Limaはファイル同期の仕組みにreverse sshfsが使われており、これが問題とされてる。今後9pかsambaへの移行が検討されており、現状は未解決。