CUBE SUGAR CONTAINER

技術系のこと書きます。

Docker でコンテナにマウントできるボリュームについて

Docker では、ボリュームという機能を使うことで、ホストや外部のストレージをコンテナにマウントできる。 今回は、それらについてざっと使い方を見ていく。 紹介するボリュームの種類は次のとおり。

  • bind mount
  • volume mount
  • tempfs mount

使った環境は以下のとおり。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.5 LTS
Release:    18.04
Codename:   bionic
$ uname -r
4.15.0-124-generic
$ docker version
Client: Docker Engine - Community
 Version:           20.10.1
 API version:       1.41
 Go version:        go1.13.15
 Git commit:        831ebea
 Built:             Tue Dec 15 04:34:59 2020
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.1
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       f001486
  Built:            Tue Dec 15 04:32:40 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.3
  GitCommit:        269548fa27e0089a8b8278fc4fc781d7f65a939b
 runc:
  Version:          1.0.0-rc92
  GitCommit:        ff819c7e9184c13b7c2607fe6c30ae19403a7aff
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

もくじ

bind mount

はじめに、従来からある bind mount について。 このやり方では、コンテナの起動時に Docker ホストのディレクトリパスを指定すると、そのディレクトリをコンテナにマウントできる。

たとえば、カレントディレクトリに確認用のテキストファイルを書き込んでおこう。

$ echo "Hello, World" > greet.txt

bind mount するときは、一般的には -v または --volume オプションを使うことが多い。 このオプションでは、左辺にマウントしたい Docker ホストのディレクトリを絶対パスで指定する。 そして、コロンを挟んだ右辺にコンテナでマウント先のディレクトリを指定する。

以下では、カレントディレクトリをコンテナの /mnt にマウントしている。

$ docker container run \
    --rm \
    -v $(pwd):/mnt \
    -it ubuntu:18.04 \
    bash

起動したコンテナの中で /mnt 以下を確認すると、先ほど作ったテキストファイルが確認できる。

root@4829d9a83fe9:/# cat /mnt/greet.txt 
Hello, World

ただし、最近では --mount というオプションも使える。 このオプションでは、マウントの種類とマウント元、マウント先を指定することで、より明示的な指示になっている。

$ docker container run \
    --rm \
    --mount type=bind,source=$(pwd),destination=/mnt \
    -it ubuntu:18.04 \
    bash

結果は先ほどと変わらないので割愛する。

volume mount

続いては volume mount というやり方を扱う。 bind mount ではマウント元のディレクトリを指定していた。 これは直感的な反面、マウントするディレクトリの管理が煩雑になるデメリットがある。 一方、volume mount では、Docker が管理している領域にマウント用のディレクトリが作られて、それを名前で指定できる。

たとえば、以下のようにしてボリュームを作成しておく。 ここでは example-volume という名前でボリュームを作った。

$ docker volume create example-volume

ボリュームの一覧を見ると、ちゃんとボリュームが作成されている。 ここで着目すべき点として DRIVER というカラムがある。 先ほど作ったボリュームでは、値が local になっている。 これは、ドライバを変更することで Docker ホスト以外の場所もボリュームとして管理できることを表している。 公式のドキュメントでも SSHFS や NFS を使ってコンテナ間でストレージを共有する方法が示されている。

$ docker volume ls
DRIVER    VOLUME NAME
local     example-volume

volume mount するときは、bind mount と同じように -v オプションが使える。 先ほどは左辺に Docker ホストのディレクトリを指定したのに対し、今回はボリュームの名前を指定する。

$ docker container run \
    --rm \
    -v example-volume:/mnt \
    -it ubuntu:18.04 \
    bash

あるいは、--mount オプションを使っても良い。 こちらの場合は typebind ではなく volume を指定した上で、source にボリュームの名前を指定する。

$ docker container run \
    --rm \
    --mount type=volume,source=example-volume,destination=/mnt \
    -it ubuntu:18.04 \
    bash

初期状態では、マウントされたディレクトリにファイルが何もない。 そこで、一旦は確認用のファイルを書き込んでコンテナを終了しておこう。

root@10cc21f927a7:/# ls /mnt
root@10cc21f927a7:/# echo "Hello, World" > /mnt/greet.txt
root@10cc21f927a7:/# exit
exit

そして、改めて同じボリュームをマウントするコンテナを起動する。

$ docker container run \
    --rm \
    --mount type=volume,source=example-volume,destination=/mnt \
    -it ubuntu:18.04 \
    bash

すると、次のように先ほど作成したファイルが存在することが確認できる。

root@324a3d594723:/# ls /mnt
greet.txt
root@324a3d594723:/# cat /mnt/greet.txt 
Hello, World

ちなみに、ボリュームの場所は次のようにして調べることができる。 まずはコンテナの識別子か名前を確認しておく。

$ docker ps
CONTAINER ID   IMAGE          COMMAND   CREATED          STATUS          PORTS     NAMES
324a3d594723   ubuntu:18.04   "bash"    59 seconds ago   Up 58 seconds             reverent_golick

そして、docker inspect を使えばボリュームの情報が確認できる。 これで、"/var/lib/docker/volumes/example-volume/_data" にマウント元のディレクトリがあることがわかった。

$ docker inspect 324a3d594723 | grep example-volume
                "example-volume:/mnt"
                "Name": "example-volume",
                "Source": "/var/lib/docker/volumes/example-volume/_data",

Docker ホスト側でディレクトリの内容を確認しておこう。 ちゃんとファイルが存在することがわかる。

$ sudo ls /var/lib/docker/volumes/example-volume/_data
greet.txt
$ sudo cat /var/lib/docker/volumes/example-volume/_data/greet.txt
Hello, World

tmpfs mount

続いては、これまでとちょっと毛色が異なる tmpfs mount を紹介する。 tmpfs は Linux のファイルシステムの一種で、主記憶装置を使った (つまりオンメモリの) ファイルシステムになっている。 もちろん、オンメモリということはデータは永続化されず揮発する。

tmpfs mount をするときは、専用のオプションとして --tmpfs が使える。 指定するのはコンテナのマウント先だけ。

$ docker container run \
    --rm \
    --tmpfs /mnt \
    -it ubuntu:18.04 \
    bash

あるいは、--mount オプションで typetmpfs を指定しても良い。

$ docker container run \
    --rm \
    --mount type=tmpfs,destination=/mnt \
    -it ubuntu:18.04 \
    bash

起動したコンテナを確認すると、/mnt ディレクトリに tmpfs でマウントされている。

root@71b7c20cbcb8:/# df -h | grep /mnt
tmpfs           7.9G     0  7.9G   0% /mnt

dd(1) を使ってちょっと大きめのファイルを書くこんでみると、別の場所に比べてスループットが高いことが確認できる。

root@71b7c20cbcb8:/# dd if=/dev/zero of=/mnt/zeros bs=1MB count=2000
2000+0 records in
2000+0 records out
2000000000 bytes (2.0 GB, 1.9 GiB) copied, 0.651633 s, 3.1 GB/s
root@71b7c20cbcb8:/# dd if=/dev/zero of=/zeros bs=1MB count=2000
2000+0 records in
2000+0 records out
2000000000 bytes (2.0 GB, 1.9 GiB) copied, 1.93574 s, 1.0 GB/s

いじょう。

参考

docs.docker.com