Repro のサーバーサイド開発環境を M1 Mac に対応させるまでの道のり

macOS Big Sur on M1 chip

Apple Silicon の時代が来る

Repro でサーバーサイドの開発をお手伝いしているうなすけと申します。

2021年10月19日に行われた Apple の新製品発表において、M1 チップを搭載した MacBook Pro が発表されました。この発表により、Intel チップを搭載した MacBook はラインナップから消え、今後は M1 チップ上で開発する機会が増えることは確実です。

ところで、私達の開発環境は M1 に対応しているのでしょうか? 社内の開発メンバーの大半は MacBook を使用しているので、もし対応していない場合、なるべく早く対応させないと新しい社内端末を購入できなかったりするので、結構影響が大きいです。

またインターネット上で、M1 Mac を使用している人々からの「全然発熱しない」だとか「ファンが回ることがない」とか「電池の持ちがすごい」とかの良い評判を目にします。このような恩恵を早く享受したいです。

M1 入手、早速 docker-compose up するも……

そんな折、社内で検証者の募集があったので応募し、M1 な MacBook Pro を手に入れることができました。

Chrome や Slack 等、必要なものを入れ終え、サーバーサイドの開発環境は Docker 化されているので Docker for Mac もインストールし、何はともあれ docker-compose up を実行してみると……動きませんでした。

覚悟はできていたので、早速やっていきましょう。

Entrykit が動かない

まず、Rails application のコンテナが起動しません。原因は Entrykit にありました。Entrykit が M1(Arm) に対応していない1ために Dockerfile 内の ENTRYPOINT の部分で起動に失敗していました。

ksss9.hatenablog.com

これについては、上記記事にもあるように、Shell script に置き換えられるものだったのでそうしました。

MySQL が動かない

まだ起動していないコンテナがあります。MySQL が起動していませんでした。

Official image としての MySQL docker image は、 linux/arm64 のものは提供されていません。これに関して、 docker-compose.yml に platform: linux/amd64 の指定をしても起動しませんでした。

実は、"the Docker Community and the MySQL Team" によってメンテナンスされている mysql image、つまり docker pull mysql として pull できるものは linux/amd64 しかありませんが、 "The MySQL team at Oracle" によってメンテナンスされている mysql/mysql-server 、つまり docker pull mysql/mysql-server として使用できる image については、 linux/arm64 の image が公開されています。

ですが、悲しいことにそれも MySQL 8以上であり、MySQL 5系は相変らず linux/amd64 な image しかありません。そして、Repro では MySQL 5系を使用しています。

ここは一旦諦め、開発環境でその違いが問題になることは恐らくないだろう、かつステージング環境において検証することができるだろうという判断をし、ローカルでは MariaDB を使うことにしました。

さて、「どのバージョンの MariaDB を使うのか」というのも、また問題になってきます。MariaDBMySQL との間の互換性については、公式に説明があります。

mariadb.com

ここで、index に使用できる文字列のサイズ制限2を考慮し、MariaDB 10.2を選択しました。

Rails が動く、けどめちゃくちゃ遅い

ここまでの過程を経て、ようやく Rails が起動し、db:migrate も通るようになりました。この段階で「とりあえず動く」ことに安堵し、同じく M1 Mac を持っていた開発メンバーにレビューをお願いしたところ「遅すぎて使いものにならない」と言われてしまいました!

この Rails アプリでは、development 環境において合計200弱の gem を install するようになっていました。どのくらい時間がかかっているのか計測したところ、9分30秒ほどかかっていました。特にnative extension の build に時間がかかっているように見えました。今まで使っていた Intel Mac (Core i9 2.4GHz) では1分半だったので、確かにこれは使いものになりません。

$ time bundle install -j4 --retry 6
...
real    9m39.292s
user    25m28.579s
sys     2m1.304s

毎回実行する類のコマンドではないにしても、これでは遅すぎます。またそれ以外にも、apt install が約1分、依存しているライブラリのビルドで約4分かかっており、言われてみれば時間がかかりすぎです。

実行されているコンテナを docker inspect した結果、 Architecture が amd64 となっていました。どうやら amd64 なコンテナとして動いてしまっており、QEMU を経由して動作している結果遅くなってしまっているようです。実際に、コンテナを起動している最中に ps aux をしたところ、QEMU が動いていることが確認できました。

$ docker inspect repro_rails | jq '.[0] | { Os: .Os, Architecture: .Architecture }'
{
  "Os": "linux",
  "Architecture": "amd64"
}

この Rails の Dockerfile では、FROM としていくつかの image で同じものを指定するため、社内でメンテナンスしている base image を指定していました。この image が Multi-arch な image ではなく amd64 の image しかないため、それを FROM とした結果、amd64 な環境のコンテナとして起動してしまっているようです。

これを、FROM に自分たちで管理している image ではなく、 Docker Hub で arm64 版が公開されている image を指定するところから書くことによって、無事に arm64 なコンテナを起動させることができました。bundle install も素早く終了するようになりました。

$ time bundle install -j4 --retry 6
...
real    1m21.249s
user    2m26.008s
sys     0m45.641s

これからやりたいこと

色々な壁を乗り越え、ようやく Repro のサーバーサイド開発環境を M1 上に構築することができました!

とはいえ、急ごしらえと言わざるをえない部分がまだ多いです。まず、社内で build している Docker image を、buildx や AWS Graviton2 instance などを活用して linux/arm64 な image を build するようにしたいです。現時点でも、Graviton2 instance で動いているコンポーネントが社内には存在しているので、流れに乗っていきたいですね。

また、まだ開発環境の対応を済ませたばかり3で、普段の開発がどのくらい変わるかということが実感できていません。SNS での評判を体感するのが楽しみです。

最後に、M1 対応を始めとして、まだまだやることが沢山ある Repro を良くしていくためのメンバーを募集しています!!

herp.careers


  1. 当初、起動時に fatal error: newosproc が出たとき、container が arm64 として動いているからなのではないかと思いました。そこで docker inspect をしてみたところ、OslinuxArchitectureamd64 となっていました。この状態であれば、Docker container 内は amd64 な環境になっているので Entrykit が動くのではないか? と思いました。実際 uname -m の結果も x86_64 となっていました。このような環境でなぜ ENTRYPOINT に指定した Entrykit が動かないのかはわかりませんでした……おそらく、https://docs.docker.com/desktop/mac/apple-silicon/#known-issues に記載のあるとおり “best effort” な対応ということなので、素直に amd64 な image を build するほうがよいのでしょう。

  2. innodb_large_prefix の設定について、これがない場合には 767 byte までのサイズの名前しか index に使用することができません。MySQL は5.7 以降、MariaDB は10.2以降でこの設定が不要になります。

  3. 記事執筆中に merge された変更によって、再び M1 上で一部コンポーネントが動かなくなってしまいました……