Docker開発環境のM1 Mac対応

Docker開発環境のM1 Mac対応

SREチームの金澤です。

M1 Macが市場に出回り、社内でも業務用PCとして採用し始めました。

従来のMacと比べてもコストパフォーマンスが良く、大方問題なく動作しているため、今後も積極的に採用していきたいところですが、最もMacを利用している開発部では、Dockerが安定して動作することが採用の必須条件になります。

M1 MacはARMアーキテクチャのCPUを採用している関係で、Docker開発環境を問題なく動かすために、いくつかの対応が必要でしたので、今回その内容をブログにまとめます。

ランサーズ開発環境のM1 Mac対応

Docker for Macは、ARMアーキテクチャに対応中ですが、対応版はまだ正式リリースされていません。
※現時点では、3/26にリリースされたRC2版を利用しています。
https://docs.docker.com/docker-for-mac/apple-m1/

開発環境のM1 Mac対応

ランサーズの開発環境は、本番AWSでのサーバー構成をエミュレートした構成になっています。

リバースプロキシコンテナ(ELBコンテナ)

AWSのELBをエミュレートしたコンテナ。社内ではELBコンテナと呼んでいます。

今まで、リバースプロキシの処理にH2Oを利用していましたが、M1 Mac環境だとこの処理でタイムアウトしてしまいました。Appコンテナ等で採用しているNginxは正しく動作したので、H2OからNginxに変更するしたところ、正しく動作することを確認。

H2OがM1 Macで動かない原因の詳細までは調べ切れませんでしたが、取り急ぎワークアラウンドとしてNginxに変更することで対応しました。

Appコンテナ

ランサーズのAppコンテナは、x86_64環境でビルドしたコンテナをAWS ECRにpushし、それを開発者がpullして利用する形をとっています。

pullしたコンテナは、M1 Mac上でも問題なく動作しました。

MySQLコンテナ

ランサーズのMySQL5.7コンテナは、x86_64環境でデータをインポートしたものを、同様にAWS ECRにpushしています。

このコンテナも同様に、問題なく動作しました。

他開発環境のM1 Mac対応

ランサーズ以外のサービスについては、開発環境、本番環境共にDockerで運用しています。
ランサーズ開発環境と違い、ECRからpullせず、すべてのコンテナをPC上でビルドしていました。

Appコンテナ

一部のサービスでビルドに失敗しました。

% docker-compose up -d 
Docker Compose is now in the Docker CLI, try `docker compose up` 

Building app [+] Building 68.3s (10/39) => [internal] load build definition from Dockerfile 

... ------ > 
[ 6/35] RUN apt-get update -y && apt-get upgrade -y && apt-get install -y --no-install-recommends bash build-essential default-mysql-client git libcurl4-openssl-dev libghc-yaml-dev libqt5webkit5-dev libxml2-dev libxslt-dev libyaml-dev linux-headers-amd64 locales nginx nodejs openssl python3-pip ruby-dev ruby-json ssh sudo supervisor tzdata vim yarn zlib1g-dev && apt-get clean -y && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/*: #9 0.223 Hit:1 http://deb.debian.org/debian buster InRelease #9 0.223 Hit:2 http://security.debian.org/debian-security buster/updates InRelease 
#9 0.265 Hit:3 https://deb.nodesource.com/node_12.x buster InRelease ... 
#9 17.04 This may mean that the package is missing, has been obsoleted, or 
#9 17.04 is only available from another source #9 17.04 
#9 17.07 E: Package 'linux-headers-amd64' has no installation candidate 
%

原因は、Dockerfileで以下のパッケージををインストールしていた箇所です。

linux-header-amd64

linux-headerについては、アーキテクチャ別にパッケージが用意されています。
M1 Mac上では、

linux-header-arm64

をインストールする必要があります。

M1 Macの場合はarm64版をインストールするために、以下のようにしました。

linux-headers-$(if [ $(uname -m) = "aarch64" ]; then echo arm64; else echo amd64; fi)

※ちなみに、原因調査中に、以下の指定による解決方法も見つかりましたが、どちらも正しくインストールできませんでした。

linux-header-$(uname -a) 
linux-header-generic

MySQLコンテナ

ランサーズで利用しているMySQLのバージョンは5.7です。
AWSで利用しているRDS AuroraがMySQL5.7ベースなので、開発環境もそれに合わせたものを利用しています。

ところが、オフィシャルで提供しているMySQL5.7コンテナはArm64をサポートしていません。
(MySQL8.0はサポートしている)

そこで、以下の解決方法を考えました。

  1. x86_64環境でビルドしたコンテナをECRにpushし、それをpullして利用する
    ・ランサーズのMySQLコンテナが正しく動作することを確認済
  2. MySQL5.7に相当するMariaDBをインストールする

2.については、Maria DBでJSON型をサポートしていない点が、互換性の面において問題となる可能性が高いと判断し、見送りました。
1.については、ランサーズ開発環境で問題なく動作することを確認できていました。
x86_64環境でDockerfileをビルドしたMySQLコンテナをECRにpushしておき、M1 Mac環境でそれをpullして使うことは可能です。

今回、1. の方法を選択し、正しく動作することを確認しました。

おまけ:AWS Graviton環境での動作検証

ランサーズでは、PC上で動作するDocker開発環境に加え、開発中の機能を検証するためのBackStage環境をAWS環境に構築しています。
EC2インスタンスにDockerをインストールし、PC開発環境と同じDockerコンテナで動作させる仕組みです。

今まで、この環境をt3a.mediumインスタンスで運用していましたが、Arm64に対応したため、AWS Graviton環境のt4g.mediumでも動くか検証してみました。

結果、正常に動いたのはRedisコンテナと、ELBコンテナのみでした。

Redisコンテナは、Arm64にも対応したマルチアーキテクチャのコンテナで、ELBコンテナも、マルチアーキテクチャのalphineコンテナをベースにビルドする形にしていたため動きましたが、それ以外のコンテナは起動できませんでした。

Docker for Macでは、M1 Macでもx86アーキテクチャのコンテナが動くように設計されていますが、純粋なArm64のLinux環境ではx86アーキテクチャのコンテナは、(予想通りでありましたが)動かないことを確認できました。

(これらを動くようにするには、Docker Buildのフェーズで、もう少し細かい配慮が必要そうです)

さいごに

今回、弊社開発環境で対応が必要だったことは、以下の3点でした。

・H2Oではなく、Nginxを使う
・linux-headerのインストールをArm64にも対応させる
・MySQL5.7はx86_64環境でビルドしたものをECRにpushしておき、それを使う

多少苦労しましたが、M1 Macで動作させることができたことにより、今後、社内でもM1 Macの導入が進んでいきそうです。

2000年代中盤以降、PC市場、およびサーバー市場がx86_64にほぼ統一されていたため、CPUアーキテクチャの違いを考慮するという状況があまり発生しませんでした。

これはある意味幸せな状態だったのですが、そこに競争力のある新しいアーキテクチャのCPUが、優れたコストパフォーマンスでPC、およびサーバー市場に食い込み始めてきたというのが最近の状況かと思います。

インフラ屋にとっては、以前よりも面倒な状態にまたなりますが、競争により、より優れた製品が市場に投入されていくこと自体は良いことだと思います。

今後は、Dockerコンテナやgolangのバイナリで、複数のCPUアーキテクチャを考慮する必要がある程度出てくると思います。

ソースからビルドする機会も多少増えるかも知れませんね。