バインドマウント

## 概要

バインドマウントとは、ボリュームマウントとは異なるタイプのマウントであり、ホスト上のファイルシステムをコンテナ内と直接共有する。
ホスト上のソースコードの変更をコンテナ上に直ちに反映することができる。

## バインドマウント

### バインドマウントを使用しない場合…

バインドマウントをせずにコンテナを起動する。

```bash
# Dockerイメージをビルドする。
$ docker build -t getting-started .

# コンテナを作成する。
$ docker run -dp 127.0.0.1:3000:3000 getting-started
```

Localhostの3000番からアプリケーションへのアクセスができるようになる。
この場合には、ホスト上のソースを修正しても、コンテナには反映されない。

:::note
Dockerイメージには、コンテナのファイルシステムの構成やアプリケーションのソースコード（ビルド済みの成果物の場合も）が含まれる。
コンテナ上で起動しているアプリケーションを最新にするには、コンテナを起動しているイメージを最新にする必要がある。
:::

```bash
# イメージを再構築する。
$ docker image build -t getting-started .

# コンテナを再作成する。
$ docker ps 
CONTAINER ID   IMAGE          COMMAND                  CREATED              STATUS              PORTS                      NAMES
19bc26a99f56   cc9d6355b61f   "docker-entrypoint.s…"   About a minute ago   Up About a minute   127.0.0.1:3000->3000/tcp   tender_matsumoto

$ docker stop 19bc26a99f56
19bc26a99f56

$ docker rm 19bc26a99f56
19bc26a99f56

$ docker run -dp 127.0.0.1:3000:3000 getting-started
```

アプリケーションを最新にするには、イメージの再構築とコンテナの再起動が必要になる。

### バインドマウント

バインドマウントを設定し、イメージを作り直す手間をなくす。
以下のコマンドでは、コンテナ起動時にバインドマウントを設定している。

```bash
$ docker run -dp 127.0.0.1:3000:3000 \
    -w /app --mount type=bind,src="$(pwd)",target=/app \
    node:18-alpine \
    sh -c "yarn install && yarn run dev"
```

<details><summary>コマンドの詳細</summary>

- `-w`
    - 指定したディレクトリの中でコマンドを実行する。
    - 今回の場合、`/app`ディレクトリ内でコマンドが実行される。
- `--mount type=bind,src="$(pwd)",target=/app`
    - マウントの種類を`bind`に指定する。
    - ホスト上のカレントディレクトリをコンテナ内の`/app`ディレクトリにバインドマウントする。

</details>

ファイルを変更すると、コンテナに直ちに反映される。
変更の度、`nodemon`のプロセスが実行される。

```bash
$ docker logs -f a868ea16dfae
yarn install v1.22.19
[1/4] Resolving packages...
success Already up-to-date.
Done in 0.56s.
yarn run v1.22.19
$ nodemon src/index.js
[nodemon] 2.0.20
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node src/index.js`
Using sqlite database at /etc/todos/todo.db
Listening on port 3000
[nodemon] restarting due to changes...
[nodemon] starting `node src/index.js`
Using sqlite database at /etc/todos/todo.db
Listening on port 3000
```

### 疑問点

バインドマウントを設定した場合、ホストのファイルの変更時にDockerイメージが更新されているのか？
バインドマウントを使用しない場合は、ソースファイルの変更を適用するためにはイメージの再構築が必要になる。バインドマウントの場合もイメージを最新にする必要はないか？

**結論**

ソースを変更しても、イメージの更新は行われない。

```bash
# イメージの更新履歴を確認する。
$ docker image history getting-started
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
b02659a4a713   12 minutes ago   CMD ["node" "src/index.js"]                     0B        buildkit.dockerfile.v0
<missing>      12 minutes ago   COPY . . # buildkit                             4.59MB    buildkit.dockerfile.v0
<missing>      13 days ago      RUN /bin/sh -c yarn install --production # b…   85.2MB    buildkit.dockerfile.v0
<missing>      13 days ago      COPY package.json yarn.lock ./ # buildkit       148kB     buildkit.dockerfile.v0
<missing>      2 weeks ago      WORKDIR /app                                    0B        buildkit.dockerfile.v0
<missing>      2 weeks ago      /bin/sh -c #(nop)  CMD ["node"]                 0B        
<missing>      2 weeks ago      /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B        
<missing>      2 weeks ago      /bin/sh -c #(nop) COPY file:4d192565a7220e13…   388B      
<missing>      2 weeks ago      /bin/sh -c apk add --no-cache --virtual .bui…   7.77MB    
<missing>      2 weeks ago      /bin/sh -c #(nop)  ENV YARN_VERSION=1.22.19     0B        
<missing>      2 weeks ago      /bin/sh -c addgroup -g 1000 node     && addu…   114MB     
<missing>      2 weeks ago      /bin/sh -c #(nop)  ENV NODE_VERSION=18.19.0     0B        
<missing>      3 weeks ago      /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B        
<missing>      3 weeks ago      /bin/sh -c #(nop) ADD file:8182c73f869a899cf…   7.73MB
```

`hisotry`からイメージの更新を観測することができる。
ファイルの更新をしても、イメージの更新履歴に変化はなかった。

:::note
なぜ、Dockerイメージが最新でないのにアプリケーションが更新されるのか？
空でないディレクトリにバインドマウントが行われる場合には、元のリソースが隠されて、バインドマウントされているソースが反映されるためである。
つまり、コンテナでは、ホスト上の最新のソースコードからアプリケーションの起動が行われている。

イメージを共有する場合には、Dockerイメージを最新にする必要がある。
:::

## 空でないディレクトリへのマウント

空でないディレクトリにバインドマウントを行う場合、ディレクトリの元の内容が隠されることを注意しなければならない。
[バインド マウント(bind mount) の使用 — Docker-docs-ja 24.0 ドキュメント](https://docs.docker.jp/storage/bind-mounts.html#id5)

### 発生した事象

`yarn run ${NODE_ENV}`の実施時に以下のエラーが発生した。

```bash
2024-03-25 00:32:59 Usage Error: Couldn't find the node_modules state file - running an install might help (findPackageLocation)
2024-03-25 00:32:59 
2024-03-25 00:32:59 $ yarn run [--inspect] [--inspect-brk] [-T,--top-level] [-B,--binaries-only] [--require #0] <scriptName> ...
```

使用した`Dockerfile`

```docker
FROM node:21.7.1-bullseye-slim
WORKDIR /app

# キャッシュ機能を有効活用するためにパッケージ関連のものを先にCOPYしている。
COPY yarn.lock package.json .yarnrc.yml .
COPY .yarn/ ./.yarn/
RUN yarn install

COPY . .
CMD yarn run ${NODE_ENV}
```

ここで意識するべき箇所は、`/app`ディレクトリに依存関係のファイルがコピーされ、`yarn install`により`node_modules`が作成されているという点。

Dockerコンテナの起動コマンド

```bash
$ docker run \
	-dp 127.0.0.1:3000:3000 \
	--env NODE_ENV=dev \
	--name web_container \
	--mount type=bind,source=./web,target=/app \
	web_container 
```

`/app`ディレクトリにバインドマウントが行われている。
空でないディレクトリへのバインドマウントが行われたため、元の内容が隠されてしまっている。
つまり、`yarn install`で作成された`node_modules`が参照できない。

### 問題の解消

問題の解消方法はものすごく単純でボリュームマウントを行う対象のディレクトリを変更すればいい。
`/web`ディレクトリごとマウントしているが、`/web`ディレクトリに含まれる個別のディレクトリごとに複数マウントする様に変更した。

```bash
$ docker run -dp 127.0.0.1:3000:3000 \
	--env NODE_ENV=dev \
	--name web_container \
	--mount type=bind,source="$(pwd)/public",target=/app/public \
	--mount type=bind,source="$(pwd)/src",target=/app/src \
	web
```

## 参考文献
- https://docs.docker.jp/storage/bind-mounts.html