2022年、開発機をIntel MacからM1 Macに切り替えました。開発体験は劇的に良くなりましたが、Docker周りで「プラットフォーム」を嫌でも意識させられることになりました。踏んだ地雷の記録です。
「手元では動くがビルドで落ちる」の正体
M1 Macに移行して最初に遭遇したのが、nokogiriのビルドエラーでした。
Error loading shared library ld-linux-aarch64.so.1: No such file or directory
原因は libc6-compat の不足。AlpineベースのDockerイメージでは、aarch64-linux向けのネイティブ拡張にこのパッケージが必要です。Intel時代には出なかったエラーで、M1で顕在化しました。
次に踏んだのがsqlite3のplatform mismatch。Gemfile.lockに sqlite3 (1.5.3-arm64-darwin) しかない状態でDockerビルドすると、コンテナ内(aarch64-linux)で動きません。
どちらも本質は同じで、 開発機と実行環境のアーキテクチャが違う という、Intel時代には意識しなくてよかった差異です。
bundle lock —add-platform という壁
sqlite3の問題は bundle lock --add-platform aarch64-linux で解決します。Gemfile.lockにLinux向けプラットフォームが追加され、Dockerビルド時に正しいバイナリが取得されるようになります。
PLATFORMS
aarch64-linux
arm64-darwin-21
実際には以下の4つを意識することになります。
aarch64-linux— Docker on M1arm64-darwin— Mac (M1)x86_64-linux— Docker on Intel / CI環境x86_64-darwin— Mac (Intel)
Bundler 2.2以降は bundle lock --add-platform で複数環境に対応できます。ただ、このコマンドの存在を知っているかどうかが分かれ目で、エラーメッセージから直接たどり着くのは少し遠回りでした。
RailsのDNSリバインディング対策に阻まれる
もう一つ引っかかったのがRailsの ActionDispatch::HostAuthorization です。
Dockerコンテナからホスト側のRailsにアクセスするには host.docker.internal を使いますが、Rails 6以降はDNSリバインディング対策として、許可されていないホスト名からのリクエストをブロックします。
Blocked host: host.docker.internal
解決策は config/environments/development.rb に1行追加するだけです。
config.hosts << "host.docker.internal"
セキュリティとしては正しい設計ですが、「自分のRailsに自分でアクセスしたいだけなのにブロックされる」のは地味にストレスでした。
M1移行で得た教訓
開発環境と実行環境のアーキテクチャが一致している という前提は、もう当たり前ではなくなりました。Intel時代はすべてx86_64で揃っていたので意識する場面が少なかったですが、ARM Macの登場でその前提が崩れています。
bundle lock --add-platform のように「知っているかどうか」で解決速度が大きく変わる問題が増えたのが、M1移行で一番面倒だったところです。
![[OpenTelemetry] Ruby(Rails) で ZipkinやJaegerを動かす](/tcard/51b78727-e3e4-4dca-9aaf-5200c566cc47/2022-08-23-51b78727-e3e4-4dca-9aaf-5200c566cc47.webp)

