バインドマウントの利用
読む時間の目安: 7 分
バインドマウントは Docker の初期のころから存在しています。 ただし ボリューム に比べると、バインドマウントの機能は限定されます。 バインドマウントを利用すると「ホストマシン」上のファイルやディレクトリがコンテナー内にマウントされます。 そのファイルやディレクトリは、ホストマシン上の絶対パスにより参照できます。 これとは違ってボリュームを利用すると、ホストマシン上に新たなディレクトリが生成され、そこが Docker の保存ディレクトリとなります。 そして Docker はそのディレクトリ内の内容を管理していきます。
そのファイルやディレクトリは、Docker ホストに存在している必要がなくなります。 存在してなかったとしても、必要とされるときには生成されます。 バインドマウントは非常に性能の良いものですが、ただしホストマシンのファイルシステムに依存するものとなり、利用可能な特定のディレクトリ構造に従ったものになります。 今から新たに Docker アプリケーションを開発しようとする場合は、これにかわって 名前つきボリューム の利用を考えてみてください。 バインドマウントを管理するために Docker CLI コマンドを直接利用することはできなくなります。
-v または --mount フラグの選択
全般に--mount
の方がわかりやすいものですが、記述は増えます。
両者の最大の違いは、-v
の文法がオプション指定のすべてを 1 項目にとりまとめるものであるのに対して、--mount
の文法はそれを 1 つずつ個別に分けている点です。
以下に両フラグにおける文法を比較します。
ヒント
はじめて利用する方は
--mount
を利用してください。 上級ユーザーは-v
や--volume
を用いることに慣れているかもしれませんが、--mount
を利用するように心がけてください。--mount
の方が簡単に利用することができるとの調査もあります。
-v
または--volume
: 3 つの項目から構成され、それぞれをコロン(:
)で区切ります。 各項目は正しい順に記述する必要があります。 各項目の意味は、そのときどきによって変わります。- バインドマウントの場合、1 つめの項目は ホストマシン 上のファイルまたはディレクトリへのパスです。
- 2 つめは、コンテナー内にマウントされるファイルまたディレクトリのパスです。
- 3 つめは任意の指定項目であり、オプション指定をカンマ区切りで指定します。
指定内容には
ro
,z
,Z
などがあります。 このオプションに関しては後に説明しています。
--mount
: 複数のキーバリューペアを指定し、各ペアはカンマにより区切ります。 そしてそれぞれのペアは<key>=<value>
という記述を行います。--mount
における記述は-v
や--volume
におけるものよりも長くなります。 しかしキーの並び順に意味はなく、このフラグに与えられたキーバリューの内容は容易に理解することができます。type
はマウントのタイプであり、bind
,volume
,tmpfs
といった値を指定します。 ここで説明しているのはバインドマウントであるため、常にbind
であるものとします。source
はマウント元です。 バインドマウントにおいては、Docker デーモンホスト上のファイルまたはディレクトリへのパスになります。source
あるいはsrc
といった指定がよく用いられます。destination
には、コンテナー上にてマウントするファイルまたはディレクトリのパスを指定します。destination
,dst
,target
といった指定がよく用いられます。- オプション
readonly
が指定されると、そのバインドマウンドが コンテナーにおける読み込み専用マウント としてマウントされます。 - オプション
bind-propagation
が指定されると、バインドプロパゲーション(bind propagation)の設定変更を行います。rprivate
,private
,rshared
,shared
,rslave
,slave
のいずれかを指定します。 --mount
フラグは、selinux ラベルを修正するためのz
またはZ
オプションには対応していません。
これ以降においては、可能なら--mount
と-v
の両方の例を示していきます。
また先に示すのは--mount
とします。
-v
と--mount
の動作の違い
-v
および--volume
フラグは、長らく Docker の一部分として実現してきているため、その動作を今さら変更することはできません。
このことがつまり、-v
と--mount
の動作の違いの 1 つ になります。
-v
または--volume
を使ってファイルやディレクトリをバインドマウントした際に、そのファイルやディレクトリが Docker ホスト上にまだ存在していなかった場合、-v
はそのマウントエンドポイントを生成します。
その場合には常にディレクトリとして生成されます。
--mount
を使ってファイルやディレクトリをバインドマウントした際に、そのファイルやディレクトリが Docker ホスト上に存在していなかった場合、Docker はそのファイルやディレクトリを自動的に生成することはしません。
かわりにエラーが出力されます。
バインドマウントを用いたコンテナーの起動
仮にsource
というディレクトリがあって、そこにソースコードが置かれているとします。
そして成果物はそのサブディレクトリsource/target/
に置かれるものとします。
ここでコンテナーが/app/
というディレクトリから、その成果物にアクセスできるようにします。
つまり開発ホスト上のソースディレクトリにおいて成果物をビルドしたら、すぐにその最新の成果物をコンテナーがアクセスできるようにする、というものです。
これを実現するには以下のようなコマンドにより、target/
ディレクトリをコンテナー内の/app/
にバインドマウントします。
コマンドはsource
ディレクトリから実行します。
$(pwd)
というサブコマンドは、Linux あるいは macOS ホスト上において、カレントディレクトリに展開されます。
Windows ユーザーは、Windows におけるパス変換 も参照してください。
--mount
と-v
によるそれぞれの例は、同一の結果になります。
ただし 2 つの例を同時に実行することはできません。
実行するためには、実行前にdevtest
コンテナーを削除しておくことが必要になります。
$ docker run -d \
-it \
--name devtest \
--mount type=bind,source="$(pwd)"/target,target=/app \
nginx:latest
$ 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"
}
],
この情報から、マウントは「バインド」マウントであることがわかります。
そしてマウント元、マウント先が正しいものであること、マウントが読み書き可能であること、プロパゲーションはrprivate
に設定されていることがわかります。
コンテナーを停止します。
$ docker container stop devtest
$ docker container rm devtest
コンテナー上の空ではないディレクトリへのマウント
バインドマウントする先のコンテナー内ディレクトリが空でなかったとします。 このときそのディレクトリ内にはじめからあった内容は、バインドマウントによって見えなくなってしまいます。 そうであっても、このことを便利に利用できる場合もあります。 たとえばアプリケーションの新バージョンをテストする際に、新たなイメージをビルドせずに実現するような場合です。 ただしそういった状況には驚くかもしれません。 またこの動きは Docker ボリューム とは異なるものです。
以下は極端な例です。
コンテナーの/usr/
ディレクトリをホストマシン上の/tmp/
ディレクトリに置き換えてしまうものです。
おそらくこのコンテナーは使いものにならなくなります。
--mount
と-v
によるそれぞれの例は、同一の結果になります。
$ 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".
$ 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
を加えます。
複数のオプション指定を行っているので、それぞれをカンマで区切ります。
--mount
と-v
によるそれぞれの例は、同一の結果になります。
$ docker run -d \
-it \
--name devtest \
--mount type=bind,source="$(pwd)"/target,target=/app,readonly \
nginx:latest
$ docker run -d \
-it \
--name devtest \
-v "$(pwd)"/target:/app:ro \
nginx:latest
バインドマウントが正しく生成されたかどうかをdocker inspect devtest
により確認します。
出力の中で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 ホストマシン上においてのみ変更可能です。
バインドプロパゲーションは高度な技術であるため、多くのユーザーは設定する必要はありません。
バインドプロパゲーションとは、指定されたバインドマウントや名前つきボリュームによって生成されるマウントが、そのレプリカに対して情報伝達(propagate)をするかどうかを表わします。
ここで/mnt
というマウントポイントを考えます。
これが/tmp
にマウントされているとします。
これに対するプロパゲーションの設定は、/tmp/a
上のマウントが/mnt/a
として利用可能かどうかを制御するものです。
プロパゲーションの各設定においては、再帰的に対応するマウントポイントが存在します。
再帰的という点でいうと、仮に/tmp/a
が/foo
としてマウントされていたとします。
このときプロパゲーション設定は/mnt/a
や/tmp/a
が存在するかどうかを定めるものです。
警告
マウントプロパゲーションは Docker Desktop では動作しません。
プロパゲーション設定 | 内容説明 |
---|---|
shared |
マウント元に対するサブマウントは、マウント先にも公開されます。マウント先に対するサブマウントも、マウント元に対して公開されます。 |
slave |
shared に類似。ただし一方向のみ。マウント元がサブマウントを公開するなら、マウント先でもこれを見ることができますが、マウント先がサブマウントを公開しても、マウント元からは見ることができません。 |
private |
マウントはプライベートなものになります。マウント元におけるサブマウントは、マウント先に公開されません。またマウント先のサブマウントも、マウント元には公開されません。 |
rshared |
shared と同様。, but the propagation also extends to and from mount points nested within any of the original or replica mount points. |
rslave |
slave と同様。, but the propagation also extends to and from mount points nested within any of the original or replica mount points. |
rprivate |
デフォルト。private と同様。つまりマウント元やマウント先に対するサブマウントは、どの方向にも伝播(propagate)しません。 |
マウントポイントに対してバインドプロパゲーションを設定するには、ホストのファイルシステムがバインドプロパゲーションに対応している必要があります。
バインドプロパゲーションの詳細は Linux カーネルドキュメントの shared subtree を参照してください。
以下の例ではtarget/
ディレクトリをコンテナーに対して 2 度マウントしています。
そして 2 つめのマウントではro
オプションと、バインドプロパゲーションのオプションrslave
を指定しています。
--mount
と-v
によるそれぞれの例は、同一の結果になります。
$ 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
$ docker run -d \
-it \
--name devtest \
-v "$(pwd)"/target:/app \
-v "$(pwd)"/target:/app2:ro,rslave \
nginx:latest
Now if you create /app/foo/
, /app2/foo/
also exists.
selinux ラベルの設定
selinux
を利用している場合z
またはZ
オプションを使って、selinux ラベルを修正することができます。
修正するのは、コンテナーに対してマウントされている ホスト上のファイルやディレクトリ です。
つまりその結果はホストマシンそのもののファイルやディレクトリを変更するため、Docker の範囲外に影響を及ぼします。
z
オプションは、複数コンテナー間においてバインドマウントされた内容を共有します。Z
オプションは、バインドマウントされた内容はプライベートなものであって共有はされません。
このオプションに対しては 最大限 注意してください。
Z
オプションを使って、/home
や/usr
などのようなシステムディレクトリをバインドマウントしてしまうと、ホストマシンは制御できなくなり、ホストマシン上のファイルに対するラベル設定を手動で行うことになります。
重要: バインドマウントをサービス内にて行った場合には、selinux ラベル(
:Z
や:z
)は、ro
と同じように無視されます。 詳しくは moby/moby #32579 を参照してください。
以下はz
オプションの指定により、複数コンテナーがバインドマウント先を共有できるようにする例です。
selinux ラベルの修正は--mount
フラグでは行うことができません。
$ docker run -d \
-it \
--name devtest \
-v "$(pwd)"/target:/app:z \
nginx:latest
compose におけるバインドマウントの利用
バインドマウントを使った一つの Docker Compose サービスは、たとえば以下のようになります。
version: "3.9"
services:
frontend:
image: node:lts
volumes:
- type: bind
source: ./static
target: /opt/app/staticvolumes:
myapp:
Compose とともにボリュームタイプをbind
にする方法に関しては ボリュームに対する Compose リファレンス や ボリューム設定に対する Compose リファレンス を参照してください。
次のステップ
- ボリューム について学ぶ
- tmpfs マウント について学ぶ
- ストレージドライバー について学ぶ