コンテナでデータを管理

これまでは 基本的な Docker の概念 や、Docker イメージ の導入部に加え、 コンテナのネットワーク について学びました。このセクションでは、どのようにしてコンテナ内やコンテナ間でデータを管理できるか学びます。

それでは、Docker Engine でデータを管理するための、主な手法2つを見ていきます。

  • データ・ボリューム
  • データ・ボリューム・コンテナ

データ・ボリューム

データ・ボリューム (data volume) とは、1つまたは複数のコンテナ内で、特別に設計されたディレクトリです。また、 ユニオン・ファイルシステム (Union File System) をバイパス(迂回)するものです。データ・ボリュームは、データの保持や共有のために、複数の便利な機能を提供します。

  • ボリュームはコンテナ作成時に初期化されます。コンテナのベース・イメージ上で、特定のマウント・ポイント上のデータが指定されている場合、初期化されたボリューム上に既存のデータをコピーします。
  • データ・ボリュームはコンテナ間で共有・再利用できます。
  • データ・ボリュームに対する変更を直接行えます。
  • イメージを更新しても、データ・ボリューム上には影響ありません。
  • コンテナ自身を削除しても、データ・ボリュームは残り続けます。

データ・ボリュームは、データ保持のために設計されており、コンテナのライフサイクルとは独立しています。そのため、コンテナの削除時、Docker は 決して 自動的にボリュームを消さないだけでなく、コンテナから参照されなくなっても”後片付け”をせず、ボリュームはそのままです。

データ・ボリュームの追加

docker createdocker run コマンドで -v フラグを使えば、コンテナにデータ・ボリュームを追加できます。-v を複数回使うことで、複数のデータ・ボリュームをマウントできます。それでは、ウェブ・アプリケーションのコンテナに対して、ボリュームを1つ割り当ててみましょう。

$ docker run -d -P --name web -v /webapp training/webapp python app.py

これはコンテナの中に /webapp という新しいボリュームを作成しました。

注釈

DockerfileVOLUME 命令を使ってもボリュームを作成できます、イメージから作成するあらゆるコンテナに対し、新しいボリュームを追加可能です。

標準では、 Docker はボリュームを読み書き可能な状態でマウントします。あるいは、読み込み専用(read-only) を指定したマウントも可能です。

$ docker run -d -P --name web -v /opt/webapp:ro training/webapp python app.py

ボリュームの場所

docker inspect コマンドを使い、ホスト上でボリュームが使っている場所を探せます。

$ docker inspect web

ボリュームに関する情報を含む、コンテナの詳細設定を表示します。出力は、おそらく次のようなものでしょう。

...
"Mounts": [
    {
        "Name": "fac362...80535",
        "Source": "/var/lib/docker/volumes/fac362...80535/_data",
        "Destination": "/webapp",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
]
...

ホスト上に場所にあたるのは、上の ‘Source’ (ソース)です。コンテナ内のボリューム指定は Destination です。RW の表示は、ボリュームの読み書き可能を意味します。

データ・ボリュームとしてホスト上のディレクトリをマウント

-v フラグの使用はボリューム作成だけではありません。Docker Engine デーモンのホスト上にあるディレクトリも、コンテナにマウント可能です。

$ docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py

このコマンドはホスト側のディレクトリ /src/webapp をコンテナ内の /opt/webapp にマウントします。パス /opt/webapp がコンテナ内のイメージに存在している場合でも、/src/webapp を重複マウントします。しかし、既存の内容は削除しません。マウントを解除したら、内容に対して再度アクセス可能となります。これは、通常の mount コマンドと同じような動作をします。

コンテナ内のディレクトリ は、/src/docs のように、常に絶対パスが必要えす。ホスト側のディレクトリ は相対パスでも 名前 でも構いません。ホスト側のディレクトリ に対して絶対パスを指定したら、Docker は指定したパスを拘束マウント(bind-mount)します。この時に 名前 の値を指定したら、Docker は指定した 名前 のボリュームを作成します。

名前 の値は、アルファベットの文字で開始する必要があります。具体的には、 a-z0-9_ (アンダースコア)、 . (ピリオド)、 - (ハイフン)です。絶対パスの場合は / (スラッシュ)で開始します。

例えば、ホスト側ディレクトリ/foo または foo を指定可能です。/foo 値を指定したら、Docker は(ディレクトリを)拘束したマウントを作成します。foo を指定したら、Docker Engine はその名前でボリュームを作成します。

Mac または Windows 上で Docker Machine を使う場合、Docker デーモンは OS X または Windows ファイルシステム上に限定的なアクセスを行います。Docker Machine は自動的に /Users (OS X) または C:\Users (Windows) ディレクトリのマウントを試みます。つまり、OS X 上で使っているファイルやディレクトリをマウント可能です。

docker run -v /Users/<パス>:/<コンテナ内のパス> ...

Windows 上でも、同様にディレクトリのマウントが使えます。

docker run -v /c/Users/<パス>:/<コンテナ内のパス> ...`

パスには、仮想マシンのファイルシステム上にある全てのパスを指定できます。もし VirtualBox などでフォルダの共有機能を使っているのであれば、追加の設定が必要です。VirtualBox の場合は、ホスト上のフォルダを共有フォルダとして登録する必要があります。それから、Docker の -v フラグを使ってマウントできます。

ホスト上のディレクトリをマウントするのは、テストに便利かも知れません。例えば、ソースコードをコンテナの中にマウントしたとします。次にソースコードに変更を加え、アプリケーションにどのような影響があるのか、リアルタイムで確認できます。ホスト側のディレクトリは絶対パスで指定する必要があります。もしディレクトリが存在しない場合、Docker Engine のデーモンは自動的にディレクトリを作成します。このホスト・パスの自動生成機能は廃止予定です。

Docker ボリュームは、標準で読み書き可能な状態でマウントしますが、読み込み専用としてのマウントもできます。

$ docker run -d -P --name web -v /src/webapp:/opt/webapp:ro training/webapp python app.py

ここでは同じ /src/webapp ディレクトリをマウントしていますが、読み込み専用を示す ro オプションを指定しています。

mount機能の制限 により、ホスト側のソース・ディレクトリ内のサブディレクトリに移動したら、コンテナの中からホスト上のファイルシステムに移動できる場合があります。ただし、悪意を持つユーザがホストにアクセスし、ディレクトリを直接マウントする必要があります。

注釈

ホスト・ディレクトリとは、ホストに依存する性質があります。そのため、ホストディレクトリを Dockerfile でマウントできません。なぜなら、イメージの構築はポータブル(どこでも実行可能な状態の意味)であるべきだからです。全てのホスト環境でホスト・ディレクトリを使えるとは限りません。

共有ストレージをデータ・ボリュームとしてマウント

コンテナにホスト側ディレクトリをマウントできるだけではありません。、いくつかの Docker ボリューム・プラグイン は iSCSI、NFS、FC のような共有ストレージにプロビジョニングやマウントが可能です。

共有ボリュームを使う利点は、ホストに依存しない点です。つまり、あらゆるホスト上で利用可能なボリュームを扱えます。共有ストレージ・バックエンドにアクセス可能なホストと、プラグインさえインストールしておけば、コンテナがどこで動いてもボリュームを利用可能です。

docker run コマンドでボリューム・ドライバを使う方法は1つです。ボリューム・ドライバでボリュームの作成時、他の例のようにパスを指定せず、ボリューム名を指定します。

次のコマンドは my-named-volume という名前付きのボリュームを作成するコマンドです。作成には flocker ボリューム・ドライバを使い、コンテナからは /opt/webapp で利用できるようにします。

$ docker run -d -P \
  --volume-driver=flocker \
  -v my-named-volume:/opt/webapp \
  --name web training/webapp python app.py

あるいは、コンテナを作成する前でも、コンテナが使うボリュームを docker volume create コマンドで作成できます。

次の例は docker volume create コマンドを使い my-named-volume ボリュームを作成します。

$ docker volume create -d flocker --name my-named-volume -o size=20GB
$ docker run -d -P \
  -v my-named-volume:/opt/webapp \
  --name web training/webapp python app.py

ボリューム・プラグインを含む利用可能なプラグインの一覧は こちら をご覧ください。

ボリューム・ラベル

SELinux のようなラベリング・システムでは、コンテナ内にマウントされたボリュームの内容に対しても、適切なラベル付けが行われます。ラベルがなければ、コンテナ内の内容物を使って実行しようとしても、セキュリティ・システムがプロセスの実行を妨げるでしょう。標準では、Docker は OS によって設定されるラベルに対して変更を加えません。

コンテナの内容物に対するラベルを変更するには、ボリュームのマウントにあたり、:z または :Z を末尾に追加可能です(接尾辞)。これらの指定したら、Docker に対して共有ボリュームが再度ラベル付けされたものと伝えます。z オプションは、ボリュームの内容を複数のコンテナが共有していると Docker に伝えます。その結果、Docker は共有コンテント・ラベルとして内容をラベル付けします。Z オプションは、内容はプライベートで共有されるべきではない(private unshared)ラベルと Docker に伝えます。現在のコンテナのみが、プライベートに(個別に)ボリュームを利用可能です。

ホスト上のファイルをデータ・ボリュームとしてマウント

-v フラグはホストマシン上のディレクトリ だけ ではなく、単一のファイルに対してもマウント可能です。

$ docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash

これは新しいコンテナ内の bash シェルを流し込むものです。コンテナを終了する時に、ホスト上の bash 履歴に対して、コンテナ内で実行したコマンドを履歴として記録します。

注釈

vised --in-place など、多くのツールによる編集は、結果としてiノードを変更する場合があります。Docker v1.1.0 までは、この影響により “sed: cannot rename ./sedKdJ9Dy: Device or resource busy” (デバイスまたはリソースがビジー) といったエラーが表示されることがありました。マウントしたファイルを編集したい場合、親ディレクトリのマウントが最も簡単です。

データ・ボリューム・コンテナの作成とマウント

データに永続性を持たせたい場合(データを保持し続けたい場合)、例えばコンテナ間での共有や、データを保持しないコンテナから使うには、名前を付けたデータ・ボリューム・コンテナ(Data Volume Container)を作成し、そこにデータをマウントするのが良い方法です。

ボリュームを持ち、共有するための新しい名前付きコンテナを作成しましょう。training/postgres イメージを再利用し、全てのコンテナから利用可能なレイヤを作成し、ディスク容量を節約します。

$ docker create -v /dbdata --name dbdata training/postgres /bin/true

次に、--volumes-from フラグを使い、他のコンテナから /dbdata ボリュームをマウント可能です。

$ docker run -d --volumes-from dbdata --name db1 training/postgres

あるいは、他からも。

$ docker run -d --volumes-from dbdata --name db2 training/postgres

この例では、postgres イメージには /dbdata と呼ばれるディレクトリが含まれています。そのため dbdata コンテナからボリュームをマウントする(volumes from)とは、元の postgres イメージから /dbdata が隠された状態です。この結果、dbdata コンテナからファイルを表示しているように見えます。

--volumes-from パラメータは複数回利用できます。複数のコンテナから、複数のデータボリュームを一緒に扱えます。

また、ボリュームのマウントは連鎖(chain)できます。この例では、dbdata コンテナのボリュームは db1 コンテナと db2 コンテナからマウントできるだけとは限りません。

$ docker run -d --name db3 --volumes-from db1 training/postgres

ボリュームをマウントしているコンテナを削除する場合、ここでは1つめの dbdata コンテナや、派生した db1db2 コンテナのボリュームは削除されません。ディスクからボリュームを削除したい場合は、最後までボリュームをマウントしていたコンテナで、必ず docker rm -v を実行する必要があります。この機能を使えば、コンテナ間でのデータボリュームの移行や更新を効率的に行えます。

注釈

コンテナ削除時、-v オプションでボリュームを消そうとしなくても、Docker は何ら警告を表示しません。 -v オプションに使わずにコンテナを削除した場合、ボリュームは最終的にどのコンテナからも参照されない “宙づり”(dangling) ボリュームになってしまいます。宙づりボリュームは除去が大変であり、多くのディスク容量を使用する場合もあります。このボリューム管理の改善については、現在 プルリクエスト#14214 において議論中です。

データ・ボリュームのバックアップ・修復・移行

ボリュームを使った他の便利な機能に、バックアップや修復、移行があります。これらの作業を使うには、新しいコンテナを作成する時に --volumes-from フラグを使い、次のようにボリュームをマウントします。

$ docker run --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata

ここでは新しいコンテナを起動し、dbdata コンテナからボリュームをマウントします。そして、ローカルのホスト上のディレクトリを /backup としてマウントします。最終的に、dbdata ボリュームに含まれる内容をバックアップするため、 tar コマンドを使い /backup ディレクトリの中にあるファイルを backup.tar に通します。コマンドの実行が完了したら、コンテナは停止し、dbdata ボリュームのバックアップが完了します。

これで同じコンテナに修復(リストア)や、他のコンテナへの移行もできます。新しいコンテナを作成してみましょう。

$ docker run -v /dbdata --name dbdata2 ubuntu /bin/bash

それから、新しいコンテナのデータ・ボリュームにバックアップしたファイルを展開します。

$ docker run --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar"

この手法を使うことで、好みのツールを用いた自動バックアップ、移行、修復が行えます。

ボリューム共有時の重要なヒント

複数のコンテナが1つまたは複数のデータ・ボリュームを共有できます。しかしながら、複数のコンテナが1つの共有ボリュームに書き込むことにより、データ破損を引き起こす場合があります。アプリケーションが共有データ・ストアに対する書き込みに対応した設計かどうか、確認してください。

データ・ボリュームは Docker ホストから直接アクセス可能です。これが意味するのは、データ・ボリュームは通常の Linux ツールから読み書き可能です。コンテナとアプリケーションが直接アクセスできることを知らないことにより、データの改竄を引き起こすことは望ましくありません。

次のステップ

これまでは、どのようにして Docker を使うのかを少々学びました。次は Docker と Docker Hub で利用可能なサービスを連携し、自動構築(Automated Build)やプライベート・リポジトリ(private repository)について学びます。

Docker Hub の操作 に移動します。