Docker を使っているとデータの永続化が問題になる。 例えばデータベースのアプリケーションを動作させるとして、どこにデータを残せばいいだろう。 通常のファイルシステム上に置いてしまうと、コンテナが終了すると使えなくなってしまう。 そんなときに便利なのが今回使うデータボリュームとデータボリュームコンテナのようだ。 これを使うと Docker ホスト上のファイルをコンテナでマウントできるようになる。
データボリューム
まず、データボリュームから使ってみる。 コンテナにデータボリュームを接続するには docker run コマンドに -v オプションをつける。 もちろん Dockerfile で指定することもできて、そのときは VOLUME 命令を使う。
例えばコンテナの /mydata にデータボリュームをマウントしてみよう。 イメージには CentOS7 を使った。
$ docker run --name datavolume1 -i -v /mydata -t centos:7 /bin/bash
コンテナ上で df コマンドを確認すると /mydata に /dev/sda1 がマウントされていることがわかる。
# df Filesystem 1K-blocks Used Available Use% Mounted on none 19049892 2073860 15985308 12% / tmpfs 510044 0 510044 0% /dev tmpfs 510044 0 510044 0% /sys/fs/cgroup /dev/sda1 19049892 2073860 15985308 12% /mydata shm 65536 0 65536 0% /dev/shm
一旦 Docker ホストの操作に戻ろう。 マウントされたデータボリュームの情報は docker inspect コマンドで確認できる。 ここで得られるフォーマットは JSON なので jq コマンドを使って表示内容を絞ると分かりやすい。
$ docker inspect datavolume1 | jq ".[0].Mounts" [ { "Name": "da484ac1637f1f18a85648b7c129ca33006a18c73f319f36fea1e257afb66a16", "Source": "/mnt/sda1/var/lib/docker/volumes/da484ac1637f1f18a85648b7c129ca33006a18c73f319f36fea1e257afb66a16/_data", "Destination": "/mydata", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ]
上記では Docker ホスト上の /mnt/sda1/var/lib/docker/volumes/... にあるファイルが Docker コンテナの /mydata にマウントされていることが読み取れる。
試しにコンテナ上で /mydata にファイルを作ってみよう。
# cd /mydata/ # touch helloworld.txt
そして Docker ホスト側で先ほど docker inspect で確認したディレクトリを確認してみる。 するとコンテナの中で作ったファイルがそのまま見えることがわかる。
$ sudo ls /mnt/sda1/var/lib/docker/volumes/da484ac1637f1f18a8564 8b7c129ca33006a18c73f319f36fea1e257afb66a16/_data helloworld.txt
なお、コンテナを終了したり削除してもこのデータボリュームとして使われているディレクトリは削除されない点に注意が必要だ。
このデータボリュームに使われるディレクトリの場所は指定することもできる。 それには -v オプションで \<dir>:\<mount> というようにカンマで区切って左側にそのパスを指定する。
$ docker run --name datavolume2 -i -v /tmp/mydata:/mydata -t centos:7 /bin/bash
上記では Docker ホスト上の /tmp/mydata を Docker コンテナの /mydata としてマウントする、という意味になる。
先ほどと同じように docker inspect でデータボリュームの情報を確認してみよう。
$ docker inspect datavolume2 | jq ".[0].Mounts" [ { "Source": "/tmp/mydata", "Destination": "/mydata", "Mode": "", "RW": true, "Propagation": "rprivate" } ]
この内容を見ても Docker ホストの /tmp/mydata が Docker コンテナの /mydata にマウントされたことが読み取れる。
先ほどと同じように Docker コンテナ側でファイルを /mydata においてみよう。
# cd /mydata/ # touch helloworld.txt
Docker ホスト側でマウントされたディレクトリをみるとファイルができていることがわかる。
$ ls /tmp/mydata helloworld.txt
データボリュームコンテナ
次はデータボリュームコンテナという機能を試してみよう。 先ほどのデータボリュームは Docker ホスト上のファイルを Docker コンテナでマウントする機能だった。 データボリュームコンテナは、データボリュームをマウントした Docker コンテナから、別のコンテナがそのデータボリュームを使えるようにする機能だ。
まずはデータボリュームを使われる側のコンテナを用意する。 先ほどと同じように -v オプションでデータボリュームを追加しておこう。 名前は volumecontainer1 にしておく。 データボリュームは /sharedvolume に追加する。
$ docker run -d --name volumecontainer1 -v /sharedvolume -t centos:7 /bin/bash
docker inspect コマンドでデータボリュームが追加されていることを確認する。
$ docker inspect volumecontainer1 | jq ".[0].Mounts" [ { "Name": "fab265e09aae405baab9e4e5c36d0c4b0b77e9208b6d93610001562cc81fb457", "Source": "/mnt/sda1/var/lib/docker/volumes/fab265e09aae405baab9e4e5c36d0c4b0b77e9208b6d93610001562cc81fb457/_data", "Destination": "/sharedvolume", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ]
次にデータボリュームを使う側のコンテナを作る。 このときはオプションとして --volumes-from で先ほどのコンテナの名前を指定しよう。
$ docker run --name vcuser1 --volumes-from volumecontainer1 -i -t centos:7 /bin/bash
コンテナに入ると /sharedvolume ディレクトリができている。
# find / -name sharedvolume
/sharedvolume
df の結果をみても /sharedvolume に /dev/sda1 がマウントされていることがわかる。
# df -a | grep sharedvolume /dev/sda1 19049892 2294828 15764340 13% /sharedvolume
ディレクトリの中にファイルを作ってみよう。
# cd /sharedvolume/ # touch helloworld.txt
データボリュームコンテナは複数のコンテナから共有できる。 そこで、もう一台「使う側」のコンテナを起動してみよう。
$ docker run --name vcuser2 --volumes-from volumecontainer1 -i -t centos:7 /bin/bash
新しく起動したコンテナで /sharedvolume ディレクトリの中身を見ると、先ほど作ったファイルが見えている。
# ls /sharedvolume/
helloworld.txt
まとめ
今回は Docker のデータボリュームとデータボリュームコンテナという機能について見てきた。 コンテナは基本的にステートレスに作るのがベストプラクティスみたいだけど、どうしてもステートフルに作らなきゃいけないものはある。 例えば自前でデータベースを用意する場合なんかはその代表で、そこではどうしてもデータの永続化について考えなきゃいけない。 そんなときは今回見てきたデータボリュームとデータボリュームコンテナを使うと便利そうだ。