バインド マウント(bind mount) の使用

バインド マウント(bind mount) は初期の Docker から存在しています。 ボリューム と比較すると、バインド マウントは機能が限定的です。バインド マウントを使う時、「ホストマシン」上のファイルやディレクトリを、コンテナ内にマウントします。ファイルやディレクトリは、ホストマシン上の絶対パスとして参照されます。対照的に、ボリュームを使う場合は、新しいディレクトリがホストマシン上の Docker のストレージディレクトリ内に作成され、 Docker が内容を直接管理します。

ファイルやディレクトリは Docker ホスト上にあらかじめ存在している必要はありません。存在しなければ、必要に応じて作成されます。バインド マウントは高性能ですが、ホストマシンのファイルシステムで利用可能な、特定のディレクトリ構造に依存します。新しい Docker アプリケーションを開発する場合は、代わりに 名前付きボリューム の利用を検討してください。バインド マウントは Docker CLI コマンドを使って直接管理できません。

Docker ホスト上のバインド マウント

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

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

ちなみに

新しいユーザは --mount 構文を使うべきです。経験のあるユーザは -v--volume 構文に慣れているでしょうが、調査によって、より簡単に利用できると分かっている --mount の利用を推奨します。

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

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

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

-v--volume フラグは長い間 Docker の一部のため、これらの挙動は変更できません。これが意味するのは、 「-v」と「--mount」間では、各々の挙動が異なります

-v--volume をバインド マウントに使う時、Docker ホスト上にファイルやディレクトリが存在しなければ、 -v はエンドポイントを作成します。 常にディレクトリとして作成されます

--mount をバインド マウントに使う時、Docker ホスト上にファイルやディレクトリが存在しなければ、 Docker は自動的に作成**しません** し、エラーを起こします。

バインドマウントを使ってコンテナを起動

source ディレクトリがあると想定します。ここをソースコードの構築時に使いますが、アーティファクト(構築結果)は他のディレクトリ source/target/ に保存します。アーティファクトをコンテナの /app/ で使いたい場合や、コンテナが新しく構築するたびに、開発ホスト上のバインド元へとアクセスしたい場合があるでしょう。以下のコマンドを使い、 target/ ディレクトリをコンテナの /app/ にバインドマウントします。そして source ディレクトリの中でコマンドを実行します。 $(pwd) サブコマンドは、Linux や macOS ホスト上での、現在の作業ディレクトリを展開します。Windows の場合は、 Windows 上でのパス変換 をご覧ください。

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

  • --mount

    $ docker run -d \
      -it \
      --name devtest \
      --mount type=bind,source="$(pwd)"/target,target=/app \
      nginx:latest
    
  • -v

    $ docker run -d \
      -it \
      --name devtest \
      -v "$(pwd)"/target:/app \
      nginx:latest
    

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

"Mounts": [
    {
        "Type": "bind",
        "Source": "/tmp/source/target",
        "Destination": "/app",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

この表示は、マウントしているのはバインド マウントであり、正しいマウント元(Source)とマウント先(Destination)が指定され、かつ、マウントは読み書きでき、さらにプロパゲーションは rprivate に設定されています。

コンテナを停止します。

$ docker container stop devtest

$ docker container rm devtest

コンテナ上の空ではないディレクトリにマウント

コンテナ上の 空ではない(non-empty) ディレクトリにバインド マウントする場合、ディレクトリの既存の内容は、バインド マウントによって 隠されます(obscured) 。これは、新しいイメージを構築せずに、アプリケーションの新しいバージョンをテストしたいような場合に有益になり得ます。一方で、 docker ボリューム とは挙動が違うため、予期しない挙動となる可能性もあります。

以下は外部から扱う例ですが、コンテナの /usr/ ディレクトリの内容は、ホストマシン上の /tmp/ ディレクトリに置き換えられます。多くの場合、非機能的なコンテナに結果としてなります。

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

  • --mount

    $ docker run -d \
      -it \
      --name broken-container \
      --mount type=bind,source=/tmp,target=/usr \
      nginx:latest
    
    docker: Error response from daemon: oci runtime error: container_linux.go:262:
    starting container process caused "exec: \"nginx\": executable file not found in $PATH".
    
  • --v

    $ docker run -d \
      -it \
      --name broken-container \
      -v /tmp:/usr \
      nginx:latest
    
    docker: Error response from daemon: oci runtime error: container_linux.go:262:
    starting container process caused "exec: \"nginx\": executable file not found in $PATH".
    

)コンテナは作成されますが、開始できません(訳者注:見ての通り、実行してもエラーが出ます)。削除します。

$ docker container rm broken-container

読み込み専用のバインドマウントを使用

アプリケーション開発では、コンテナがバインド マウントへの書き込みを必要とするなら、変更は Docker ホスト側へと反映されます。一方で、コンテナがデータの読み込みだけを必要とする場合があります。

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

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

  • --mount

    $ docker run -d \
      -it \
      --name devtest \
      --mount type=bind,source="$(pwd)"/target,target=/app,readonly \
      nginx:latest
    
  • -v

    $ docker run -d \
      -it \
      --name devtest \
      -v "$(pwd)"/target:/app:ro \
      nginx:latest
    

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

"Mounts": [
    {
        "Type": "bind",
        "Source": "/tmp/source/target",
        "Destination": "/app",
        "Mode": "ro",
        "RW": false,
        "Propagation": "rprivate"
    }
],

コンテナを停止します。

$ docker container stop devtest

$ docker container rm devtest

バインド プロパゲーションの設定

バインド プロパゲーション (伝搬)(bind propagation) は、バインド マウントとボリュームの両方で rprivate がデフォルトです。Linux ホスト マシン上のバインド マウントのみ設定変更が可能です。バインド プロパゲーションは高度なトピックであり、多くのユーザは変更の必要がほとんどないでしょう。

指定したバインドマウントや名前付きボリューム内で作成されたマウントを、そのマウントの複製に 伝搬(propagated) できるかどうかで、バインド プロパゲーションを参照します。マウントポイント /mnt/tmp もマウントしていると仮定します。 /tmp/a をマウントしている場所の 伝搬設定(propagation setting) の制御は、 /mnt/a にも有効です。それぞれの伝搬設定は、再帰的に補完されます。再帰的な例として、 /tmp/a/foo にもマウントしていると仮定します。伝搬設定の制御は、 /mnt/a/tmp/a にも及びます。

警告

マウント 伝搬は Docker Desktop では機能しません。

伝搬設定 説明
shared オリジナルマウントのサブマウントは、 複製されたマウント(replica mount) として現れます。また、複製されたマウントのサブマウントもオリジナルマウントへ伝搬されます。
slave 共有マウント(shared mount) と似ていますが、一方通行です。オリジナルマウントがサブマウントとして現れると、複製されたマウントで見えるようになります。しかし、複製されたマウントがサブマウントとして現れる場合は、オリジナルマウントが見えません。
private マウントは プライベート(private) です。この中のサブマウントは、複製されたマウントに現れません。また、複製されたマウントのサブマウントは、オリジナルマウントにも現れません。
rshared 共有マウントと同じですが、マウントポイントをネストして、あらゆるオリジナルもしくは複製されたマウントポイントまで、伝搬が広がります。
rslave slave と同じですが、マウントポイントをネストして、あらゆるオリジナルもしくは複製されたマウントポイントまで、伝搬が広がります。
rprivate デフォルトです。プライベートと同じであり、あらゆるオリジナルもしくは複製されたマウントポイントから、他の場所に現れないのを意味します。

マウントポイントにバインド伝搬を設定する前に、ホストファイルシステムで既にバインド伝搬をサポートしている必要があります。

バインド伝搬についての詳しい情報は、 shared subtree に関する Linux Kernel ドキュメント をご覧ください。

以下の例は target/ ディレクトリをコンテナ内に2度マウントし、2つめのマウントに ro オプションをつけ、さらに rslave バインド伝搬オプションを付けます。

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

  • --mount

    $ docker run -d \
      -it \
      --name devtest \
      --mount type=bind,source="$(pwd)"/target,target=/app \
      --mount type=bind,source="$(pwd)"/target,target=/app2,readonly,bind-propagation=rslave \
      nginx:latest
    
  • -v

    $ docker run -d \
      -it \
      --name devtest \
      -v "$(pwd)"/target:/app \
      -v "$(pwd)"/target:/app2:ro,rslave \
      nginx:latest
    

これで /app/foo を作成し、 /app/foo/ も存在します。

selinux ラベルを設定

selinux を使う時、コンテナ内にマウントされている ホストファイルやディレクトリ の selinux ラベルを、 zZ オプションを追加して変更できます。これはホストマシン上自身のファイルやディレクトリにも影響し、結果的に Docker の範囲外となります。

  • z オプションは、バインドマウントの内容が、複数のコンテナ間で共有されるのを示す
  • Z オプションは、バインドマウントの内容が、プライベートかつ非共有を示す

これらのオプションは極度に注意して使います。 /home//usr/ のようなシステムディレクトリを Z オプションでバインドマウントすると、ホストマシンが操作不可能な状況になり、手動でホストマシン上のファイルを リラベル(relabel) する必要があるでしょう。

重要

サービスでバインドマウントを使う場合、 selinux ラベル( :Z:z )だけでなく、 :ro も無視されます。詳細は moby/moby #32579 をご覧ください。

以下の例は z オプションを設定し、バインドマウントの内容を、複数のコンテナが共有できるように設定します。

--mount フラグを使う場合は、 selinux のラベルを変更できません。

$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app:z \
  nginx:latest

compose でバインドマウントを使用

バインドマウンドを使う1つの Docker Compose サービスは、このようにします。

version: "3.9"
services:
  frontend:
    image: node:lts
    volumes:
      - type: bind
        source: ./static
        target: /opt/app/staticvolumes:
  myapp:

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

次のステップ