以下の内容はhttps://kazuhira-r.hatenablog.com/entry/2026/01/01/002043より取得しました。


Dockerのストレージ(コンテナデータの永続性)について学ぶ(Volumes、Bind mounts、tmpfs mounts)

これは、なにをしたくて書いたもの?

今まで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のストレージに関するドキュメントはこちら。

Storage | Docker Docs

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オプション)などでよく使われるのは
バインドマウントだということに気づきます。というわけで、各マウントオプションについてもう少し
見ていきましょう。

バインドマウント

順番的にはボリュームマウントが先だと思うのですが、よく見るのはバインドマウントな気がするので先に
こちらを見ておきましょう。

バインドマウントは、ホスト上のファイルまたはディレクトリーをコンテナにマウントします。

Bind mounts | Docker Docs

ユースケースとしては以下のようなものがあります。

  • ホスト上の開発環境とコンテナ間でソースコードやビルド成果物を共有する
  • コンテナ内でファイルなどを作成した時に、ホスト側のファイルシステムに保存する
  • ホスト側の設定ファイルをコンテナと共有する

バインドマウントは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>

Bind mounts / Syntax

推奨は--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によって管理される永続的なストレージということでした。

Volumes | Docker Docs

ボリュームはdocker volume createコマンドで作成するか、コンテナやサービスの作成時にDocker Daemon
作成させることもできます。

ボリュームはDockerホスト上に保存され、コンテナにマウントすることで使用できます。またホスト側の機能からは
分離されていることがポイントです。

また以下が特徴です。

  • バックアップや移行を簡単に行える(バインドマウントより容易)
  • Docker CLIまたはAPIで管理できる
  • LinuxコンテナおよびWindowsコンテナの両方で動作する
  • 複数のコンテナ間で安全に共有可能
  • アプリケーションで高パフォーマンスのIOが求められる時に適している

Volumes / When to use volumes

一方で、ホストからボリューム内のファイルやディレクトリーにはアクセスできません。

ボリュームはコンテナのライフサイクルの外にあります。複数のコンテナで同時にマウントでき、自動的に削除される
ことはありません。使用されていないボリュームは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>

Volumes / Syntax

推奨は--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で作成したボリュームをマウントする場合は、externaltrueに指定します。

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 mounts | Docker Docs

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 1231 11:12 ./
drwxrwxr-x 3 xxxxx xxxxx 4096 1231 11:04 ../
-rw-rw-r-- 1 xxxxx xxxxx   16 1231 11:03 hello.txt
-rw-r--r-- 1 root    root      21 1231 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のストレージ、コンテナデータの永続性について見てみました。

今までここまで細かく見てこなかったので、なるほどというかだいぶ適当に使ってきたんだなという気分に
なりました。

次はストレージドライバーについても軽く見ておきたいなと思いました。




以上の内容はhttps://kazuhira-r.hatenablog.com/entry/2026/01/01/002043より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14