【Docker】ホスト側のnode_modulesが空になる問題を改善する

## `node_modules`の取り扱いには注意が必要
Dockerを利用して環境構築をする際には`node_modules`の取り扱いに注意する必要があります。  
何も意識していないとホスト側で`yarn install`などをしてしまうとエラーが発生することがあります。  

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

また`yarn install`などを`Dockerfile`で実行した際に`node_modules`が削除されたりします。  

以下の記事ではコンテナ内のnode_modulesが消える問題の改善方法について記載しています。

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

しかし、上記の記事に記載している方法ではホスト側の`node_modules`が空になってしまいます。

今回は、ホスト側の`node_modules`が空になる問題の改善方法について記載します。
`Next.js`の環境で試したので`Next.js`で記載しますが他でも応用可能と思います。

## ホスト側の`node_modules`が空になる原因と改善方法
### 原因
`docker-compose.yml`を以下のようにすることで`node_modules`に対してボリュームを設定してバインドマウントから除外しています。
バインドマウントから除外することでコンテナ内の`node_modules`が消えることは回避できますが、　`node_modules`がボリュームで管理されるようになるため、ホスト側には反映されなくなります。
そのため、ボリュームで管理されている`node_modules`のみ空となってしまいます。

```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:
```

バインドマウントやボリュームの違いなどについては以下の記事をご参照ください。  

https://qiita.com/P-man_Brown/items/30fba7bb265a525c77ad

### 改善方法
以下で紹介する方法はWindowsでは動作しないようです。

https://github.com/moby/moby/issues/19990

https://docs.docker.com/engine/reference/commandline/volume_create/#opt

`docker-compose.yml`を以下のようにすることでボリュームで管理されている`node_modules`の内容をホスト側にも表示できます。

```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:
    driver: local
    driver_opts:
      type: none
      device: ${PWD}/node_modules
      o: bind
```

下の`volumes`内の`device`には絶対パスを記述する必要があります。

これで、`docker compose up`するとホスト側にも`node_modules`の中身が表示されます。
```:ターミナル
$ docker compose up
[+] Running 3/3
 ⠿ Network next_default        C...                        0.0s
 ⠿ Volume "next_node_modules"  Created                     0.0s
 ⠿ Container next-front-1      Created                    93.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.2s (124 modules)
```
上の`Container next-front-1 Created 93.0s`を見てわかるとおり、ボリュームが作成された後、初回の`docker compose up`に時間がかかります。

また注意点としては、ホスト側に空の`node_modules`を用意する必要があります。
用意していないと以下のようなエラーが出ます。

```:ターミナル
Error response from daemon: error while mounting volume
'/var/lib/docker/volumes/next_node_modules/_data': failed to mount local volume: 
mount /Users/user_name/Documents/sources/docker-next-test/Next/node_modules:
/var/lib/docker/volumes/next_node_modules/_data, flags: 0x1000:
no such file or directory
```

<details>
<summary><b>空の node_modules を用意してもエラーが出る場合</b></summary>

空の`node_modules`を用意して`docker compose build`したにもかかわらず以下のようなエラーが出ることがあります。
```:ターミナル
$ docker compose build

$ docker compose up
[+] Running 3/0
 ⠿ Network next_default        C...                        0.0s
 ⠿ Volume "next_node_modules"  Created                     0.0s
 ⠿ Container next-front-1      Created                     0.0s
Attaching to next-front-1
next-front-1  | yarn run v1.22.18
next-front-1  | $ next dev
next-front-1  | /bin/sh: 1: next: not found
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 127.
```

コンテナ内で以下のコマンドを実行し、`node_modules`が本当に空か確認します。
```:ターミナル
myuser@36f641e85791:/myapp$ ls -a node_modules
.  ..  .DS_Store
```
ホスト側の`node_modules`に`.DS_Store`があれば、これがコンテナ側に反映されてしまうことが原因です。
ホスト側の`node_modules`内の`.DS_Store`を削除するために以下を実行します。
```:ターミナル
$ rm node_modules/.DS_Store
```
そして`docker compose down -v` → `docker compose up`で改善します。
</details>

なお上記の他にホスト側でも`yarn install`することや（ホスト側が空になる状態であればホスト側の変更はコンテナ内に反映されません）、VSCodeで `Remote - Containers`を利用することでも対処できます。  
`Remote - Containers`の使用方法については以下をご参照ください。

https://qiita.com/P-man_Brown/items/298e34af3fd6d363e085

またホスト側に`node_modules`の中身が表示されるようになりましたが、ホスト側の変更がコンテナ内に反映されてしまいます。
ホスト側で`yarn install`などをするとエラーの原因となることがあります。
ホスト側での`yarn install`等を禁止する方法については以下のページをご参照ください。

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