これは、なにをしたくて書いたもの?
今までDockerのストレージについてちゃんと見てこなかったので、ドキュメントを眺めてみます。主にマウントに
関する内容です。
以下の違いがわかるようになるためのエントリーです。
## バインドマウント $ docker container run -v <host-path>:<container-path> ## ボリュームマウント(名前付きボリューム) $ docker container run -v <volume-name>:<mount-path> ## ボリュームマウント(匿名ボリューム) $ docker container run -v <mount-path>
Dockerのストレージ
Docker Engineのストレージに関するドキュメントはこちら。
Dockerのストレージがカバーしているのは、次の2つの概念です。
- コンテナデータの永続性
- Docker Daemonのストレージバックエンド
- containerdイメージストアおよびストレージドライバー
今回は主にコンテナデータの永続性について扱います。
コンテナレイヤーの基本
デフォルトではコンテナ内に作成されたすべてのファイルは、読み取り専用の不変イメージレイヤー上にある
書き込み可能なコンテナレイヤーに保存されます。
このコンテナレイヤーに書き込まれたデータは、コンテナの破棄とともになくなります。
Storage / Container layer basics
書き込み可能レイヤーはコンテナごとに異なり、書き込み可能レイヤーからホストや他のコンテナにデータを
簡単に抽出することはできません。
ストレージマウントオプション
Dockerでは、コンテナの書き込み可能なレイヤーの外にデータを保持するためのマウントオプションをサポート
しています。
- ボリュームマウント
- バインドマウント
- tmpfsマウント
- 名前付きパイプ
Storage / Storage mount options
どのタイプのマウントを使っても、コンテナ内から見たデータは同じです。コンテナのファイルシステム内の
ディレクトリーまたは個別のファイルとして扱われます。
ボリュームマウント
ボリュームマウントは、Docker Daemonによって管理される永続的なストレージです。ボリュームを使用している
コンテナが削除された後も、ボリュームはデータを保持します。
Storage / Storage mount options / Volume mounts
ボリュームのデータはホスト上に保存されますが、ボリュームの中にあるデータを操作するにはボリュームを
コンテナにマウントする必要があります。
ボリュームはパフォーマンスが重視されるデータ処理や、長期保存のニーズ向けとされています。ストレージの
管理はDocker Daemonになるため、ボリュームはホストでのファイルシステムに直接アクセスした場合と同等の
パフォーマンスを得ることができます。
バインドマウント
バインドマウントは、ホストのパスとコンテナの間に直接リンクを作成して、ホスト上の任意の場所に保存されている
ファイルやディレクトリーへのアクセスを可能にします。これらのマウントはDockerによって分離されていないため、
ホスト上の非Dockerプロセスとコンテナプロセスの両方がマウントされたファイルを同時に変更できます。
Storage / Storage mount options / Bind mounts
tmpfsマウント
tmpfsマウントでは、ファイルをホストマシンのメモリーに直接保存するため、データはディスクに書き込まれません。
一時的なストレージであり、コンテナが停止したり再起動すると、もしくはホストが再起動するとデータは
なくなります。
Storage / Storage mount options / tmpfs mounts
中間データのキャッシュや資格情報などの機密情報の管理、一時的なメモリー内ストレージとしての利用に
適しているとされています。
名前付きパイプ
Dockerホストとコンテナの間の通信に使用できる仕組みです。
Storage / Storage mount options / Named pipes
ここまで読むと
Dockerを使用していると、-vオプション(もしくは--volumeオプション)などでよく使われるのは
バインドマウントだということに気づきます。というわけで、各マウントオプションについてもう少し
見ていきましょう。
バインドマウント
順番的にはボリュームマウントが先だと思うのですが、よく見るのはバインドマウントな気がするので先に
こちらを見ておきましょう。
バインドマウントは、ホスト上のファイルまたはディレクトリーをコンテナにマウントします。
ユースケースとしては以下のようなものがあります。
バインドマウントはDockerイメージのビルド時にも使用できます。
コンテナ内にすでにファイルやディレクトリーが存在するパスにバインドマウントすると、コンテナ側の既存の
ファイルは隠蔽されます。
Bind mounts / Bind-mounting over existing data
またバインドマウントはDocker Daemonホスト側で作成されます。これはリモートのDocker Daemonを使用している
時は、クライアント側のファイルにアクセスするバインドマウントは実現できないことを意味します。
読み取り専用にすることも可能です。
Bind mounts / Considerations and constraints
バインドマウントを使用する構文は以下ですね。
$ docker container run --mount type=bind,src=<host-path>,dst=<container-path> $ docker container run --volume <host-path>:<container-path>
推奨は--mountオプションの使用で、バインドマウントでは--mount type=bindと指定します。
$ docker container run --mount type=bind,src=<host-path>,dst=<container-path>[,<key>=<value>...]
有効なオプションはこちら。
Bind mounts / Syntax / Options for --mount
--volumeオプション。
$ docker container run -v <host-path>:<container-path>[:opts]
有効なオプションはこちら。
Bind mounts / Syntax / Options for --volume
読み取り専用のバインドマウント。
Bind mounts / Use a read-only bind mount
今回は詳しく見ませんが、再起マウントとバインド伝播について。
Docker Composeの場合は、以下の書き方になります。
services: frontend: image: node:lts volumes: - type: bind source: ./static target: /opt/app/static volumes: myapp:
Bind mounts / Use a bind mount with Docker Compose
これはLong syntaxと呼ばれるもので、Short syntaxだと見慣れた以下の記載になります。
services: frontend: image: node:lts volumes: - "./static:/opt/app/static"
Define services in Docker Compose / Attributes / volumes
ボリューム
次はボリュームです。ボリュームは、Docker Daemonによって管理される永続的なストレージということでした。
ボリュームはdocker volume createコマンドで作成するか、コンテナやサービスの作成時にDocker Daemonに
作成させることもできます。
ボリュームはDockerホスト上に保存され、コンテナにマウントすることで使用できます。またホスト側の機能からは
分離されていることがポイントです。
また以下が特徴です。
- バックアップや移行を簡単に行える(バインドマウントより容易)
- Docker CLIまたはAPIで管理できる
- LinuxコンテナおよびWindowsコンテナの両方で動作する
- 複数のコンテナ間で安全に共有可能
- アプリケーションで高パフォーマンスのIOが求められる時に適している
一方で、ホストからボリューム内のファイルやディレクトリーにはアクセスできません。
ボリュームはコンテナのライフサイクルの外にあります。複数のコンテナで同時にマウントでき、自動的に削除される
ことはありません。使用されていないボリュームはdocker volume pruneで削除できます。
そしてボリュームには名前付きボリュームと匿名ボリュームの2種類があります。
Volumes / Named and anonymous volumes
匿名ボリュームは、自動的にランダムかつDockerホスト内で一意な名前が付けられるボリュームです。
匿名ボリュームを使用しているコンテナを削除してもボリューム自体は保持されますが、他のコンテナから自動的に
再利用または共有されることはありません。2つ以上のコンテナ間で匿名ボリュームを共有するには生成された
ランダムなIDを使用して匿名ボリュームをマウントする必要があります。
※といっても、こうするなら名前付きボリュームを使うと思いますが…
ただ、例外としてコンテナ作成時に--rmオプションを付与している場合、コンテナ作成時に関連付けられた
匿名ボリュームはコンテナ削除時に一緒に削除されます。
Volumes / Remove volumes / Remove anonymous volumes
ちなみにdocker volume pruneはデフォルトでは使用されていない匿名ボリュームのみが削除されます。
--allまたは-aオプションをつけると、名前付きボリュームも含めて使われていないボリュームが削除されます。
コンテナ起動時にボリュームをマウントするコマンドはこちら。
$ docker container run --mount type=volume,src=<volume-name>,dst=<mount-path> $ docker container run --volume <volume-name>:<mount-path>
推奨は--mountオプションの使用です。--mount type=volumeと指定します。
$ docker container run --mount type=volume[,src=<volume-name>],dst=<mount-path>[,<key>=<value>...]
オプションはこちら。
Volumes / Syntax / Options for --mount
sourceまたはsrcオプションを省略すると匿名ボリュームになります。
--volumeで指定する場合はこちら。
$ docker container run -v [<volume-name>:]<mount-path>[:opts]
Volumes / Syntax / Options for --volume
<volume-name>:を省略すると匿名ボリュームになります。
読み取り専用にする場合。
Volumes / Use a read-only volume
ボリュームのサブディレクトリーをマウントすることもできます。これには--mountオプションのvolume-subpathを
使います。
たとえば以下のコマンドでは、logsというボリュームを作成して/logsディレクトリー内に2つのディレクトリーを
作成し、それぞれを別のコンテナでマウントしています。
## ボリューム作成 $ docker volume create logs ## ボリューム内にディレクトリーを作成 $ docker container run --rm \ --mount src=logs,dst=/logs \ alpine mkdir -p /logs/app1 /logs/app2 ## app1コンテナでは/logs/app1をマウント $ docker container run -d \ --name=app1 \ --mount src=logs,dst=/var/log/app1,volume-subpath=app1 \ app1:latest ## app2コンテナでは/logs/app2をマウント $ docker container run -d \ --name=app2 \ --mount src=logs,dst=/var/log/app2,volume-subpath=app2 \ app2:latest
Volumes / Mount a volume subdirectory
Docker Composeで使用する場合。Short syntaxでの名前付きボリュームですね。
services: frontend: image: node:lts volumes: - myapp:/home/node/app volumes: myapp:
この場合、初回のdocker compose up実行時にボリュームが作成されます。
先にdocker volume createで作成したボリュームをマウントする場合は、externalをtrueに指定します。
services: frontend: image: node:lts volumes: - myapp:/home/node/app volumes: myapp: external: true
Volumes / Use a volume with Docker Compose
Long syntaxの場合はこちら。
services: backend: image: example/backend volumes: - type: volume source: db-data target: /data volume: nocopy: true subpath: sub - type: bind source: /var/run/postgres/postgres.sock target: /var/run/postgres/postgres.sock volumes: db-data:
Define services in Docker Compose / Attributes / volumes
ボリュームのバックアップやリストアについてはこちら。
Volumes / Back up, restore, or migrate data volumes
こちらを応用すると、ボリュームからホスト側にファイルを取り出すこともできそうですね。
tmpfsマウント
最後はファイルをホスト側のメモリーに保存するtmpfsマウントです。
tmpfsマウントについてはこちらで書いたので、今回は割愛します。
DockerおよびDocker Composeでtmpfsを使う - CLOVER🍀
ドキュメントを見るのはこれくらいにして、最後に実際に動かして少し確認しておきましょう。
環境
今回の環境はこちら。
$ docker version Client: Docker Engine - Community Version: 29.1.3 API version: 1.52 Go version: go1.25.5 Git commit: f52814d Built: Fri Dec 12 14:49:32 2025 OS/Arch: linux/amd64 Context: default Server: Docker Engine - Community Engine: Version: 29.1.3 API version: 1.52 (minimum version 1.44) Go version: go1.25.5 Git commit: fbf3ed2 Built: Fri Dec 12 14:49:32 2025 OS/Arch: linux/amd64 Experimental: false containerd: Version: v2.2.1 GitCommit: dea7da592f5d1d2b7755e3a161be07f43fad8f75 runc: Version: 1.3.4 GitCommit: v1.3.4-0-gd6d73eb8 docker-init: Version: 0.19.0 GitCommit: de40ad0
使用するイメージはUbuntu Linux 24.04 LTSのものとします。
$ docker image pull ubuntu:24.04 24.04: Pulling from library/ubuntu 20043066d3d5: Pull complete 06808451f0d6: Download complete Digest: sha256:c35e29c9450151419d9448b0fd75374fec4fff364a27f176fb458d472dfc9e54 Status: Downloaded newer image for ubuntu:24.04 docker.io/library/ubuntu:24.04
バインドマウントを使う
まずはバインドマウントから。
ホスト側のファイルを用意しておきましょう。
$ mkdir data $ echo 'Hello from Host' > data/hello.txt
--mountオプションで使う場合。
$ docker container run -it --rm --mount type=bind,src=$(pwd)/data,dst=/var/lib/host-data ubuntu:24.04 $ docker container run -it --rm --mount type=bind,src=./data,dst=/var/lib/host-data ubuntu:24.04
--volumeオプションで使う場合。
$ docker container run -it --rm --volume $(pwd)/data:/var/lib/host-data ubuntu:24.04 $ docker container run -it --rm --volume ./data:/var/lib/host-data ubuntu:24.04 $ docker container run -it --rm -v $(pwd)/data:/var/lib/host-data ubuntu:24.04 $ docker container run -it --rm -v ./data:/var/lib/host-data ubuntu:24.04
どの方法でもいいのですが、コンテナ内からはマウントしたホスト側で作成したファイルが見えます。
# cat /var/lib/host-data/hello.txt
Hello from Host
コンテナ内でマウントしたディレクトリー内にファイルを作成。
# echo 'Hello from container' > /var/lib/host-data/hello2.txt # cat /var/lib/host-data/hello2.txt Hello from container
これはコンテナがなくなってもホスト側からも見えます。
$ cat data/hello2.txt
Hello from container
uidなどはなにも調整していないので、所有者はrootですが。
$ ll data 合計 16 drwxrwxr-x 2 xxxxx xxxxx 4096 12月 31 11:12 ./ drwxrwxr-x 3 xxxxx xxxxx 4096 12月 31 11:04 ../ -rw-rw-r-- 1 xxxxx xxxxx 16 12月 31 11:03 hello.txt -rw-r--r-- 1 root root 21 12月 31 11:12 hello2.txt
ボリュームマウント
次はボリュームマウントです。
名前付きボリューム
まずは名前付きボリューム。--mountオプションまたは--volumeオプションに名前を指定することで、
コンテナの起動と同時に名前付きボリュームを作成できます。
--mountオプションで指定する場合。
$ docker container run -it --rm --mount type=volume,src=my-volume,dst=/var/lib/named-volume ubuntu:24.04
--volumeオプションで指定する場合。
$ docker container run -it --rm --volume my-volume:/var/lib/named-volume ubuntu:24.04 $ docker container run -it --rm -v my-volume:/var/lib/named-volume ubuntu:24.04
ホスト側でdocker volume lsとすると、作成した名前付きボリュームが見えます。
$ docker volume ls DRIVER VOLUME NAME local my-volume
マウントしたディレクトリー内にファイルを作成してみます。
# echo hello > /var/lib/named-volume/hello.txt # echo world > /var/lib/named-volume/world.txt # cat /var/lib/named-volume/hello.txt hello # cat /var/lib/named-volume/world.txt world
1度コンテナを終了して、新しいコンテナで同じボリュームをマウントしてファイルを見てみます。
$ docker container run -it --rm --mount type=volume,src=my-volume,dst=/var/lib/named-volume ubuntu:24.04 ls -l /var/lib/named-volume total 8 -rw-r--r-- 1 root root 6 Dec 31 02:16 hello.txt -rw-r--r-- 1 root root 6 Dec 31 02:16 world.txt $ docker container run -it --rm --mount type=volume,src=my-volume,dst=/var/lib/named-volume ubuntu:24.04 cat /var/lib/named-volume/hello.txt hello $ docker container run -it --rm --volume my-volume:/var/lib/named-volume ubuntu:24.04 cat /var/lib/named-volume/world.txt world
--mountオプションと--volumeオプションを混ぜてみました。ちゃんとファイルが見えますね。
ボリュームのinspect。
$ docker volume inspect my-volume [ { "CreatedAt": "2025-12-31T11:15:53+09:00", "Driver": "local", "Labels": null, "Mountpoint": "/var/lib/docker/volumes/my-volume/_data", "Name": "my-volume", "Options": null, "Scope": "local" } ]
1度ボリュームを削除しましょう。
$ docker volume rm my-volume $ docker volume ls DRIVER VOLUME NAME
最初にボリュームを作っておく場合。
$ docker volume create my-volume my-volume $ docker volume ls DRIVER VOLUME NAME local my-volume $ docker volume inspect my-volume [ { "CreatedAt": "2025-12-31T11:27:12+09:00", "Driver": "local", "Labels": null, "Mountpoint": "/var/lib/docker/volumes/my-volume/_data", "Name": "my-volume", "Options": null, "Scope": "local" } ]
これでも同じように使えます。
$ docker container run -it --rm --mount type=volume,src=my-volume,dst=/var/lib/named-volume ubuntu:24.04 bash -c 'echo hello > /var/lib/named-volume/hello.txt' $ docker container run -it --rm --mount type=volume,src=my-volume,dst=/var/lib/named-volume ubuntu:24.04 cat /var/lib/named-volume/hello.txt hello
ちなみにボリュームは使用しているコンテナがある場合は削除できません。
$ docker volume rm my-volume Error response from daemon: remove my-volume: volume is in use - [f7690b874d25adc94f3bbf7739df9f8bc9e2137bb582e5e0695325ee21dcb280]
匿名ボリューム
匿名ボリュームの場合は、--mountオプションまたは--volumeオプションにパスだけ指定します。
--mountオプションで指定する場合。
$ docker container run -it --rm --mount type=volume,dst=/var/lib/anonymous-volume ubuntu:24.04
--volumeオプションで指定する場合。
$ docker container run -it --rm --volume /var/lib/anonymous-volume ubuntu:24.04 $ docker container run -it --rm -v /var/lib/anonymous-volume ubuntu:24.04
匿名ボリュームもふつうに使えます。
# echo hello > /var/lib/anonymous-volume/hello.txt # cat /var/lib/anonymous-volume/hello.txt hello
この時、docker volume lsで見るとこのようなランダムな名前のボリュームが見つかります。
$ docker volume ls DRIVER VOLUME NAME local 18f70d123cba29c96d3f5cba0f662c14a4152b51b5ae389e0355c4dcd7f5c86e
inspectしてみます。
$ docker volume inspect 18f70d123cba29c96d3f5cba0f662c14a4152b51b5ae389e0355c4dcd7f5c86e [ { "CreatedAt": "2025-12-31T12:06:56+09:00", "Driver": "local", "Labels": { "com.docker.volume.anonymous": "" }, "Mountpoint": "/var/lib/docker/volumes/18f70d123cba29c96d3f5cba0f662c14a4152b51b5ae389e0355c4dcd7f5c86e/_data", "Name": "18f70d123cba29c96d3f5cba0f662c14a4152b51b5ae389e0355c4dcd7f5c86e", "Options": null, "Scope": "local" } ]
そして、docker container run時に--rmオプションを付けていると、匿名ボリュームはコンテナ終了時に
一緒に破棄されるのでした。
$ docker volume ls
DRIVER VOLUME NAME
では、--rmをつけるのをやめてみましょう。
$ docker container run -it --mount type=volume,dst=/var/lib/anonymous-volume ubuntu:24.04
匿名ボリュームができました。
$ docker volume ls DRIVER VOLUME NAME local 83ae5553a608865c6501975d94b8da7807ba6d0541e291cc4b42ab5e01880289
コンテナ内でファイルを作成しておきます。
# echo hello > /var/lib/anonymous-volume/hello.txt
今度はコンテナを終了してもボリュームが残ります。
$ docker volume ls DRIVER VOLUME NAME local 83ae5553a608865c6501975d94b8da7807ba6d0541e291cc4b42ab5e01880289
この匿名ボリュームを他のコンテナから使おうと思うと、このランダムな名前を使って名前付きボリュームとして
マウントします。
$ docker container run -it --mount type=volume,src=83ae5553a608865c6501975d94b8da7807ba6d0541e291cc4b42ab5e01880289,dst=/var/lib/anonymous-volume ubuntu:24.04 cat /var/lib/anonymous-volume/hello.txt hello
まあめんどうです…。こうするくらいなら名前付きボリュームを使いましょう、という感じですね。
残った匿名コンテナはdocker volume pruneで削除します。
$ docker volume prune
ただ、終了しても削除されていないコンテナが使っていたりすると消えないので、先にコンテナを掃除して
おきましょう。
組み合わせる
もちろん複数の種類のマウントを組み合わせることもできます。
$ docker container run -it --rm \ --mount type=bind,src=$(pwd)/data,dst=/var/lib/host-data \ --mount type=volume,src=my-volume,dst=/var/lib/named-volume \ --mount type=volume,dst=/var/lib/anonymous-volume \ ubuntu:24.04 $ docker container run -it --rm \ --volume $(pwd)/data:/var/lib/host-data \ --volume my-volume:/var/lib/named-volume \ --volume /var/lib/anonymous-volume \ ubuntu:24.04
おわりに
Dockerのストレージ、コンテナデータの永続性について見てみました。
今までここまで細かく見てこなかったので、なるほどというかだいぶ適当に使ってきたんだなという気分に
なりました。
次はストレージドライバーについても軽く見ておきたいなと思いました。