はじめに
最近、くろかわこうへいさんや、KENTAさんの動画をよく見る中で「あれ、Docker できないのやばくね?」と、ふと思いDocker の学習を始めました。参考書やネットでちょっと調べていくと「あれ、思ってたよりも難しくないじゃん」と思っていた矢先に、volume が出てきて一気につまづきまいした。記事などもいろいろ読んで調べてみたものの、よくわからず挫折しかけていた時に、公式ドキュメントに出会い読んでみると「何これわかりやすいじゃん!」ってなりました(笑)。
ということで、公式ドキュメントを読んで自分なりに理解したことをまとめます。
実行環境
- Windows Home 10
- Docker Toolbox v19.03.1
この記事を読むとわかること
- volume とは
- Dockerをデータの永続化の具体的な方法
- データのマウントの具体的な挙動及び実装方法
- bind mount
- volume
- tmpfs mount
Volume には 2 つの意味がある
後述するが、データを永続化させる方法は bind mount
と volume
の2 種類ある。ただ、どちらも -v
や --volume
を使う。つまり、volume という単語には 2 つの意味があることになる。
- データをマウントするための機能(-v, --volume)
- Dockerのリソース内にあるコンテナの実行データを保管する場所
この記事では前者の機能を示す volume
を データのマウントと定義する。
なぜデータのマウントが必要か
そもそもなぜ、データのマウントが必要なのか?
それは、コンテナ内部にデータを保存しても、コンテナ破棄すると消えてしまうため、データを永続化する際は、コンテナの外にデータを置く必要があるため。
データをマウント方法は 3 つ
概要
データのマウント方法は 3 つある。データを永続化させる場合は Bind mount
か Volume
を使う。
- Bind mount
- Volume
- Tmpfs mount
種類 | データの保管場所 | 永続化 |
---|---|---|
Bind mount | ホストディレクトリ | ○ |
Volume | Docker のリソース | ○ |
Tmpfs mount | メモリ | × |
出典:docker docs
Bind mount
-
特徴
- ホストディレクトリやファイルがコンテナにマウントされる
- コンテナ内のデータの変更に応じてホスト側のファイルも変更される
-
用途
-
データを永続化させたい
-
開発環境でホスト側のソースコードの修正を反映させたい
※ 本番環境のイメージではマウントでデータをコンテナに反映させるのではなく、`copy`を使うことが推奨されている。
-
-
実装方法
-
-v
または--volume
を使うオプションの設定が **1 つ**のフィールドに固まっており、コンテナにデータのマウント先のディレクトリがなければ**自動的に作成**される。
-
--mount
を使うオプションの設定が**複数**のフィールドに分かれているコンテナにマウント先のディレクトリが無ければ、**エラー**を返す。(※筆者の環境ではディレクトリが自動作成された)
-
-
例
volume_testディレクトリをマウントしてみる。ディレクトリの中は text.txt のみ。
volume_test
test.txt
テキストファイルの内容。
test
volume_testディレクトリに移動する。
まずは、-v
(--volume
)コマンドでカレントディレクトリを ubuntu
コンテナ内のすでに存在する /usr/src/var/tmp
にマウントしてみる。
> docker run -it --name bindmount -v $(pwd):/usr/src/var/tmp ubuntu bash
root@eab26b27a693:/# cd usr/src/var/tmp
root@eab26b27a693:/usr/src/var/tmp# ls
test.txt
きちんと ディレクトリのデータがマウントされていることが分かる。ファイルの内容もtest
から mount success!
に変更してみる。
root@eab26b27a693:/usr/src/var/tmp# cat test.txt
test
root@eab26b27a693:/usr/src/var/tmp# echo mount success! > test.txt
root@eab26b27a693:/usr/src/var/tmp# cat test.txt
mount success!
実際にホスト側のファイルを確認してみると変更が反映されている!
次に、 ubuntu
コンテナ内に存在しない /volume_test
にマウントしてみる。
> docker run -it --name bindmount -v $(pwd):/volume_test ubuntu bash
root@b3e9bf711b7e:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var volume_test
root@b3e9bf711b7e:/# cd volume_test
root@b3e9bf711b7e:/volume_test# ls
test.txt
存在しないディレクトリにマウントした際には新しくディレクトリが作られている。先程と同様にファイルの内容もtest
から mount success!
に変更してみる。
root@b3e9bf711b7e:/volume_test# cat test.txt
test
root@b3e9bf711b7e:/volume_test# echo mount success! > test.txt
root@b3e9bf711b7e:/volume_test# cat test.txt
mount success!
こちらでもホスト側のファイルを確認してみると変更が反映されている!
次に --mount
コマンドでカレントディレクトリを ubuntu
内のすでに存在する /usr/src/var/tmp
にマウントしてみる。-v
とは違いマウントの仕方やホスト側のディレクトリ、マウントのディレクトリといった複数のオプションをカンマつなぎで記述する。
> docker run -it --name bindmount --mount type=bind,source="$(pwd)",target=/usr/src/var/tmp ubuntu bash
root@f77d388c204e:/# cd usr/src/var/tmp
root@f77d388c204e:/usr/src/var/tmp# ls
test.txt
--mount
を使った場合でも同様にマウントできた。実際にコンテナ内のファイルを編集した際は上記の内容と同様に、ホスト側のファイルも編集された。
次に、ubuntu
コンテナに存在しないディレクトリ( volume_test
)にマウントしてみる。
docker run -it --rm --name bindmount --mount type=bind,source="$(pwd)",target=/volume_test ubuntu bash
root@a738ae0ebbd0:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var volume_test
volume_test
が存在している!!! マウントできた!?
公式ドキュメントによると、
If you use --mount to bind-mount a file or directory that does not yet exist on the Docker host, Docker does not automatically create it for you, but generates an error.
出典:docker docs
つまり、--mount
を使ってコンテナにバインドマウントする際は、コンテナ内にすでに存在するディレクトリにマウントしないとエラーが出る。なぜできたのだろう??
バインドマウントできているか確かめてみる。
root@a738ae0ebbd0:/# cd volume_test/
root@a738ae0ebbd0:/volume_test# cat test.txt
test
root@a738ae0ebbd0:/volume_test# echo mount sucess! > test.txt
root@a738ae0ebbd0:/volume_test# cat test.txt
mount sucess!
ホスト側のファイルも変更されているので、バインドマウントができている。なぜだろう??
ちなみに-v
, --mmount
どちらを使ってもマルチコンテナ間でもデータの共有は可能。
Volume
-
特徴
- docker リソース内のデータがコンテナにマウントされる
- ホストディレクトリに依存しない
-
用途
- データを永続化させたい
- 開発環境でソースコードの修正を反映させる以外の目的
-
実装方法
bind mount
と同じ。-v
または--mount
を使う。 -
例
まずはvolume_test
という名前のボリュームを作る。
$ docker volume create volume_test
volume_test
$ docker volume ls
local volume_test
まずは、-v
(--volume
)コマンドでカレントディレクトリを ubuntu
コンテナ内のすでに存在する /usr/src/var/tmp
にマウントしてみる。また、ボリュームが存在しない場合は自動でボリュームが作成される。test.txt
に mount success! と入力してコンテナを閉じる。
$ docker run -it --name volume_mount -v volume_test:/usr/src/var/tmp ubuntu bash root@fe23da2e739c:/# cd /usr/src/var/tmp
root@fe23da2e739c:/usr/src/var/tmp# ls
root@fe23da2e739c:/usr/src/var/tmp# touch test.txt
root@fe23da2e739c:/usr/src/var/tmp# echo mount success! > test.txt
root@fe23da2e739c:/usr/src/var/tmp# cat test.txt
mount success!
root@fe23da2e739c:/usr/src/var/tmp# exit
コンテナを再度立ち上げて、test.txtが存在するか確認してみる。
$ docker container rm volume_mount
volume_mount
$ docker run -it --name volume_mount -v volume_test:/usr/src/var/tmp ubuntu bash
root@e54d18741135:/# cat usr/src/var/tmp/test.txt
mount success!
マウントできている!ubuntu
コンテナに存在しないディレクトリにマウントしても同様の結果が得られる。
次に、次に --mount
コマンドでカレントディレクトリを ubuntu
内のすでに存在する /usr/src/var/tmp
にマウントしてみる。先程と同様に、test.txt
に mount success! と入力してコンテナを閉じる。
$ docker run -it --name volume_mount --mount source="volume_test",target=/usr/src/var/tmp ubuntu bash
root@03074d3166a9:/# echo > usr/src/var/tmp/test.txt
root@03074d3166a9:/# cat usr/src/var/tmp/test.txt
root@03074d3166a9:/# echo mount success! > usr/src/var/tmp/test.txt
root@03074d3166a9:/# cat usr/src/var/tmp/test.txt
mount success!
root@03074d3166a9:/# exit
再度、volume_test
をマウントしてコンテナを立ち上げる。
$ docker run -it --name volume_mount --mount source="volume_test",target=/usr/src/var/tmp ubuntu bash
root@9fe26cd9575f:/# cat usr/src/var/tmp/test.txt
mount success!
マウントできている!ubuntu
コンテナに存在しないディレクトリにマウントしても同様の結果が得られる。
ちなみに-v
, --mmount
どちらを使ってもマルチコンテナ間でもデータの共有は可能。
Tmpfs mount
-
特徴
- マルチコンテナ間でデータの共有はできない
- コンテナが止まるとホストメモリ上に保存されたファイルは消える
- Docker on Linux でのみ動作する
-
用途
- 一時的にデータを退避させたい
- セキュリティ的な理由で、ホストディレクトリやDocker リソース内の書き込み可能な領域にファイルを保管したくない
-
実装方法
-
-tmpfs
を使うマウントディレクトリ以外のオプションの設定が不可、コンテナにデータのマウント先のディレクトリがなければ**自動的に作成**される。
-
--mount
を使うオプションの設定が**複数**のフィールドに分かれているコンテナにマウント先のディレクトリが無ければ、**エラー**を返す。
-
まとめ
- データの永続化の種類は二つ
- bind mount
- volume
それぞれの違いは、永続化データの保存領域。使い分けは、開発においてソースコードの修正をコンテナにも反映させたいといったホストOSのファイルに依存することなら bind mount そうでなければ、volume を使う。
- 一時的にデータを退避させるなら tmpfs mount を使う。
初心者なので、至らぬところはあるかと思いますがご指摘いただいたけると幸いです