Dockerの volume trick を試してみる

# はじめに
ReactをDockerで使ってみようとした際に、Reactのアプリケーションディレクトリを丸ごとバインドマウントしたところ、コンテナ側でインストールしたnode_modulesがホスト側にもマウントされてしまう、という問題にぶち当たりました。
その解消方法として、`volume trick` というものがあったので、少し試してみた記事になります。

# volume trick とは
バインドマウントするフォルダ内にマウントさせたくないフォルダが存在する場合に、そのフォルダをマウント対象から除外する方法。
参考: https://jdlm.info/articles/2016/03/06/lessons-building-node-app-docker.html

※ Dockerfile内の `COPY` や `ADD` でコンテナ内にファイルを配置する場合は `.dockerignore` が使えるので、わざわざ volume trick を使う必要はありません。（[参考](https://docs.docker.jp/engine/reference/builder.html#dockerignore)）

# リポジトリ
https://github.com/b-coffin/volume-trick-tutorial

# ファイル構造
今回試してみた際のファイル構造は下記の通りです。
```
.
├── docker-compose.yml
├── Dockerfile
└── src
     ├── target
     │    └── inHost.txt // コンテナ側にマウントされないことを確認したい
     └── index.html
```

この構造で、`src` はバインドマウントするが、`src/target` はマウントしないようにします。

# ファイル記述

```Dockerfile:Dockerfile
FROM nginx:latest

WORKDIR /usr/share/nginx/html

RUN mkdir target \
  && echo touch in image > target/inImage.txt # ホスト側にマウントされないことを確認したい
```

```yml:docker-compose.yml
version: "3.8"

services:
  server:
    build: .
    ports:
      - 80:80
    volumes:
      - ./src:/usr/share/nginx/html # バインドマウント
      - exception-volumes:/usr/share/nginx/html/target # バインドマウントするフォルダ中のtargetフォルダのみvolumeマウントの対象とする

volumes:
  exception-volumes: # volume trick用のダミーのvolume
```

# 動かしてみる

```console:コンテナ起動
$ docker compose up -d --build
```

```console:コンテナ内のファイル構造確認
$ docker compose exec server ls
index.html  target

$ docker compose exec server ls target
inImage.txt
```

```console:ホスト側のファイル構造確認
$ ls src
index.html      target

$ ls src/target
inHost.txt
```

動かしてみた結果、下記のことを確認することができました。

- ホスト側の `target/inHost.txt` がコンテナ側にない
- イメージビルド時に作成した `target/inImage.txt` がコンテナ側にはあるが、ホスト側にはない


図で表すと下記のような差分があります。
```diff:コンテナ側のファイル構造（ホストと比べた差分を表示）
  .
  ├── target
+ │    └── inImage.txt
- │    └── inHost.txt
  └── index.html
```

コンテナとボリュームのイメージとしては、下記になります。
（[こちらのページ](https://qiita.com/gounx2/items/23b0dc8b8b95cc629f32)のイメージ図がわかりやすかったので、参考にしています。）

```
+--<Host>--+   +--<Docker>-----------------------------------------------------------------+
|          |   |  +--<Container:c1>-------------------+  +--<Volume:exception-volumes>--+  |
|          |   |  |                                   |  |                              |  |
| ./src/------------> /usr/share/nginx/html           |  |                              |  |
|          |   |  |   /usr/share/nginx/html/target <---------/                          |  |
|          |   |  |                                   |  |                              |  |
|          |   |  +-----------------------------------+  +------------------------------+  |
+----------+   +---------------------------------------------------------------------------+
```

# 結果
対象のフォルダをバインド対象から外すことができました！

# 参考
- [Lessons from Building a Node App in Docker - jdlm.info](https://jdlm.info/articles/2016/03/06/lessons-building-node-app-docker.html)
- [DockerでVolumeをマウントするとき一部を除外する方法 - Qiita](https://qiita.com/suin/items/e53eee56da23d476addc)
- [Docker と node_modules と Volume Trick - Hatena Blog](https://shotat.hateblo.jp/entry/2016/12/01/221631)
- [VSCode&Docker Volumeにおけるnode_modules問題を解決する - Zenn](https://zenn.dev/foolishell/articles/3d327557af3554)
- [Docker、ボリューム(Volume)について真面目に調べた - Qiita](https://qiita.com/gounx2/items/23b0dc8b8b95cc629f32)