🙆‍♂️

Dockerのマウントについて

2022/02/10に公開

伊藤です。dockerでいつもコンテナを作り直す必要がある時とない時、何が違うのかと疑問に思っていたのですが、理由がわかったので共有します。

そもそもマウントには「取り付ける」という意味がありますが、dockerでは何を取り付けているのかというとコンテナに自分のPCのストレージの一部(これをvolumeと呼ぶ)をマウントしています。

そして、このマウントの方法には2種類あるのです。(ここがややこしい)

1 ボリュームマウント

volumeマウントは、docker engineの管理下にvolumeを確保してそこにファイルを管理します。こちらのいい点としては、docker engineの管理下にあるので、いい意味でも悪い意味でも触りづらい、ということがあります。このマウントされたファイルにアクセスして書き換えるにはdocker engineを介する必要があるのですね。

2 バインドマウント

バインドマウントは、自分のPCのデスクトップやドキュメントなど、docker engineの管理していない部分のファイルをマウントすることです。こちらのマウントの方法では、dockerと自分のPCが同じファイルを一緒に共有しているようなイメージで、直接ファイルを書き換えれてしまいます。ソースコードとかは書き換えながら挙動を確認するので当然こちらのマウント方法でないと、毎回ファイルを書き換えるたびにコンテナを作り直す羽目になってしまいます。

という感じで、滅多に触らないし、ファイルの存在を意識する必要のないものはボリュームマウント、逆に書き換える必要のあるものはバインドマウントをするべきなのですね。

実際のdocker-compose.ymlファイルを確認しよう

ということがわかったところで、弊社のdocker-composeファイルを確認しましょう。

docker-compose.yml
version: "3.9"
services:
  nextjs:
    ports:
      - 3000:3000
    build:
      context: .
      dockerfile: Dockerfile
    container_name: hr_website_container
    volumes:
      - ./app:/usr/src/app
      - /usr/src/app/node_modules
      - /usr/src/app/.next

こちらのファイルのvolumesという部分がマウントの設定をしている部分です。

- ./app:/usr/src/app

こちらがバインドマウントの設定を行っている部分ですね。つまり、app直下のソースコードを編集すると、それを共有しているコンテナ内部のusr/src/app直下にあるファイルも書き換えられ、変更が反映される訳です。

- /usr/src/app/node_modules
- /usr/src/app/.next

一方、こちらがボリュームマウントを行っている部分です。先ほどのように、

自分のPCのディレクトリ:コンテナ内で共有したいディレクトリ

と書くのではなく、

コンテナ内にマウントしたいディレクトリ名

と記述してますね。こちらは、自分のPC内部とは別に領域が確保されるので、PC内部で書き換えてもコンテナを削除して作り直すまで更新されないのです。これらは匿名volumeと呼ばれ、

docker volume ls

で確認することができます。実際に確認してみると、

DRIVER    VOLUME NAME
local     45e7d3f68e3265e0943dfb51b20eb063ef474a54e6491ad2434c2a2b5404045f
local     66dbd57a326497cd7ad2875384816bc50b41986cb2fda375c9f4e895b0ab3e34
local     acb629483241ae0e95bfdc2493af1cbf1192df18536c86efb1b2af9354b8e18c
local     c3fde274b6ea08a8f6c7185382eef1c6f557f64b9da0435f02707411df558954

hash化された匿名volumeが生成されていることがわかります。

docker-compose.yml
version: "3.9"
services:
  nextjs:
    ports:
      - 3000:3000
    build:
      context: .
      dockerfile: Dockerfile
    container_name: hr_website_container
    volumes:
      - ./app:/usr/src/app
      - node_modules:/usr/src/app/node_modules
      - next:/usr/src/app/.next
volumes:
  node_modules:
  next:

上記のようにvolumeマウントする際に名前を指定して、docker volume lsでvolume一覧を確認すると

local     hr_next
local     hr_node_modules

のように名前付きでvolumeができていることがわかります。
実際に

 docker inspect hr_node_modules

というように詳細を確認すると

[
   {
       "CreatedAt": "2022-02-11T00:20:28Z",
       "Driver": "local",
       "Labels": {
           "com.docker.compose.project": "hr",
           "com.docker.compose.version": "1.29.2",
           "com.docker.compose.volume": "node_modules"
       },
       "Mountpoint": "/var/lib/docker/volumes/hr_node_modules/_data",
       "Name": "hr_node_modules",
       "Options": null,
       "Scope": "local"
   }
]

このようにdocker engineの中に名前付きでデータが確保されることがわかります。

これらを毎回指定して参照してあげることもできます。匿名volumeは毎回名前が変わるので、containerを作るたびにvolumeが作られ、node_modulesなどは更新されるのですね。逆に名前付きvolumeにして毎回同じものを参照するようにすると、containerを作り直してもvolumeが変わらないということが起きてしまうので、node_modulesが変わるたびにvolumeを消して作り直す必要があります。

ということで、dc downした?系の疑問がこれでなくなるのかと思ってます。

Discussion