はじめに
M2 Mac (Apple silicon: arm64 = aarc64 アーキテクチャ) で amd64 アーキテクチャ用のコンテナをビルドしようとしたところ、エラーが発生したので、その内容と対策方法を残しておきます
同じ Apple silicon である M1 Mac でも同様だと思われます
エラー内容
以前記事で紹介した AWS Copilot による Phoenix のデプロイを実行していました
Dockerfile は以下の内容です
FROM elixir:1.15.4
# hadolint ignore=DL3008
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y inotify-tools \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN mix local.rebar --force
COPY ./app /app
WORKDIR /app
RUN mix deps.get
RUN mix compile.phoenix
EXPOSE 4000
CMD ["mix", "phx.server"]
以前はいわゆる Intel Mac だったので、ローカルもデプロイ先も同じ amd64 = x86_64 アーキテクチャでしたが、先日導入した M2 Mac で同じ手順を実行したところ、コンテナのビルド中(copilot svc deploy
実行時)に以下のようなエラーが発生しました
...
=> ERROR [phoenix 6/7] RUN mix deps.get 2.9s
------
> [phoenix 6/7] RUN mix deps.get:
2.608 Segmentation fault (core dumped)
------
failed to solve: process "/bin/sh -c mix deps.get" did not complete successfully: exit code: 139
何度かやっていると、以下の内容になることもありました
...
=> ERROR [phoenix 6/7] RUN mix deps.get 3.2s
------
> [phoenix 6/7] RUN mix deps.get:
2.417 no next heap size found: 2305842975249271902, offset 0
2.422
2.422 Crash dump is being written to: erl_crash.dump...done
2.924 Aborted (core dumped)
------
failed to solve: process "/bin/sh -c mix deps.get" did not complete successfully: exit code: 134
同じ定義で Intel Mac でデプロイすると上手くいったので、これは明らかにアーキテクチャの違いによるものです
エラーの原因
AWS Copilot の manifest.yml は以下のようになっていました
...
cpu: 256
memory: 512
platform: linux/x86_64 # ここで x86_64 アーキテクチャの指定
count: 1
exec: true
network:
connect: true
...
この場合、 AWS Copilot の内部的な処理で以下のような Docker のビルドコマンドが実行されています
$ docker build \
-t xxx.dkr.ecr.ap-northeast-1.amazonaws.com/apb/xxx-svc:latest \
--platform linux/x86_64 \
--label com.aws.copilot.image.builder=copilot-cli \
--label com.aws.copilot.image.container.name=xxx-svc \
--label com.aws.copilot.image.version=v1.29.1 \
<カレントディレクトリー>
--platform linux/x86_64
で x86_64 アーキテクチャを指定していますね
Dockerfile の FROM
で指定している元イメージは elixir:1.15.4 です
OS/ARCH のところに複数のアーキテクチャが入っており、このイメージがマルチアークテクチャであることが分かります
マルチアーキテクチャの場合、 --platform
を指定しなければ、ビルドしている環境に合ったアーキテクチャが自動的に選択されます
M1 Mac や M2 Mac であればデフォルトで linux/arm64/v8 が使われます
今回のように --platform linux/x86_64
を指定すると linux/x86_64 が選択されます
それでも基本的には問題なくビルドが進みます
ただし、 mix deps.get
実行時にこのアーキテクチャの差異によってエラーが発生したものと思われます
エラーの対応策
Elixir Forum に対応策がありました
Dockerfile の mix deps.get
よりも前に以下のコードを追加することでエラーが発生しなくなります
ENV ERL_FLAGS="+JPperf true"
Erlang のドキュメントに JPperf
に関する情報が記載されていますが、正直なぜこれで上手くいくのかはよく分かっていません
分かる方はコメントいただけると助かります
まとめ
例えば Fly.io のようにコンテナのビルドをクラウド上で実行する場合、このような問題は発生しません
AWS Copilot を使う場合でも、まだ試していませんが Pipeline などを使えば大丈夫そうです
今回はローカルでも対策を取ることができましたが、やはりアーキテクチャの違いによる問題は今後も出てくると思うので、まだしばらくは Intel Mac も手元に必要そうですね