Docker の 3 種類のマウント
Docker コンテナ内で作成・編集した作業ファイルは、コンテナを停止 (docker container stop
) しても消えてしまうことはありませんが、コンテナを削除 (docker container rm
) した場合には消えてしまいます。
これは、コンテナ内のファイル群が、コンテナ内に閉じて存在しているからです(だからこそコンテナなのですが)。
Docker のマウント機能を用いると、コンテナ内の特定のディレクトリパス(の中のファイル群)をホスト PC 上のファイルシステムに関連付けることができるため、コンテナのライフサイクルとは切り離して作業ファイルを管理できるようになります。 マウントには下記で説明する 3 種類がありますが、作業ファイルをホスト PC 側に永続化したい場合は、「ボリュームマウント」か「バインドマウント」というマウントタイプを使用します。 もうひとつの「tmpfs マウント」は、その名のとおりテンポラリファイルにのみ使用できます。
- ボリュームマウント
- ホスト PC 上にコンテナ用のデータファイルを作成 し、コンテナ内の特定のディレクトリパスにマッピングします。 このデータファイルはポータビリティが高く、クラウド上に保存するということもできます(ボリュームドライバーが必要)。 複数のコンテナから 1 つのボリュームを共有することも可能です。 コンテナ内で作成したファイルの永続化には、このボリュームの使用が推奨されています。
- バインドマウント
- ホスト PC の特定のディレクトリ(絶対パス指定) を、コンテナ内の特定のディレクトリパスにマッピングします。 ボリュームと比べてポータビリティが低いため、名前付きボリュームの使用が推奨されてます。例えば、バインドマウントでは、ホスト側の多数のファイルとマッピングされてしまうため別環境に移しにくいとか、マウント時のパス表現が OS に依存してしまうといった欠点があります。 ホスト側からコンテナで操作したファイルをささっと覗いて見たいときはバインドマウントは便利ですが、これはコンテナ内で作成した危険なファイルが、そのままホスト上にも作られてしまうということを示しています。
- tmpfs マウント
- ホスト PC のメモリ領域 を、コンテナ内の特定のディレクトリパスにマッピングします。コンテナ上でファイル生成を行うと、実際には一時的なメモリ領域に保存されることになるので、ここに保存されたファイルはコンテナを停止すると消えてしまいます。一時的なファイルを格納するディレクトリを tmpfs マウントすることで、コンテナサイズの増加を防ぐことができ(書き込みレイヤーに出力されない)、パフォーマンスの向上を見込めます。
上記で、「ホスト PC」といっているのは Docker コンテナの実行環境のことであり、Windows や macOS で Docker Desktop を使用している場合は、正確には Linux VM のことを示します。
ボリュームマウント (volume mount) の使い方
コンテナ内での作業内容(生成したファイル)をホスト PC 側に永続化したいときに最初に検討すべきは「ボリューム」の使用です。 ボリュームの実体はホスト PC 上のファイルシステム(Docker 管轄下)に永続化されたファイルであり、コンテナ側から見ると「ディレクトリ」として見えます。
ボリュームを作成する (docker volume create)
ボリュームを明示的に作成するには次のように実行します(コンテナ実行時に自動生成することも可能です)。 ボリューム名を明示するので、名前付きボリューム (named volume) と呼ばれています。
$ docker volume create my-vol
ボリュームの一覧を表示する (docker volume ls)
Docker ホスト上に存在しているボリュームの一覧を表示するには、次のように実行します。
$ docker volume ls
DRIVER VOLUME NAME
local e61a091c345b2b969dd288f984be...
local my-vol ← 今回作成したボリューム
local out
(ボリューム名がランダムな記号列になっているものは、匿名マウント時に自動生成されたボリュームです)
ボリュームの詳細情報を表示する (docker volume inspect)
指定したボリュームの詳細情報を確認するには次のようにします。
$ docker volume inspect my-vol
[
{
"CreatedAt": "2022-01-25T07:18:24Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": null,
"Scope": "local"
}
]
ボリュームを削除する (docker volume rm/prune)
# ボリューム名を指定して削除
$ docker volume rm my-vol
# 未使用のボリューム(どのコンテナからも参照されていないもの)をすべて削除
$ docker volume prune
ボリュームマウントしてコンテナを起動する
コンテナ側の /volume
というパスに、上記で作成した my-vol
ボリュームをマウントして使ってみます。
ここでは、軽量の Alpine Linux イメージ (alpine:latest
) を使ってコンテナを作成します。
# コンテナ (my-alpine) の作成
$ docker container create -it --mount src=my-vol,dst=/volume --name my-alpine alpine:latest
# コンテナが作成できているか確認
$ docker container ls -a
--mount
オプションの src
や dst
で、ボリューム名やマウント先のパスを指定します。
src=<ボリューム名>
…src
ではなくsource
でも OK。存在しないボリューム名を指定すると、その名前のボリュームが自動的に生成されます。src
パラメーターを省略すると、ランダムな16進文字列の名前のボリュームが生成されます。dst=<コンテナ側のパス>
…dst
ではなくdestination
やtarget
でも OK。type=<マウントタイプ>
… ボリュームマウントの場合は省略できます。マウントタイプとしてvolume
、bind
、tmpfs
を指定します。
(過去の記事には、-v
オプションを使っているものもありますが、現在は公式に --mount
オプションの使用が推奨されています(挙動がわかりにくく問題が発生しやすいなどの理由があります)。特別な事情がない限り、--mount
オプションの方を使うようにしてください)
コンテナを作成したら、次のようにして起動してシェル接続できます。
$ docker start -ai my-alpine
コンテナ側の /volume
というディレクトリが見えているかを確認してください。
この中にファイルを作成すると、ホスト OS 側のボリューム領域に内容が保存されるので、次回コンテナを起動 (docker start
) したときにファイル編集作業の続きを行えます。
my-volume
ボリュームを他のコンテナにマウントして共有するということもできます。
# ls /volume # 初期状態は空っぽ
# echo Hello > /volume/hello.txt
# exit
ボリュームファイルの格納場所
ボリュームのデータファイルは具体的にはホスト PC の /var/lib/docker/volumes
に生成されますが、ボリュームは docker volume
コマンドを介して操作するので、通常は保存先のパスを意識する必要はありません。
Windows や macOS で Docker Desktop を使用している場合は、Volumes タブでボリュームの一覧を確認することができます(Docker Desktop のバックエンドとして使われる Linux VM 上に格納されているため、Windows や macOS 上で上記のパスを探しても見つからないことに注意してください)。
バインドマウント (bind mount) の使い方
バインドマウントは、ホスト OS 上のディレクトリをコンテナ側のディレクトリにマッピングします。
つまり、お互いのファイルシステム上で同じディレクトリ/ファイルを参照できるようになります。
次の例では、ホスト OS 上のカレントディレクトリにある data
ディレクトリを、コンテナ側の /data
にマウントしています。
ホスト OS 側のディレクトリは、存在するディレクトリを絶対パスで指定する必要があります(次の例では、"$(pwd)/data"
で絶対パスを生成しています)。
$ mkdir data
$ docker container create -it --mount type=bind,src="$(pwd)/data",dst=/data --name my-alpine alpine:latest
バインドマウントするときは、上記のように --mount
オプションで type=bind
と指定する必要があります。
コンテナを起動してシェル接続し、適当なファイルを /data
ディレクトリ以下に作成してみます。
$ docker container start -ai my-alpine
# echo Hello > /data/hello.txt
# exit
ホスト OS 側で data
ディレクトリを見ると、コンテナで作成したファイルが存在していることを確認できます。
$ ls data
hello.txt
tmpfs マウントの使い方
tmpfs マウントを使用すると、ホスト OS 上のメモリ領域を一時ファイルシステムとして、コンテナ側のディレクトリにマッピングすることができます。 結果として、そのディレクトリ内に保存したファイルは、コンテナを停止したときに削除されます。 一時的にしか使用しないファイルは、tmpfs マウントしたディレクトリ内に作成することで、Docker イメージの肥大化を防ぐことができます。 また、そのファイルはメモリ上に生成されるため、パフォーマンス的にも有利です。
$ docker container create -it --mount type=tmpfs,dst=/sandbox --name my-alpine alpine:latest
tmpfs マウントするときは、上記のように --mount
オプションで type=tmpfs
と指定する必要があります。
この例では、コンテナ側の /sandbox
ディレクトリを tmpfs マウントのターゲットとしています。
コンテナを起動して /sandbox
内にファイルを作成し、コンテナを停止してみます。
$ docker start -ai my-alpine
# echo Hello > /sandbox/hello.txt
# exit
再度コンテナを起動して /sandbox
の中を見ると、上記で作成した hello.txt
は消えていることがわかります。
$ docker start -ai my-alpine
# ls /sandbox
#