【Docker】バインドマウントによる上書きに要注意
プログラミング初学者にとって、とっつきにくいと感じるものの一つがDockerだと思います。
そんな同志の方に向けて、私がDockerで解決法に悩んだ問題とその解決方法を共有したいと思います。
■使用バージョン
・ node:v18.16.0
・ docker:version 24.0.2
・ docker-compose:version 1.29.2
問題
早速ですが、冒頭の問題についてご紹介します。
下記コードでは、docker-compose up
コマンドでdocker-compose.ymlを呼び出し、
buildプロパティで指定したDockerfileの実行を試みています。
Dockerfile内にRUN npm install
コマンドがありますが、
このままではnpm installの結果がビルドして立ち上げたコンテナ内に反映されません。
修正すべき箇所はどこになるかお分かりになりますでしょうか。
※ちなみにエラーは生じません...そのため当時は原因の特定に苦労しました....
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "8080:8080"
volumes:
- ./frontend:/app
From node:18
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 8080
CMD ["npm", "run", "start"]
※ディレクトリ構成は下の通りです。
frontend
├── Dockerfile
└── package.json
docker-compose.yml
解答
解答ですが、問題の箇所はdocker-compose.yml内にあるvolumeプロパティの記載
になります。
そして修正については、
docker-compose.ymlのボリュームマウントを二つに分け、
node_modulesを別途管理する
が正解となります。
以下の解答例では、node_modulesを名前付きボリュームで別管理しています。
■docker-compose.yml
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "8080:8080"
volumes:
- ./frontend:/app
- node_modules:/app/node_modules #追加
volumes:
node_modules: #追加
解説
上記の原因は、docker-compose.ymlで行われているバインドマウントにあります。
volumes:
- ./frontend:/app
この部分で、ホスト側のfrontend配下をコンテナ側のapp配下にマウントしていますが、
このマウントによりnpm installで生成されたapp側のnode_modulesが上書きされてしまっています。
結果、RUN npm installは問題なく実行されapp側にnode_modulesが作成されるものの、
そのあとにfrontend配下のフォルダ構成を上書きしてしまい、
コンテナ内のフォルダ構成がfrontend配下と同じになる(node_modulesが消えてしまう)
という状態となってしまいます。
上図のようにnode_modulesが➁ボリュームマウントで上書きされてしまっている事が原因であるため、対処法としてnode_modulesを別のボリュームマウントで分けて別途管理することが必要となります。
具体的には、上図のようになります。
図では、node_modulesをDockerホスト上のボリュームとマウントしています。
このようにすることで、node_modulesが➁によるボリュームマウントで上書きされてしまうことを防いでいます。
まとめ
当記事では、Dockerのバインドマウントの挙動についてご紹介しました。
今回ご紹介した問題では、一見するとDockerfileのnpm installがあたかも実行されていなかったかのような振る舞いとなります。
加えて、エラーが全く出なかったため、原因の特定に非常に苦労しました。
エラーが出ないにも関わらず、想定と違う動きをした場合の恐ろしさを身をもって体感しました。
Discussion