M1 Mac時代のDockerクロスプラットフォーム開発で踏んだ地雷

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 M1
  • arm64-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移行で一番面倒だったところです。

Related Posts