Rails に限らず、 Ruby 製の WebFreamwork を Apple Silicon の Docker 上で使っていると下記のエラーに遭遇することがあります。
/var/www/bundle/ruby/3.0.0/gems/rb-inotify-0.10.1/lib/rb-inotify/notifier.rb:69:in `initialize': Function not implemented - Failed to initialize inotify (Errno::ENOSYS)
私の場合 M2 MacBook Air 上の Docker で middleman を使おうとしてこのエラーに遭遇しました。
だいぶ悩まされましたが、多分これで根本解決だという方法を見つけたので、メモとして残しておきます。
おそらくですが、 Rosetta2 がない環境でもこれで動くと思います。
追記
一晩明けて、上記エラーが出ない最小条件を探っていたのですが、どうも Ruby のイメージを arm64/v8 にするだけでいい様です(gem のインストール場所だけ解決する必要があります)。
でもまぁ一応調べたことは調べたことなので、削除はせずに残しておきます。
Ruby のイメージを arm64/v8 にする
上記からダウンロードした Ruby のイメージを使いましょう。
docker-compose.yml に platform を追加する
version: "3.9"
services:
middleman:
build: .
platform: linux/arm64/v8
command: bundle exec middleman server
volumes:
- ./:/var/www
ports:
- "4567:4567"
のように、 platform: linux/arm64/v8
を付け足しましょう。
rb-fsevent という gem のバージョンを 0.11.2 以上にする
この rb-fsevent という gem はバイナリを含んだ gem なのですが、そのバイナリが arm64 に対応するようにコンパイルされ直したのが 0.11.0 以上のようです。
だから多分 0.11.0 でも動くとは思うのですが、私が動作確認しているのは 0.11.2 なので章タイトルはそう書かせていただきました。
Bundle を arm64 用に設定する
$ bundle lock --add-platform aarch64-linux
このコマンドを実行すると、 Gemfile.lock に「今使ってるのは arm64 だよー」と書き込んでくれます。
Docker 上の bundle_path を永続化する
普段 rubylang/ruby の Docke Image を使っていると気づかないハマりどころなのですが、先ほど紹介した arm64/v8 をはじめとした Docker 公式をベースにした Docker Image は BUNDLE_PATH が /usr/local/bundle
に固定されている様です。
この設定を外すのも悪くはないのですが、個人的にはこういう設定をわざわざ入れてきているのならそれに沿った方がいいだろうと思ったので、パスの設定はそのまま永続化だけすることにしました。
方法は以下の通りです。
version: "3.9"
services:
middleman:
build: .
platform: linux/arm64/v8
command: bundle exec middleman server
volumes:
- ./:/var/www
- installed_gem:/usr/local/bundle
ports:
- "4567:4567"
volumes:
installed_gem:
上記のように volumes の設定を追加しましょう。
まとめ
ここまでやって、ちゃんと docker compose run --rm container_name bundle install
をしてからフレームワークを動かしてみると、冒頭のエラーは出なくなっているかと思います。
そもそもの Ruby のイメージを arm64/v8 にするというところが1番のキモだと思っています。
このメモが誰か他の人のお役に立てば幸いです。