【Dockerの最新機能を使ってみよう】Dockerのボリュームプラグインとストレージドライバについて知る

 新たなサーバー環境構築ツールとして普及が始まっているDockerは、その開発も積極的に行われている。そこで本連載記事では、4回に渡って最近Dockerに実装された新機能について紹介していく。今回は、Dockerのボリュームプラグインとストレージドライバについて紹介する。

Dockerの「ボリュームプラグイン」と「ストレージドライバ」

 昨今のDockerでは、各機能を個別のコンポーネントに分離する方向で開発が進められている。ストレージ関連の処理もすでに分離されており、「ボリュームプラグイン」やストレージドライバ」を使って目的や環境に応じた設定を行うことが可能になっている。この2つは名前が似通っているため混乱しやすいが、まったく別のものだ(図1)。

図1 ボリュームプラグインとストレージドライバ
図1 ボリュームプラグインとストレージドライバ

 まずボリュームプラグインだが、これはコンテナ外のストレージをコンテナ内に「ボリューム」としてマウントするためのプラグイン機構だ。Dockerではコンテナを削除するとコンテナ内のファイルシステム内に加えた変更点はすべて破棄されてしまうが、ボリュームを利用することで、簡単にコンテナ内からコンテナ外のストレージにファイルを保存し、データを永続化できる。当初はDockerホスト上のディレクトリをコンテナ内にマウントする機能のみが提供されていたが、その後Docker 1.8ではプラグイン機構が導入され、さまざまなストレージデバイスをコンテナにマウントできるようになっている。

 いっぽうのストレージドライバは、Dockerがイメージやコンテナを管理する際に使用するものだ。Dockerではイメージやコンテナ内のファイルシステムに加えられた変更を差分形式で保持することで、効率良くストレージを利用できるよう設計されている。当初Dockerはこれを実現するためにaufsのような特別なファイルシステムを使用していたのだが、バージョン0.7でストレージドライバ機構が導入され、Device Mapperやvfsといった機構を利用したイメージの管理が可能となった。その後、OverlayFSやBtrfs、ZFSといった差分管理機構を持つファイルシステムを活用するドライバが追加されている。

 以下ではこのボリュームプラグインおよびストレージドライバについて、実際の利用例と設定方法を紹介していく。なお、特に言及のない限り利用した検証環境はFedora 23で、Dockerのバージョンは1.10.3だ。

コンテナ内にコンテナ外のストレージをマウントするための「ボリューム」機能

 前述の通り、2015年6月にリリースされたDocker 1.7ではボリュームプラグイン機構が利用できるようになった。それまではDockerを実行するホスト(Dockerホスト)上のディレクトリのみがコンテナ内にマウントできたが、プラグインを利用することでネットワーク上にあるストレージなどをコンテナに直接マウントできるようになる。以下ではまず基本となるDockerのボリューム機構について説明し、続いてプラグインの利用方法について紹介する。

Dockerホストのディレクトリをコンテナ内にマウントする

 Dockerでは、「docker run」コマンドでコンテナを起動する際に「-v <ホスト上ディレクトリ>:<マウント先>」オプションを利用することで、Dockerホスト上の指定したディレクトリをコンテナ内の指定したディレクトリにマウントできる。また、「-v <ホスト上ディレクトリ>:<マウント先>:ro」のようにマウント先指定のあとに「:ro」を付けることで、リードオンリーでマウントを行うことも可能だ。

 たとえば次の例では、Dockerホスト上の/var/testdataディレクトリを、コンテナ内の/dataディレクトリにマウントしている。

# docker run -ti -v /var/testdata:/data busybox

 このようにしてマウントしたディレクトリ内に永続化したいファイルを保存することで、コンテナを削除してもそのデータを残しておけるようになる。

 「-v」オプションでは、次のようにDockerホスト上のディレクトリ指定を省略することも可能だ。

# docker run -ti -v /data --name test01 busybox

 この場合、Dockerはvar/lib/docker/volumesディレクトリ以下にランダムな名前を持つディレクトリを作成し、そのディレクトリがコンテナにマウントされる。コンテナにマウントされているボリュームは「docker inspect」コマンドで確認できる。

 たとえば先のように起動したコンテナの詳細を確認すると、以下のように「”Mounts”:」以下で「e0553ba36bf2c33b83fada68610e5103b6596c9e7dcc17622c277e180cd7f317」という名前のボリュームがマウントされており、このボリュームはDockerホストの/var/lib/docker/volumes/e0553ba36bf2c33b83fada68610e5103b6596c9e7dcc17622c277e180cd7f317/_dataディレクトリに対応していることが分かる。

# docker inspect test01
  
  
        "Mounts": [
            {
                "Name": "e0553ba36bf2c33b83fada68610e5103b6596c9e7dcc17622c277e180cd7f317",
                "Source": "/var/lib/docker/volumes/e0553ba36bf2c33b83fada68610e5103b6596c9e7dcc17622c277e180cd7f317/_data",
                "Destination": "/data",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
  
  

 なお、この例はDocker 1.10.3でのものだ。Docker 1.7系以前では次のように「Mounts」ではなく「Volumes」という項目でマウントされているボリュームの情報が表示される。

    "Volumes": {
        "/data": "/var/lib/docker/vfs/dir/b1adacb83f130cdd2fffbbec8e77c70c23685c5278cc34eba12ed05be81ff669"
    },
    "VolumesRW": {
        "/data": true
    }

 このようにして作成されたボリュームは、前述のとおりコンテナを削除しても削除されない。ボリュームをコンテナと一緒に削除したい場合、コンテナを削除する「docker rm」コマンドを「-v」オプション付きで実行すれば良い。

# docker rm -v <コンテナ名>

 あるコンテナがマウントしているボリュームを、ほかのコンテナにマウントさせることも可能だ。コンテナの作成時、「–volumes-from <コンテナ名>」オプションを指定することで、指定したコンテナにマウントされているすべてのボリュームを新たに作成するコンテナにマウントできる。

 次の例は「test02」というコンテナを作成するとともに、先ほど作成した「test01」というコンテナにマウントされているボリュームをtest02コンテナにもマウントさせるものだ。

# docker run -ti --volumes-from test01 --name test02 busybox

 test01では/dataディレクトリにボリュームがマウントされており、このボリュームがtest02でも同じ/dataディレクトリにマウントされる。この場合、/dataディレクトリは両方のコンテナで共有され、test01コンテナで/dataディレクトリに対し変更を行うと、それがtest02コンテナの/dataディレクトリにも反映される(逆も同様)。

Docker 1.8以降のボリューム関連の新機能

 Docker 1.7ではボリューム関連機能のコードが一新され、プラグインによる拡張が可能となった。さらにDocker 1.8以降ではこれらの機能が標準で利用可能となり、多くの機能強化が追加されている。

 まず、Docker 1.8では「-v <ボリューム名>:<マウント先>」のように指定することで、作成するボリュームに名前を付けることが可能となった。次の例は、「testvolume01」という名前のボリュームを作成してコンテナにマウントするものだ。

# docker run -t -i -v testvolume01:/data --name test03 busybox

 なお、ボリューム名として「/」で始まる文字列を与えてしまうと、Dockerホスト上のディレクトリを指定してマウントすることになってしまうので注意したい。

 作成したボリュームは、その名前を指定して別のコンテナにマウントさせることができる。この場合、–volumes-fromオプションとは異なり、最初にボリュームを作成したコンテナでのマウント先とは異なるマウント先を指定することも可能だ。

 たとえば新たに「test04」というコンテナを作成し、その/data2ディレクトリに先ほど作成したtestvolume01ボリュームをマウントしたい場合、次のように指定する。

# docker run -t -i -v testvolume01:/data2 --name test04 busybox

 また、Docker 1.9以降では「docker volume」コマンドが導入され、こちらでボリュームの管理が可能となった。たとえば、「docker volume ls」コマンドでボリューム一覧を確認できる。

# docker volume ls
DRIVER              VOLUME NAME
local               testvolume01

 ボリュームの詳細については「docker volume inspect」コマンドで確認できる。

# docker volume inspect testvolume01
[
    {
        "Name": "testvolume01",
        "Driver": "local",
        "Mountpoint": "/var/lib/docker/volumes/testvolume01/_data"
    }
]

 ボリュームを作成する「docker volume create」コマンドも用意されている。このとき、「–name」オプションで作成するボリュームの名前を指定できる。

# docker volume create --name testvolume02
testvolume02

 名前を指定しない場合、ランダムな文字列が名前として使用される。

# docker volume create
1a7a1bd3f4a7e359a685cb5a90b4d47849da631ccf94640f5d740c69fae9d75b

 作成したボリュームは次のように「docker run」コマンドの実行時に「-v」オプションを使ってコンテナにマウントできる。

# docker run -d -i -v testvolume02:/data --name test05 busybox

 ボリュームの削除は「docker volume rm」コマンドで行える。

# docker volume rm testvolume01
testvolume01

 ちなみにDocker 1.8以前ではボリュームを個別に指定して削除するインターフェイスは存在せず、手動で/var/lib/docker/volumesディレクトリ以下のディレクトリを削除する必要があった。また、Docker 1.10以降では「docker rm -v」コマンドでコンテナと一緒にボリュームを削除しようとした場合でも、名前が付けられたボリュームについては削除されないよう仕様変更が行われている。

ローカル以外のストレージをボリュームとして利用する

 Docker 1.8で利用可能になったボリュームプラグイン機能を利用することで、Dockerホスト以外のストレージをボリュームとして利用できるようになった。誰もが独自のプラグインを開発できるよう仕様も公開されており、すでにいくつかのプラグインがサードパーティによって開発されている(Docker公式ドキュメントの「Understand Engine plugins」ページ)。

 今回はこのうち、NFSやWindowsファイル共有(Samba/CIFS)、Amazon EFSを使ってボリュームを作成できる「docker-volume-netshare」(以下、netshareプラグイン)を紹介する。

 netshareプラグインを利用するには、まずプラグインのインストールが必要となる。ソースコードからビルドしても良いが、今回はリリースページで公開されているバイナリを利用する。今回はコンパイル済みバイナリ(docker-volume-netshare_0.16_linux_amd64.tar.gz)を利用した。

 ダウンロードしたアーカイブファイルにはドキュメントとともに「docker-volume-netshare」というバイナリファイルが含まれているので、これを/usr/local/binディレクトリ以下にコピーする。

# tar xvzf ../docker-volume-netshare_0.16_linux_amd64.tar.gz
# cd docker-volume-netshare_0.16_linux_amd64/
# cp docker-volume-netshare /usr/local/bin/

 また、NFSで共有されているディレクトリをボリュームとして利用する際は、nfsを利用するためのパッケージを別途インストールしておく必要がある。Red Hat Enterprise LinuxやCentOS、Fedora環境であれば、「nfs-utils」パッケージを、UbuntuやDebian環境では「nfs-common」パッケージをインストールしておけば良い。

 netshareプラグインを利用する際は、使用するコンテナの起動前にこのdocker-volume-netshareバイナリを実行しておく必要がある。実行時に指定する引数についてはREADMEファイルを参照して欲しいが、NFSを利用する場合は以下のように「nfs」を引数として指定する。

# /usr/local/bin/docker-volume-netshare nfs &

 netshareプラグインを使ってNFS共有されているディレクトリ上にボリュームを作成するには、「docker volume create」コマンドを利用する。ここで、「-d nfs」オプションでnfsドライバを利用するよう指示し、「–name」オプションに「<NFSサーバーのホスト名>/<パス>」を指定することで、NFS共有されているディレクトリをボリュームとして扱える。次の例は、「example.com」というホスト上の「/var/nfs_exports」というディレクトリを使用するものだ。

# docker volume create -d nfs --name example.com/var/nfs_exports
example.com/var/nfs_exports

 作成したボリュームは、以下のように「-v <コンテナ名>:<マウント先ディレクトリ>」オプションを指定することでコンテナにマウントできる。

docker run -ti -v example.com/var/nfs_exports:/data --name test01 busybox

 なお、今回はdocker-volume-netshareバイナリを直接実行しているが、運用環境などで利用する場合はsystemd等でサービスとして起動するように設定しておくほうが良いだろう。そのための設定ファイルとして、systemd設定ファイルおよびそこで使用する設定ファイルが公開されている。また、Debian/Ubuntu向けにはdebパッケージが提供されているので、そちらを利用すれば良いだろう。