ボリュームの使用

Docker コンテナによって作成され、かつ、使われるデータを保持するために、ボリュームは適した仕組みです。 バインド マウント はホストマシン上のディレクトリ構造と OS に依存しますが、ボリュームは Docker によって完全に管理されます。バインド マウントと比べ、ボリュームには複数の利点があります。

  • ボリュームはバインド マウントに比べ、バックアップや 移動(migrate) が簡単
  • Docker CLI コマンドや Docker API を使ってボリュームを管理可能
  • ボリュームは Linux と Windows コンテナの両方で動作
  • ボリュームは複数のコンテナ間で、より安全に共有可能
  • ボリューム ドライバによって、リモートホストやクラウドプロバイダに保管でき、ボリューム内容の暗号化や、他の機能性も追加可能
  • 新しいボリュームは、コンテナによって内容を事前に入力可能
  • Docker Desktop 上のボリュームは、Mac と Windows ホストからのバインド マウントに比べ、より高い性能

加えて、コンテナの書き込み可能なレイヤーにデータを保持するためには、ボリュームがより良い選択です。これは、ボリュームによってコンテナが使用する容量が増えませんし、対象となるコンテナのライフサイクル外でボリュームの内容が存在し続けます。

Docker ホスト上のボリューム

もしもコンテナが保持する必要がない状態のデータを生成する場合は、 tmpfs マウント の使用を検討ください。それにより、データがどこかに保持される続けるのを防止し、さらに、コンテナの書き込み可能なレイヤーへの書き込みを避けるため、コンテナのパフォーマンスを向上します。

ボリュームは rprivate バインド プロパゲーション(bind propagation) を使いますが、バインド プロパゲーションはボリューム用に設定できません。

-v と --mount フラグの選択

一般的に、 --mount は説明的かつ冗長です。最大の違いは、 -v 構文は1つのフィールドに全てのオプションをつなげるのに対し、 --mount 構文はそれらを分けます。以下は各フラグの比較です。

ボリューム ドライバのオプションの指定が必要であれば、 --mount を使う必要があります。

  • -v--volume :コロン記号( : )で区切られた、3つのフィールドで構成。フィールドは正しい順番で記述する必要があり、それぞれのフィールドの意味は直ちに分からない
    • 名前付きボリュームの場合、1つめのフィールドはボリューム名であり、ホストマシン上でユニークな必要がある。無名ボリュームでは、1つめのフィールドは省略する。
    • 2つめのフィールドは、コンテナ内にマウントされるファイルやディレクトリがどこにあるかのパス。
    • 3つめのフィールドはオプションで、 ro のようなオプションをカンマで区切ったリスト。これらオプションについては、後ほど扱う。
  • --mount :複数のキーバリューのペアで構成。それらは、カンマで区切られ、かつ、それぞれが <key>=<value> のセットで構成。 --mount 構文は -v--volume よりも冗長だが、キーの順番は意味が無く、フラグの値は理解しやすい。
    • マウントの type (形式)は、 bindvolumetmpfs のどれか。このトピックで扱うのはボリュームのため、形式は常に volume
    • マウントの source (マウント元)。名前付きボリュームでは、これがボリューム名になる。無名ボリュームでは、このフィールドは省略。 source もしくは src として指定。
    • destination (マウント先)の値は、コンテナ内にマウントされるファイルやディレクトリのパスがどこかを示す。 destinationdsttarget のどれかを指定。
    • readonly (読み込み専用)オプションがあれば、バインド マウントは 読み込み専用としてコンテナ内にマウント される。 readonly ``ro で指定。
    • volume-opt オプションは複数回指定でき、オプション名と値で構成されるキーバリューのペアをとる。

警告

外部の CSV パーサから値をエスケープ

ボリューム ドライバが、オプションでカンマ区切りのリストを受け付ける場合、外部の CSV パーサから値をエスケープする必要があります。 volume-opt をエスケープするには、ダブルクォート( " )で囲み、マウント パラメータ全体をシングルクォート( ' )で囲みます。 たとえば、 local ドライバは o パラメータ内でカンマで区切ったマウントオプションを受け入れます。以下の例は、リストをエスケープするための正しい方法を表します。

$ docker service create \
    --mount 'type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>,volume-driver=local,volume-opt=type=nfs,volume-opt=device=<nfs-server>:<nfs-path>,"volume-opt=o=addr=<nfs-address>,vers=4,soft,timeo=180,bg,tcp,rw"'
    --name myservice \
    <IMAGE>

以降の例では --mount-v 構文の両方を可能であれば表し、かつ、 --mount を先に表します。

-v--mount との挙動の違い

バインド マウントとは異なり、 --mount-v フラグの両方がボリュームに対するオプションを全て利用できます。

サービスにボリュームを使う場合は、 --mount のみサポートされます。

ボリュームの作成と管理

バインド マウントとは異なり、あらゆるコンテナの範囲外でボリュームの作成や管理ができます。

ボリューム作成:

$ docker volume create my-vol

ボリューム一覧:

$ docker volume ls

local               my-vol

ボリュームの :ruby:`調査 <inspect>` :

$ docker volume inspect my-vol
[
    {
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
        "Name": "my-vol",
        "Options": {},
        "Scope": "local"
    }
]

ボリュームを削除:

$ docker volume rm my-vol

ボリュームを使ってコンテナを起動

起動するコンテナにボリュームが存在していなければ、 Docker はボリュームを作成します。以下の例はボリューム myvol2 をコンテナ内の /app にマウントします。

以下の -v--mount 例は、どちらも同じ結果になります。一度実行すると、 devtest コンテナと myvol2 ボリュームを削除しないと、両方実行できません。

  • --mount
$ docker run -d \
  --name devtest \
  --mount source=myvol2,target=/app \
  nginx:latest
  • -v
$ docker run -d \
  --name devtest \
  -v myvol2:/app \
  nginx:latest

docker inspect devtest を使い、ボリュームが作成され、正しくマウントされているのを確認します。 Mounts セクションを見ます。

"Mounts": [
    {
        "Type": "volume",
        "Name": "myvol2",
        "Source": "/var/lib/docker/volumes/myvol2/_data",
        "Destination": "/app",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
],

この表示は、マウントしているのはボリュームであり、正しいマウント元(Source)とマウント先(Destination)が指定され、かつ、マウントは読み書きできます。

コンテナを停止し、ボリュームを削除します。ボリュームの削除は別の手順なので注意してください。

$ docker container stop devtest

$ docker container rm devtest

$ docker volume rm myvol2

docker-compose でボリュームを使う

単一の docker compose サービスとボリュームは,次のようなものです。

version: "3.9"
services:
  frontend:
    image: node:lts
    volumes:
      - myapp:/home/node/app
volumes:
  myapp:

docker-compose up を始めて実行すると、ボリュームが作成されます。続く実行でも、同じボリュームが再利用されます。

ボリュームは docker volume create によって、 compose の外でも直接作成できます。その場合、以下のように docker-compose.yml の中で参照します。

version: "3.9"
services:
  frontend:
    image: node:lts
    volumes:
      - myapp:/home/node/app
volumes:
  myapp:
    external: true

compose でボリュームを使うための詳しい情報は、 compose リファレンス をご覧ください。

ボリュームとサービスを起動

サービスの起動とボリュームの定義時、各サービス コンテナは自身のローカルボリュームを使います。 local ボリューム ドライバを使う場合は、コンテナ間でデータを共有できませんが、いくつかのボリューム ドライバは共有ストレージをサポートします。Docker for AWS と Docker for Azure の両方で、 Cloudstor プラグインを使ってのデータ保管をサポートします。

以下の例は、4つのレプリカを持つ nginx サービスを起動し、それぞれが myvol2 と呼ぶローカルボリュームを使います。

$ docker service create -d \
  --replicas=4 \
  --name devtest-service \
  --mount source=myvol2,target=/app \
  nginx:latest

サービスが実行中かどうかを確認するには、 docker service ps devtest-service を使います。

$ docker service ps devtest-service

ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
4d7oz1j85wwn        devtest-service.1   nginx:latest        moby                Running             Running 14 seconds ago

サービスを削除すると、全てのタスクも停止します。

$ docker service rm devtest-service

サービスを削除しても、サービスによって作成されたボリュームは削除されません。ボリュームの削除とは、別のステップです。

サービスに対する構文の違い

docker service create コマンドは -v--volume フラグをサポートしません。ボリュームをサービスのコンテナ内にマウントするには、 --mount フラグを使用する必要があります。

コンテナを使ってボリュームを加える

コンテナの作成時、先述の通り新しいボリュームを作成し、コンテナが持っているファイルやディレクトリ内に、ディレクトリとしてマウントされます(先ほどの /app/ のように)。このディレクトリの内容は、ボリュームからコピーされたものです。コンテナがマウントした後にボリュームを使用すると、同じボリュームを使う他のコンテナからも、作成された内容にアクセスできます。

これを説明するために、以下の例では nginx コンテナを起動し、コンテナの /usr/share/nginx/html ディレクトリ内に新しいボリューム nginx-vol を作成します。このディレクトリは Nginx の HTML コンテンツをデフォルトで置く場所です。

例にある --mount-v は、どちらも同じ結果になります。

  • --mount
$ docker run -d \
  --name=nginxtest \
  --mount source=nginx-vol,destination=/usr/share/nginx/html \
  nginx:latest
  • -v

    $ docker run -d

    --name=nginxtest -v nginx-vol:/usr/share/nginx/html nginx:latest

これらの例を試した後は、以下のコマンドでコンテナとボリュームを削除します。ボリュームの削除は別のステップなので、気を付けてください。

$ docker container stop nginxtest

$ docker container rm nginxtest

$ docker volume rm nginx-vol

. _use-a-read-only-volume: 読み込み専用のボリュームを使用 ==============================

アプリケーション開発では、コンテナがバインド マウントへの書き込みを必要とするなら、変更は Docker ホスト側へと反映されます。一方で、コンテナがデータの読み込みだけを必要とする場合があります。複数のコンテナは同じボリュームをマウントできるのを思い出してください。これがあれば、一方は読み書きできるようにマウントし、もう一方では読み込み専用としてのマウントが、同時に行えます。

以下は前述の例を変更したもので、コンテナ内へのマウントポイントの後に、 ro をオプションのリスト(デフォルトは空)に追加し、ディレクトリを 読み込み専用(read only) のボリュームとしてマウントします。複数のオプションを指定するには、それらをカンマで区切ります。

例にある --mount-v は、どちらも同じ結果になります。

  • --mount

    $ docker run -d \
      --name=nginxtest \
      --mount source=nginx-vol,destination=/usr/share/nginx/html,readonly \
      nginx:latest
    
  • --v

    $ docker run -d \
      --name=nginxtest \
      -v nginx-vol:/usr/share/nginx/html:ro \
      nginx:latest
    

読み込み専用のマウントが正しく作成されたかどうかを確認するには、 docker inspect nginxtest を使います。 Mounts セクションを探します。

"Mounts": [
    {
        "Type": "volume",
        "Name": "nginx-vol",
        "Source": "/var/lib/docker/volumes/nginx-vol/_data",
        "Destination": "/usr/share/nginx/html",
        "Driver": "local",
        "Mode": "",
        "RW": false,
        "Propagation": ""
    }
],

コンテナを停止、削除してから、ボリュームを削除します。ボリュームの削除は別のステップです。

$ docker container stop nginxtest

$ docker container rm nginxtest

$ docker volume rm nginx-vol

マシン間のデータ共有

耐障害性(fault-tolerant) のアプリケーションを構築する場合は、同じファイルにアクセスするために、同じサービスにタイして複数のレプリカの設定が必要になるでしょう。

共有ストレージ

これをアプリケーションの開発時に実現するには、いくつかの方法があります。1つは Amazon S3 のようなクラウド オブジェクト ストレージ システム上に、アプリケーションがファイルを保存するような仕組み(ロジック)の追加です。他の手法は、NFS や Amazon S3 のような外部のストレージ システム上への書き込みをサポートしているドライバを使っての、ボリュームの作成です。

ボリュームドライバにより、アプリケーションの仕組みから、基礎となるストレージシステムを抽象化できるようになります。たとえば、サービスが NFS ドライバでボリュームを使う場合であれば、アプリケーションの仕組みを変更しなくても、クラウド上にデータを保管するなど、異なるドライバを使ってもサービスを更新できます。

ボリュームドライバをの使用

docker volume create を使ってボリュームの作成時や、まだ作成していないボリュームを使うコンテナの起動時に、ボリュームドライバを指定できます。以下は vienx/sshfs ボリュームドライバを使う例であり、第一に、スタンドアロン ボリュームを作成し、それから新しいボリュームを作成するコンテナを起動します。

初期セットアップ

この例では2つのノードがあるものと想定しています。そのうち1つは Docker ホストであり、2つめに SSH を使って接続できます。

Docker ホスト上で、 vienx/sshfs プラグインをインストールします。

$ docker plugin install --grant-all-permissions vieux/sshfs

ボリュームドライバを使ってボリュームを作成

この例では SSH パスワードを指定しますが、2つのホストで鍵設定を共有していれば、パスワードを省略できます。各ボリュームドライバでには設定可能なオプションが無い場合と複数ある場合があり、指定する場合は -o フラグを使います。

$ docker volume create --driver vieux/sshfs \
  -o sshcmd=test@node2:/home/test \
  -o password=testpassword \
  sshvolume

ボリュームドライバを使い、ボリュームを作成するコンテナを起動

この例では SSH パスワードを指定しますが、2つのホストで鍵設定を共有していれば、パスワードを省略できます。各ボリュームドライバでには設定可能なオプションが無い場合と複数ある場合があります。 ボリュームドライバにオプションを渡す必要がある場合は、ボリュームのマウントに -v ではなく --mount フラグを使う必要があります

$ docker run -d \
  --name sshfs-container \
  --volume-driver vieux/sshfs \
  --mount src=sshvolume,target=/app,volume-opt=sshcmd=test@node2:/home/test,volume-opt=password=testpassword \
  nginx:latest

この例は、サービスの作成時に NFS ボリュームを作成する方法を表します。例では NFS サーバとして 10.0.0.10 を使い、 NFS サーバ上に公開するディレクトリを /var/docker-nfs とします。ボリュームドライバは local なので注意します。

NFSv3

$ docker service create -d \
  --name nfs-service \
  --mount 'type=volume,source=nfsvolume,target=/app,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/var/docker-nfs,volume-opt=o=addr=10.0.0.10' \
  nginx:latest

NFSv4

$ docker service create -d \
    --name nfs-service \
    --mount 'type=volume,source=nfsvolume,target=/app,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/var/docker-nfs,"volume-opt=o=addr=10.0.0.10,rw,nfsvers=4,async"' \
    nginx:latest

CIFS/Samba ボリュームの作成

ホスト上のマウントポイントを変更しなくても、Docker で直接 Samba 共有ディレクトリをマウントできます。

$ docker volume create \
     --driver local \
     --opt type=cifs \
     --opt device=//uxxxxx.your-server.de/backup \
     --opt o=addr=uxxxxx.your-server.de,username=uxxxxxxx,password=*****,file_mode=0777,dir_mode=0777 \
     --name cif-volume

注意として、IP アドレスの代わりにホスト名を使う場合は、docker がホスト名の名前解決をできるようにするため、 addr オプションが必要になります。

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

ボリュームはバックアップ、 復旧(restore)移行(migrate) に役立ちます。新しいコンテナの作成に --volumes-from フラグを使うと、そのボリュームをマウントします。

ボリュームのバックアップ

たとえば、 dbstore という名前の新しいコンテナを作成します。

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

それから次のコマンドで行うのは、

  • 新しいコンテナを起動し、 dbstore コンテナからボリュームをマウント
  • ローカルホストディレクトリを /backup としてマウント
  • dbdata ボリュームの内容を tar を使い、手元の /backup ディレクトリ内の backup.tar へ出力するコマンドを渡す
$ docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata

コマンドの処理が終わると、コンテナは終了し、手元の dbdata ボリュームにバックアップが残されます。

バックアップからボリュームを復旧

作成したバックアップを使えば、同じコンテナや他の別の場所で作ったコンテナにも復旧できます。

たとえば、 dbstore2 という名前の新しいコンテナを作成します。

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

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

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

このテクニックは自動バックアップや移行、復旧テストに、自分の好きなツールを使って行えます。

ボリューム削除

Docker のデータボリュームはコンテナを削除した後も残り続けます。2つのボリュームタイプについて考えます。

  • 名前付きボリューム は、コンテナ外に awsome:/bar のような指定された参照元がある
  • 無名ボリューム は明示的な参照元が無いため、コンテナの削除時、Docker Engine デーモンに対し、ボリュームを削除するよう指示する

無名ボリューム(anonymous volume) の削除

無名ボリュームを自動的に削除するには、 --rm オプションを使います。たとえば、このコマンドは無名の /foo ボリュームを作成します。コンテナの削除時、 Docker Engine は /foo ボリュームを削除しますが、 awesome ボリュームは削除しません。

$ docker run --rm -v /foo -v awesome:/bar busybox top

注釈

他のコンテナが --volumes-from でボリュームをバインドすると、ボリュームの定義は「コピーされ」、1つめのコンテナを削除した後も無名ボリュームは残り続けます。

全てのボリュームを削除

全ての未使用ボリュームを削除し、空き容量を拡げます。

$ docker volume prune

次のステップ