Docker volumeのホスト-コンテナ間のファイル受け渡しでハマった時のメモ

## 概要
docker-composeを使って

* 片方のコンテナでyarn buildしてwebpack -> bundleファイルを作成
* もう片方のコンテナにvolumeを通じてbundleを見せて、nginxから静的配信

ということをさせているのだけれど、そこでハマったのでメモ。

## 構成

大体こんな感じ

```
.
├── Dockerfile
├── README.md
├── client
│   ├── index.html
│   ├── index.js
│   └── style.css
├── docker-compose.yml
├── nginx
│   └── default.conf
├── out
│   ├── bundle.js
│   ├── img/
│   └── index.html
├── package.json
├── webpack
```

この時、webpackのoutput設定をoutフォルダにしているため、outフォルダを以下のようにvolume定義してホストと２コンテナ間で共有したかった。

```
# Dockerfile
FROM node:12.0.0

RUN useradd --user-group --create-home --shell /bin/false app

ENV HOME=/home/app

COPY package.json webpack* $HOME/myapp/
COPY client $HOME/myapp/client
COPY out $HOME/myapp/out

RUN chown -R app:app $HOME/*

USER app
WORKDIR $HOME/myapp
RUN yarn install
RUN yarn build
#yarn buildはpackage.json内でwebpackするよう定義されている

CMD ["tail", "-f", "/dev/null"]
# foregroundで実行するため

```

```yml:docker-compose.yml
version: "3.7"
services:
  bundle-build:
      build: .
      container_name: "bundle-build"
      volumes:
          - type: volume
            source: out
            target: /home/app/myapp/out

  nginx-proxy:
      image: nginx
      container_name: "nginx"
      ports:
          - '443:443'
      volumes:
          - type: bind
            source: ./nginx
            target: /etc/nginx/conf.d
          - type: volume
            source: out
            target: /www/app/myapp
            read_only: true
      command: "nginx -g 'daemon off;'"
      restart: unless-stopped
      depends_on:
          - bundle-build

volumes:
    out:

```

なので、実行順は

1. bundle-buildイメージを作成 -> ここでyarn buildされ、bundleファイルが作成される
1. nginxのイメージ作成はスキップ
1. docker-composeに従って設定周りが実行 -> 1で作成されたbundleファイルがコンテナとホスト間で共有される

となるかと思っていた。

## 実行してみた

上記のような設定でdocker-compose up すると、初めてイメージ、コンテナ、ボリュームを作るときはうまくいくが、htmlやjsファイルを修正して再度bundleファイルを作成しようとすると、更新分がうまく反映されないという問題があった。

## 原因

volumeの仕様として以下のような動きをする様子。

* イメージ作成後、ホストにコンテナのvolumeをマウントするとき、ホスト側のファイルが優先され、コンテナ側のファイルを上書きする

このため、一方のコンテナでwebpackして差分を含めた新しいbundleファイルを作っても、前回docker-compose upした際に作成されるbundleファイルがホスト側のvolumeに残っているため、コンテナ側でせっかく作った新bundleファイルを旧bundleファイルが上書きしてしまう。

## 対応

コンテナ側を優先してマウントする設定などが無いようなので、以下のようにDockerfileのCMD内でyarn buildするように変更した。

```
# Dockerfile
FROM node:12.0.0

RUN useradd --user-group --create-home --shell /bin/false app

ENV HOME=/home/app

COPY package.json webpack* $HOME/myapp/
COPY client $HOME/myapp/client
COPY out $HOME/myapp/out

RUN chown -R app:app $HOME/*

USER app
WORKDIR $HOME/myapp
RUN yarn install
#RUN yarn build
#yarn buildはpackage.json内でwebpackするよう定義されている

#CMD ["tail", "-f", "/dev/null"]
# foregroundで実行するため
CMD ["yarn", "build"]
# コンテナ起動時にyarn build
```

こうすることで、更新分を含めたbundle1ファイルが作成されるようになった。
ただ、コンテナ起動時にyarn buildするため、webpackが終わってbundleファイルが作成されるまで、nginx側に古いファイルが見えているという問題がある。まあ数十秒かからず終わるので、そこまで厳密にならなくてもよいかもしれないが。

## 終わりに
docker、便利なんだけど特にvolume周りで想定外の動作をすることが多い。
いろいろオンラインで調査しても、先人たちも同じように戸惑っているケースが多かった。
また、情報が錯綜しているケースもあるので、手を動かして自分で確かめることが必要かと。

## 参考

[Dockerにおけるボリュームのマウント](https://logicoffee.hatenablog.com/entry/2018/06/21/123025)
[Docker Volume (特に volumeタイプ) のわかりづらいところを説明してみる](https://qiita.com/mtgto/items/aabc212a1d3f99878828#fnref1)