【Next.js】Failed to load SWC binary for linux/arm64の解消法【Docker】


### エラーが発生した状況
DockerでNext.jsの環境を構築し、開発を行なっていました。
ホスト側で`yarn add`をした後に`docker compose up`したところ`Failed to load SWC binary for linux/arm64`となりました。

```:ターミナル
$ yarn add @mui/material @mui/styled-engine-sc styled-components

$ docker compose up
[+] Running 1/0
 ⠿ Container next-front-1  Crea...                          0.0s
Attaching to next-front-1
next-front-1  | yarn run v1.22.18
next-front-1  | $ next dev
next-front-1  | ready - started server on 0.0.0.0:3000, url: http://localhost:3000
next-front-1  | error - Failed to load SWC binary for linux/arm64, see more info here: https://nextjs.org/docs/messages/failed-loading-swc
next-front-1  | info  - Attempted to load @next/swc-linux-arm64-gnu, but it was not installed
next-front-1  | info  - Attempted to load @next/swc-linux-arm64-musl, but it was not installed
next-front-1  | info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
next-front-1  | error Command failed with exit code 1.
next-front-1 exited with code 1

```

### 原因
ホスト側で`yarn add`などをしてしまうと`swc-linux-arm64~`がなくなり、`Failed to load SWC binary for linux/arm64`となります。

確認したところ、ホスト側で`yarn add`をしたことで`node_modules/.yarn-integrity`内の`"systemParams"`が`"linux-arm64-93"`から`darwin-arm64-93`に変更されていました。
下の画像左がホスト側で`yarn add`した後の`.yarn-integrity`で右がする前の`.yarn-integrity`です。
![スクリーンショット 2022-04-23 19.17.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/2827f922-a319-77e7-d2e6-b7769ed95a0c.png)

また`node_modules`内の`@next`を確認すると上記と同様に`swc-linux-arm64~`が`swc-darwin-arm64`に置き換わっていました。

ホスト側で`yarn add`する前（変更前）
![スクリーンショット 2022-04-23 22.01.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/e9345fbd-5e18-6c98-b24d-1668b4037884.png)

ホスト側で`yarn add`した後（変更後）
![スクリーンショット 2022-04-23 22.00.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/8fe6d274-76fa-ac0d-28a0-47fbc31de989.png)

なお`package.json`及び`yarn.lock`には`yarn add`で追加されたもの以外の変更箇所はないようでした。


ホスト側及びコンテナ側のOS名とアーキテクチャを確認すると以下のとおりでした（M1 macを使用しています）。
```:ターミナル
$ uname -s -m
Darwin arm64

$ docker compose run サービス名 uname -s -m
Linux aarch64
```
OSが異なっていることがわかります。

`arm64`及び`aarch64`については以下のページをご参照ください。

https://e-words.jp/w/ARM64.html


ホスト側で`yarn add`したことでホスト側のOSなどに基づいて`swc-darwin-arm64`がインストールされ、`swc-linux-arm64~`が`swc-darwin-arm64`に置き換わります。
この変更が`docker compose up`した際にコンテナ内に反映されます。
するとコンテナ内の環境で必要な`swc-linux-arm64~`が存在しないため、`Failed to load SWC binary for linux/arm64`となります。

### 解決方法
ホスト側で`yarn add`する前の状態に再度変更するか、上記の変更がコンテナ内に加わらないようにする必要があります。

#### コンテナ側で`yarn install`
コンテナ内で`yarn install`することでホスト側で`yarn add`する前の状態に戻ります。
具体的には以下の通りです。

```:ターミナル
$ docker compose run サービス名 yarn install
yarn install v1.22.18
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
Done in 121.88s.

$ docker compose up
[+] Running 1/0
 ⠿ Container next-front-1  Crea...                          0.0s
Attaching to next-front-1
next-front-1  | yarn run v1.22.18
next-front-1  | $ next dev
next-front-1  | ready - started server on 0.0.0.0:3000, url: http://localhost:3000
next-front-1  | wait  - compiling...
next-front-1  | event - compiled client and server successfully in 2.1s (124 modules)
```
`docker compose run サービス名 yarn install`でコンテナ側で `node_modules`が作成され、その変更がホスト側にも反映されます。
よってその後に`docker compose up`すると問題なく起動します。

#### `volume`を利用する
`node_modules`に対してボリュームを設定し、ホスト側の`node_modules`に対する変更がコンテナ内に反映されないようにします。
`docker-compose.yml`の`volumes`を以下のように記述します。

```docker-compose.yml
version: '3.9'
services:
  front:
    build: .
    volumes:
      - .:/myapp
      - node_modules:/myapp/node_modules
    environment:
      NODE_ENV: development
    ports:
      - 3000:3000
volumes:
  node_modules:
```
上記では名前付きボリュームを利用していますが、匿名ボリュームでも可です。
匿名ボリュームの場合には以下のようになります。
```docker-compose.yml
version: '3.9'
services:
  front:
    build: .
    volumes:
      - .:/myapp
      - /myapp/node_modules
    environment:
      NODE_ENV: development
    ports:
      - 3000:3000
```

より詳細な説明を以下に記述しました。

https://qiita.com/P-man_Brown/items/c2a69d943853cb381fbe


#### ホスト側で`yarn install`不可にする
ホスト側で`yarn install`や`yarn add`を実行することでエラーが発生するため、これらを実行できなくすることで予防することができます。
実行不可にする方法については以下のページに記載しています。

https://qiita.com/P-man_Brown/items/77b31fed33cc59f43ace


### npmの場合
npmを使用した場合についても確認しました。
`create-next-app`実行時に`--use-npm`オプションを付けることでnpmを使用することができます。

結果、npmの場合にはホスト側で`npm install`を実行しても`node_modules`内`@next`の`swc-linux-arm64~`が`swc-darwin-arm64`に変更されることはなく、`swc-linux-arm64~`と`swc-darwin-arm64`の両方が存在するようになりました。

ホスト側で`npm install`する前
![スクリーンショット 2022-04-30 12.13.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/53dad626-1a6b-64e2-7324-431cf08f97c8.png)

ホスト側で`npm install`した後
![スクリーンショット 2022-04-30 12.13.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/0de97d12-77e3-8ea4-02de-f2f737adff0c.png)

そのため、ホスト側で`npm install`をしても`Failed to load SWC binary for linux/arm64`は発生しません。

ただし、`node_modules`が存在しない状態でホスト側で`npm install`を実行すると`swc-darwin-arm64`だけがインストールされ、`docker compose up`すると`Failed to load SWC binary for linux/arm64`が発生します。

`node_modules`が存在しない状態でホスト側で`npm install`した後
![スクリーンショット 2022-04-30 12.07.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/2578168e-7954-b985-8307-15c864ddb62b.png)
ホスト側で`npm ci`した場合も`node_modules`が再生成されるため上記と同様になります。

「ホスト側で`npm install`する前」の状態に戻すためには、コンテナ側で`npm ci`を実行します。
具体的には以下のコマンドを実行します。
```:ターミナル
$ docker compose run <サービス名> npm ci
```
`npm install`では、`swc-linux-arm64~`は追加されエラーは解消されるものの、`node_modules`が再生成されるわけではないため、`swc-darwin-arm64`は存在せず`swc-linux-arm64~`のみ存在する状態にならないことがあります。

### 参考になるページ

https://github.com/vercel/next.js/discussions/30468

https://nextjs.org/docs/messages/failed-loading-swc