M2 Mac で amd64 アーキテクチャの Phoenix コンテナをビルドする

## はじめに

M2 Mac (Apple silicon: arm64 = aarc64 アーキテクチャ) で amd64 アーキテクチャ用のコンテナをビルドしようとしたところ、エラーが発生したので、その内容と対策方法を残しておきます

同じ Apple silicon である M1 Mac でも同様だと思われます

## エラー内容

以前記事で紹介した AWS Copilot による Phoenix のデプロイを実行していました

https://qiita.com/RyoWakabayashi/items/b831516ca9f012fb56b5

Dockerfile は以下の内容です

```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 は以下のようになっていました

```yml
...
cpu: 256
memory: 512
platform: linux/x86_64  # ここで x86_64 アーキテクチャの指定
count: 1
exec: true
network:
  connect: true
...
```

この場合、 AWS Copilot の内部的な処理で以下のような Docker のビルドコマンドが実行されています

```bash
$ 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 です

https://hub.docker.com/layers/library/elixir/1.15.4/images/sha256-1e44b2944d10e1eeda4ec8b04e4bce4b8cb90100a2fb3c7502d3e60bf35f81e8?context=explore

![スクリーンショット 2023-08-25 14.38.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/9fa7c755-4fb5-0a3c-11a6-8d56c3ae5a42.png)

OS/ARCH のところに複数のアーキテクチャが入っており、このイメージがマルチアークテクチャであることが分かります

マルチアーキテクチャの場合、 `--platform` を指定しなければ、ビルドしている環境に合ったアーキテクチャが自動的に選択されます

M1 Mac や M2 Mac であればデフォルトで　linux/arm64/v8 が使われます

今回のように `--platform linux/x86_64` を指定すると linux/x86_64 が選択されます

それでも基本的には問題なくビルドが進みます

ただし、 `mix deps.get` 実行時にこのアーキテクチャの差異によってエラーが発生したものと思われます

## エラーの対応策

Elixir Forum に対応策がありました

https://elixirforum.com/t/mix-deps-get-memory-explosion-when-doing-cross-platform-docker-build/57157

Dockerfile の `mix deps.get` よりも前に以下のコードを追加することでエラーが発生しなくなります

```Dockerfile
ENV ERL_FLAGS="+JPperf true"
```

Erlang のドキュメントに `JPperf` に関する情報が記載されていますが、正直なぜこれで上手くいくのかはよく分かっていません

https://www.erlang.org/doc/man/erl.html

分かる方はコメントいただけると助かります

## まとめ

例えば Fly.io のようにコンテナのビルドをクラウド上で実行する場合、このような問題は発生しません

AWS Copilot を使う場合でも、まだ試していませんが Pipeline などを使えば大丈夫そうです

https://aws.github.io/copilot-cli/ja/docs/concepts/pipelines/

今回はローカルでも対策を取ることができましたが、やはりアーキテクチャの違いによる問題は今後も出てくると思うので、まだしばらくは Intel Mac も手元に必要そうですね