🤔

【Docker】バインドマウントによる上書きに要注意

2023/06/05に公開

プログラミング初学者にとって、とっつきにくいと感じるものの一つが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の結果がビルドして立ち上げたコンテナ内に反映されません。

修正すべき箇所はどこになるかお分かりになりますでしょうか。
※ちなみにエラーは生じません...そのため当時は原因の特定に苦労しました....

docker-compose.yml
frontend:
    build: 
        context: ./frontend
        dockerfile: Dockerfile
    ports:
        - "8080:8080"
    volumes:
        - ./frontend:/app
Dockerfile
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