アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

M1 Mac 上の Docker Desktop で Intel Mac で使ってきた Docker Compose を動かしてみる

morishitaです。

昨年、Macbook Pro が一新されて最新モデルは、Apple Silicon 搭載機、つまり M1 Mac のみとなりました。
2020 年に最初の M1 Mac が出たとき、 ARM 系 CPU に変わるということから私が思ったのは「開発環境で使っている Docker イメージが使えなくなるのでは?」ということでした。
実際、M1 上の Docker Desktop では x86_64 向けのイメージからコンテナを作成できます。
しかし、Intel CPU がエミュレートされるのでものによっては動かないものもあるようです。

アクトインディでは開発環境を Docker Compose により構築できるようにしています。
もちろん、Intel Mac で作成したので Intel Mac 前提になってしまっている部分もあると思います。
そのまますんなり動かないかもしれない。
でも、この先 Macbook Pro は ARM 系の Apple Silicon に置き換わっていく...。
すでに「開発マシンとして Intel Mac がほしいのだけど…、入手が困難」という話もちらほら聞きます。

そのうちなんとかしないといけなくなる。

ということで、とりあえず今回はいこレポの開発環境が M1 Mac でどこまで動くのか試してみました。

マシンスペック etc は次の通りです。

  • MacBook Pro(14 インチ、2021)
    • チップ Apple M1 Max
    • メモリ 64GB
    • OS macOS Montrey
  • Docker Desktop (v4.3.2)

いこレポの開発環境ってどんなの?

ざっくりこんなコンテナで構成されています。

  • MySQL
    • mysql:5.6 をベースに開発用データを含むイメージをx86-64 環境で別途ビルドしておりそれを利用
  • Nginx
    • Docker 公式イメージの nginx:alpine をそのまま利用
  • Rails
    • Ruby の Docker 公式イメージ ruby:2.6 をベースにビルド
    • マルチステージビルドで公式イメージ node:14 から Node.js をコピー
  • Webpack
    • Rails と同じイメージを利用して Webpack Dev Server を稼働

とりあえず、ビルドしてみた

まずはイメージをビルドしてみます。

$ docker-compose build

docker-compose で各自のローカル PC でビルドするのは Rails のコンテナだけです。

いくつかエラーが発生してすんなりとはビルドできませんでした。

EntryKit が動かない

最初に引っかかったのは Entrykit です。 いろいろ便利なので利用しています。

Dockerfile の一部を抜粋すると次の様にインストールしていました。

RUN wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz && \
    tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz && \
    rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz && \
    mv entrykit /bin/entrykit && \
    chmod +x /bin/entrykit && \
    entrykit --symlink

しかし、Docker イメージをビルドするとこの箇所で次のエラーが発生しビルドが失敗します。
entrykit --symlink でシンボリックリンクを作るときにエラーとなるようです。

runtime: failed to create new OS thread (have 2 already; errno=22)
fatal error: newosproc

うーむ、Platform: linux/arm64 を指定していてもすでにビルドされたものをコピーするだけではダメなようです。
しょうがないので、 Multi Stage ビルドで EntryKit を自前ビルドするようにして解決しました1

具体的には Dockerfile の先頭に次を追加して entrykit をビルドしました。

FROM golang:1.15 AS entrykit
RUN go get -v -ldflags "-s -w" github.com/progrium/entrykit/cmd

そして、次のコマンドでコピーします。

COPY --from=entrykit /go/bin/cmd /bin/entrykit
RUN chmod +x /bin/entrykit && \
    entrykit --symlink

これでビルドが通りました。 しかも arm64 でコンパイルできるので、Rails コンテナを x86_64 で動かさなくて良くなりました

mini_racer は 0.4.0 以上に

しばらくアップデートをサボっていたため当初 mini_racer 3.1.0 を使っていました。
そのバージョンをインストールしようとすると libv8 8.4.255.0 のインストールでエラーが発生しました。

mini_racer のリポジトリを確認してみると、0.4.0 で M1 mac に対応したようです。

github.com

最新の mini_racer 0.6.1 は問題なくインストールできました。

mini_racerassets:precompile の際に uglifier で利用される Javascript のランタイムですが、 いこレポの Docker イメージには Node.js をインストールしています。 したがって別途 JS のランタイムとして mini_racer をインストールする必要はありませんでした2。 これを機に削除してしまいました。


docker-compose up の前に引っかかったのは上記の2点くらいでした。

動かしてみる

インストール必要なものはすべて入れたので、いよいよアプリケーションを起動してみました。

docker-compose up を実行です。

linux/amd64 エミュレートでは ActiveSupport::EventedFileUpdateChecker が使えない

最初に docker-compose up をしたとき、次のエラーが発生して起動しませんでした。

Errno::ENOSYS: Function not implemented - Failed to initialize inotify

開発環境は development モードで動かし、コードの変更を即時反映するためファイル更新を検知する inotify が動かないようです。
ぐぐってみると M1 Mac 上で platform: linux/amd64 として動かしているコンテナで発生するようです3
最初 解決するには ActiveSupport::FileUpdateChecker を使うと良いとのことです。

その設定は config/environments/development.rb にあります。 次のように変更しました。

- config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+ config.file_watcher = ActiveSupport::FileUpdateChecker

これで、うまく起動しました。

ちなみに platform: arm64v8 として動くコンテナではこのエラーは発生しないようです。
最終的に platform: arm64v8 で動かしたのでこの変更は不要となりました。

そしてブラウザでローカルで動作するいこレポを表示できました。

動いちゃった

もっと苦労するのかと思いきや案外すんなり動いてくれました。

各コンテナのプラットフォームを確認してみます。

❯ docker-compose exec nginx uname -a
Linux 5d6f97bd4247 5.10.76-linuxkit #1 SMP PREEMPT Mon Nov 8 11:22:26 UTC 2021 aarch64 Linux

❯ docker-compose exec mysql uname -a
Linux bca189e84af8 5.10.76-linuxkit #1 SMP PREEMPT Mon Nov 8 11:22:26 UTC 2021 x86_64 GNU/Linux

❯ docker-compose exec rails uname -a
Linux 80107dd88966 5.10.76-linuxkit #1 SMP PREEMPT Mon Nov 8 11:22:26 UTC 2021 aarch64 GNU/Linux

❯ docker-compose exec webpack uname -a
Linux 2eefd0c62280 5.10.76-linuxkit #1 SMP PREEMPT Mon Nov 8 11:22:26 UTC 2021 aarch64 GNU/Linux

上記の通り MySQL 以外は aarch64 で動いています。
MySQL のコンテナだけ x86_64 で動いています。
x86_64 で動かさざるを得ないコンテナには platform: linux/amd64 をつけなきゃダメかもと思っていましたがそれも必要ありませんでした。

ブラウザでざっとサイトを一巡してみましたが、動作自体には問題なさそうです。
動作速度もこれまで使っていた Intel Mac で動かした場合と遜色ない感じです。
速くもなってないし、かといって遅くもなりませんでした。

あらあら、予定が狂ってしまった。
このエントリはもともと「M1 Mac で試してみたけどダメでした。でも Lima で解決!」というプロットで書こうと思っていたのに。
まあ、動かないより動くほうがいいので良かったのですが。

でも、せっかくなので次回のエントリでは Lima でも試したことを書こうと思います。

最後に

アクトインディではエンジニアを募集しています。 actindi.net


  1. entrykitがM1 Mac(Apple Silicone)で実行できないときの対処法 を参考にさせていただきました。

  2. uglifierexecjs を利用して Javascript のランタイムを呼び出しますが、execjs は Node.js があればそれを利用してくれます。

  3. Function not implemented - Failed to initialize inotify (Errno::ENOSYS) Apple M1 linux/amd64 platform · Issue #34 · evilmartians/terraforming-rails