M1MacのDockerでyarn installがエラーになる件について

# はじめに
前回書いた記事[「既存RailsアプリをDocker化する手順」](https://qiita.com/snow_swallow/items/31e6bb582123a58676e3)でDocker環境を構築する過程で、
`yarn install`時でM1チップ特有のエラーが発生しました。
今回は備忘録も兼ねエラーについて簡単にまとめていきたいと思います。
※下記記事ではエラーが発生しないよう対応済みです。

https://qiita.com/snow_swallow/items/31e6bb582123a58676e3

# 開発環境
下記開発環境にてDocker化を行いました。
- CPU: Apple M1
- OS: MacOS Ventura バージョン13.3.1

# 各種設定
Docker化するにあたり、使用したバージョンは以下の通りです。
- ruby: 3.0.2
- rails: 6.0.3
- node: 15.0.1
- postgresql: 12.0

また、Dockerfileとdocker-compose.ymlの設定は以下の通りです。
```Dockerfile:Dockerfile
FROM node:15.0.1 as node
FROM ruby:3.0.2

COPY --from=node /opt/yarn-* /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/
COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/
RUN ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
    && ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npx \
    && ln -fs /usr/local/lib/node /usr/local/bin/nodejs \
    && ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn \
    && ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarnpkg

RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    postgresql-client

RUN mkdir /myapp
WORKDIR /myapp

COPY package.json /myapp/package.json
COPY yarn.lock  /myapp/yarn.lock
RUN yarn install

COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install

COPY . /myapp
```

```yml:docker-compose.yml
version: '3'

services:
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0' &&
    ports: 
      - '3000:3000'
    volumes:
      - .:/myapp
    environment:
      - 'DATABASE_PASSWORD=postgres'
    tty: true
    stdin_open: true
    depends_on:
      - 'db'

  db:
    image: postgres:12.0
    environment:
      - POSTGRES_PASSWORD=password
```

# エラー内容
上記Dokcerfileとdocker-compose.ymlの設定で、`docker-compose build`を実行すると、
下記の通り、`yarn install`でエラーが発生します。
内容を見るに、`/lib/ld-linux-armhf.so.3`というファイルまたはディレクトリがないと
言っている模様。

```zsh:terminal
=> ERROR [stage-1 11/15] RUN yarn install                                 0.2s
------
 > [stage-1 11/15] RUN yarn install:
#0 0.165 qemu-arm: Could not open '/lib/ld-linux-armhf.so.3': No such file or directory
------
failed to solve: process "/bin/sh -c yarn install" did not complete successfully: exit code: 255
```

# 解決方法
エラー内容を調べていると、どうやらアーキテクチャに問題があることが判明しました。
Dockerfileで指定した、`node:15.0.1`をDocker hubで調べてみると、
下図赤枠部分の通り、M1のアーキテクチャである`linux/arm/v8`には対応していない模様。

![スクリーンショット 2023-06-04 21.14.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3326710/28c3fe4c-4a3e-018c-a8bd-6e83f3817377.png)

よって、nodeのバージョンを以下の通り、`linux/arm/v8`に対応したバージョンを選択し、
再度`docker-compose build`を実行します。
(※最新のバージョンを選択すると別のエラーが発生したので、今回はVer.16.13を選択)

```diff_docker:Dockerfile
- FROM node:15.0.1 as node
+ FROM node:16.13 as node
```

どうやら、無事にインストールできた模様。

```zsh:terminal
=> [stage-1  2/15] COPY --from=node /opt/yarn-* /opt/yarn                 0.0s
 => [stage-1  3/15] COPY --from=node /usr/local/bin/node /usr/local/bin/   0.1s
 => [stage-1  4/15] COPY --from=node /usr/local/lib/node_modules/ /usr/lo  0.2s
 => [stage-1  5/15] RUN ln -fs /usr/local/lib/node_modules/npm/bin/npm-cl  0.2s
 => [stage-1  6/15] RUN apt-get update && apt-get install -y     build-es  6.2s
 => [stage-1  7/15] RUN mkdir /myapp                                       0.2s
 => [stage-1  8/15] WORKDIR /myapp                                         0.0s
 => [stage-1  9/15] COPY package.json /myapp/package.json                  0.0s
 => [stage-1 10/15] COPY yarn.lock  /myapp/yarn.lock                       0.0s
 => [stage-1 11/15] RUN yarn install                                      19.5s
 => [stage-1 12/15] COPY Gemfile /myapp/Gemfile                            0.0s
 => [stage-1 13/15] COPY Gemfile.lock /myapp/Gemfile.lock                  0.0s
 => [stage-1 14/15] RUN bundle install                                    60.9s
 => [stage-1 15/15] COPY . /myapp                                          2.0s
 => exporting to image                                                     2.2s
 => => exporting layers                                                    2.2s
```

あとは、`docker-compose run web rails db:create db:migrate`と
`docker-compose up`を実行することで、railsアプリを無事に起動することができました🎉

## 参考
アーキテクチャを`linux/arm/v8`に対応したものに変更する方法以外に、
`platform`を指定する方法でもエラーを解消することができます。
以下の通り、`docker-compose.yml`に`platform: linux/amd64`を追記し、
`docker-compose build`を実行します。
`platform: linux/amd64`によって、アーキテクチャをamd64に設定しています。

```diff_yaml:docker-compose.yml
services:
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0' &&
    ports: 
      - '3000:3000'
    volumes:
      - .:/myapp
+   platform: linux/amd64
    environment:
      - 'DATABASE_PASSWORD=postgres'
    tty: true
    stdin_open: true
    depends_on:
      - 'db'
```


10分〜15分ほど待つと無事ビルドが完了します。
(bundle installが長く、自分は途中でインストールが失敗しているものと勘違いしていました)

```zsh:terminal
=> [stage-1  2/15] COPY --from=node /opt/yarn-* /opt/yarn                 0.0s
 => [stage-1  3/15] COPY --from=node /usr/local/bin/node /usr/local/bin/   0.1s
 => [stage-1  4/15] COPY --from=node /usr/local/lib/node_modules/ /usr/lo  0.2s
 => [stage-1  5/15] RUN ln -fs /usr/local/lib/node_modules/npm/bin/npm-cl  0.3s
 => [stage-1  6/15] RUN apt-get update && apt-get install -y     build-e  36.8s
 => [stage-1  7/15] RUN mkdir /myapp                                       0.3s
 => [stage-1  8/15] WORKDIR /myapp                                         0.0s
 => [stage-1  9/15] COPY package.json /myapp/package.json                  0.0s
 => [stage-1 10/15] COPY yarn.lock  /myapp/yarn.lock                       0.0s
 => [stage-1 11/15] RUN yarn install                                      71.2s
 => [stage-1 12/15] COPY Gemfile /myapp/Gemfile                            0.0s
 => [stage-1 13/15] COPY Gemfile.lock /myapp/Gemfile.lock                  0.0s
 => [stage-1 14/15] RUN bundle install                                   565.5s
 => [stage-1 15/15] COPY . /myapp                                          3.3s
 => exporting to image                                                     2.8s
 => => exporting layers                                                    2.8s
```

このまま`docker-compose run web rails db:create db:migrate`を実行すると、
下記エラーが発生するため、`config/environment/development.rb`を
以下の通り編集します。

```zsh:terminal
rails aborted!
Errno::ENOSYS: Function not implemented - Failed to initialize inotify
```
```diff_ruby:config/environment/development.rb
-  config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+  config.file_watcher = ActiveSupport::FileUpdateChecker
```
そして、`docker-compose run web rails db:create db:migrate`と
`docker-compose up`を実行することで、railsアプリを起動することができます。

# まとめ
最後に本記事のまとめです。
- M1MacでDockerを使用する際は、使用するパッケージが`linux/arm/v8`に対応しているか確認する
- アーキテクチャを`linux/amd64`指定することで、エラーを回避する方法もあるが、
  最適化されている`linux/arm/v8`の方が動作が速い
  (特にインストールの時間はかなり違います)

以上となります。最後まで記事を読んでいただきありがとうございます。

# 参考文献
- [既存RailsアプリをDocker化する手順](https://qiita.com/snow_swallow/items/31e6bb582123a58676e3)
- [M1 ProのDockerで沼にハマった話](https://zenn.dev/kenkenlysh/articles/6ba1f5b6b59f2c)
- [Apple Silicon（M1/M2）環境で、a-blog cms の docker環境を用意する](https://developer.a-blogcms.jp/blog/custom/docker-arm64.html)