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

こんにちは、@r_takaishi です。近所にスパイスカレーのお店ができてハッピーです。今回は、Reproのサーバーサイド開発環境におけるM1 Mac対応を改めて行ったので、やったことを紹介します。

なお、これまでの経緯は以下の通りです。

いつのまにかConfluent PlatformがM1 Mac上のDockerで動くようになっていた

Repro のサーバーサイド開発環境を M1 Mac に対応させるまでの道のり(撤退編) - Repro Tech Blog ではConfluent Platformのコンテナイメージがx86_64のものしかなく、arm64で起動しなかったということでM1Mac対応を断念しました。しかし、今回改めて試したところ、特に支障なくx86_64のコンテナイメージからKafkaなどが起動してきました。前回検証した時から数ヶ月経過しているので、もしかしたらDocker Desktopのバージョンアップによって起動するようになったのかもしれません。なお、今回検証で用いたDocker Desktopのバージョンは Version: 4.7.1 (77678) 以降です。

しかし、起動することと開発に使えるかは別の話です。パフォーマンスに問題があったり、他のコンテナから通信できない可能性もあります。そこで、別のコンテナからKafkaに対して大量にデータを送ってみて、M1 MacIntel Macで転送時間を比較しました。M1 MacIntel Macのスペックは以下の通りです。

また、Docker Desktopのバージョンや設定は以下で統一しました。

  • Docker Desktop
    • Version: 4.7.1 (77678)
    • CPUs: 4
    • メモリ: 9GB
    • スワップ: 1GB
    • Experimental Features
      • Enable VirtioFS

結果としてM1 MacIntel Macでほぼ差がなく、開発に支障はでないだろうと判断しました。

ChromeChrome-Driver

M1上で開発に使うコンテナは全て起動してきたのですが、これだけではまだ十分ではありません。テストにChromeChrome-Driverを使っているため、アーキテクチャに応じたものをインストールする必要があります。

しかし、Googleが提供しているバイナリにはarm64用のものがありません。そこでDebian Projectが提供しているchromiumchromium-driverを試してみたところ、動くようだったのでこれらを使うように変更しました。

MySQL

Repro のサーバーサイド開発環境を M1 Mac に対応させるまでの道のり - Repro Tech Blog ではMariaDBを使うようにしたのですが、mysql/mysql-server がarm64でも動いたので、そちらに切り替えました。MySQLのバージョンの都合でx86_64のイメージをarm64で動かすことになりますが、x86_64の環境とarm64の環境で同じイメージを使える方がメリットが大きいと考えました。

ちなみに、x86_64のmysql イメージはM1 Mac上で動かないのですが、起動時に gosu というGo製のツールを実行しようとしてエラーになっているようです。

repro-mysql-1  | runtime: failed to create new OS thread (have 2 already; errno=22)
repro-mysql-1  | fatal error: newosproc
repro-mysql-1  |
repro-mysql-1  | runtime stack:
repro-mysql-1  | runtime.throw(0x524da0, 0x9)
repro-mysql-1  |        /usr/local/go/src/runtime/panic.go:527 +0x90
repro-mysql-1  | runtime.newosproc(0xc82002a000, 0xc820039fc0)
repro-mysql-1  |        /usr/local/go/src/runtime/os1_linux.go:150 +0x1ab
repro-mysql-1  | runtime.newm(0x555ce8, 0x0)
repro-mysql-1  |        /usr/local/go/src/runtime/proc1.go:1105 +0x130
repro-mysql-1  | runtime.main.func1()
repro-mysql-1  |        /usr/local/go/src/runtime/proc.go:48 +0x2c
repro-mysql-1  | runtime.systemstack(0x5c4300)
repro-mysql-1  |        /usr/local/go/src/runtime/asm_amd64.s:262 +0x79
repro-mysql-1  | runtime.mstart()
repro-mysql-1  |        /usr/local/go/src/runtime/proc1.go:674
repro-mysql-1  |
repro-mysql-1  | goroutine 1 [running]:
repro-mysql-1  | runtime.systemstack_switch()
repro-mysql-1  |        /usr/local/go/src/runtime/asm_amd64.s:216 fp=0xc820024770 sp=0xc820024768
repro-mysql-1  | runtime.main()
repro-mysql-1  |        /usr/local/go/src/runtime/proc.go:49 +0x62 fp=0xc8200247c0 sp=0xc820024770
repro-mysql-1  | runtime.goexit()
repro-mysql-1  |        /usr/local/go/src/runtime/asm_amd64.s:1696 +0x1 fp=0xc8200247c8 sp=0xc8200247c0
repro-mysql-1 exited with code 2

M1向けの開発環境が壊れたら検知したい

ここまでやったことで、M1 Mac単体でReproのサーバーサイド開発が行えるようになりました。しかし、記事執筆時点の社内の開発環境はx86_64が大多数です。また、Macで開発しているメンバーがM1モデルに切り替えていったとしてもx86_64のLinuxで開発しているメンバーがいるため、x86_64とarm64の開発環境は混在し続けます。

これが意味することは、どちらか一方のアーキテクチャ上で開発している人が加えた変更によってもう一方のアーキテクチャ上の開発環境が壊れる可能性があるということです。CIでテストを実行しているのがx86_64のみなので、x86_64で加えた変更によってarm64の開発環境が壊れる可能性が高いと考えています( 前回断念したのもx86_64でのみ動くコンポーネントが追加された影響)。

となると、x86_64とarm64の両方で継続的にテストを実施することで開発環境が壊れたときに検知したくなります。PullRequest作成時のCIにおいて、複数アーキテクチャでテストできると理想的でしょう。しかし、試してみたところ現状のCIの仕組みだとうまく動きませんでした

現在、CIにはECS Fargateを使っています。一つのECS タスク内でテストに使うコンポーネントを起動し、タスクを並列に起動してテストを分散実行しています。ECS Fargateはarm64をサポートしており、Task DefinitionのruntimePlatformでアーキテクチャを指定することでarm64 コンテナイメージを動かすことができます(参考)。しかし、現時点ではテストを実行するためにはx86_64のイメージとarm64のイメージの両方を必要しており、x86_64のイメージはarm64のECS Fargateでは起動しませんでした。うーん、難しい。

これを解決するには、CIの仕組みをかなり大きく変える必要があるだろうと考えています。ECSを使ったままでもできそうですし、EKSを使うこともできそうです。継続的な開発環境の維持は今後の課題として検証したいですね。

まとめ

Reproのサーバーサイド開発環境をM1 Macに対応させました。今回、検証のためにMacbook Airを借りたのですが、評判通り非常によいマシンでした。ただ、Reproのサーバーサイドを開発するにはややスペック不足という印象を持ちました。特にIDEを使う場合はメモリがもう少しほしいですね。