ボリュームとバインドマウント — Docker Tips
コンテナの中だけにファイルを置くと、コンテナ削除で消えます。残す手段がボリュームとバインドマウントで、前者は Docker 側の保管庫、後者はホストのフォルダそのものです。Windows ではパスの書き方がつまずきどころになります。
参考: Storage · Volumes · Bind mounts · Sharing local files with containers
目次
- データが消えるという問題
- 名前付きボリュームとバインドマウントの違い
- docker run でのマウント指定方法
- 名前付きボリュームを使う
- バインドマウントを使う
- Windows でのバインドマウントの注意点
- Compose での volumes 設定
- うまくいかないとき
データが消えるという問題
コンテナのファイルシステムは、イメージのレイヤーの上に 書き込み可能な層 を 1 枚重ねたものです。この書き込み可能な層はコンテナに紐づいており、docker rm でコンテナを削除すると一緒に失われます。
docker run -it --name temp-test ubuntu:24.04 bash# コンテナ内でecho "important data" > /data.txtexitdocker rm temp-test# /data.txt は永久に失われるこのコンテナを再度 docker run で作っても、それは 新しい書き込み層を持つ別のコンテナ であり、以前の /data.txt は存在しません。「コンテナを止めて start で再開する」なら書き込み層は残りますが、「rm して run し直す」場合は毎回ゼロからのファイルシステムになります。
学習や開発でよくあるパターンとして、DB のデータやアプリのログをコンテナの中だけに置いてしまい、コンテナを作り直した瞬間にすべて消えてしまう、という失敗があります。これを防ぐのがボリュームとバインドマウントです。
名前付きボリュームとバインドマウントの違い
| 項目 | 名前付きボリューム | バインドマウント |
|---|---|---|
| 実データの場所 | Docker が管理する領域(ホスト上のパスは意識しない) | ホストの指定したディレクトリ・ファイル |
| 作成のタイミング | 初回マウント時に Docker が自動作成 | -v は存在しなければホスト側ディレクトリを自動作成することが多い。--mount type=bind はソースが既に存在している必要がある |
| 用途の典型例 | DB のデータ永続化など「コンテナ管理下でよい」データ | ソースコードや設定ファイルなど「ホストから直接見たい・編集したい」データ |
| ポータビリティ | 高い(OS のパス構造に依存しない) | ホストのディレクトリ構造に依存する |
| 直接ホストから編集 | 基本的に想定されていない(docker volume inspect で場所は分かるが直接操作は非推奨) |
想定されている(ホストのエディタでそのまま編集できる) |
選び方:
- 「アプリやコンテナの外からは基本触らない、消えると困るデータ」→ 名前付きボリューム(DB のデータディレクトリなど)
- 「ホスト側のエディタで編集したい、コンテナと同期させたいファイル」→ バインドマウント(ソースコード、設定ファイルなど)
docker run でのマウント指定方法
マウントの指定には -v(--volume)と --mount の 2 つの書き方があります。
| フラグ | 書式 | 特徴 |
|---|---|---|
-v / --volume |
<ソース>:<コンテナ内パス>[:オプション] |
短く書ける。コロン区切りのため、Windows のドライブレター(C:)を含むパスは注意が必要 |
--mount |
type=<種類>,source=<ソース>,target=<コンテナ内パス> |
冗長だが明示的。公式ドキュメントでは新しい書き方として --mount が推奨されている |
# -v の例(名前付きボリューム)docker run -d -v mydata:/var/lib/data ubuntu:24.04 sleep infinity# --mount の例(同じ内容)docker run -d --mount type=volume,source=mydata,target=/var/lib/data ubuntu:24.04 sleep infinity--mount はキー・バリューの形式のためタイプミスに気づきやすく、Windows パスのようにコロンを含む文字列でも解釈のあいまいさが起きにくい利点があります。学習で最初に触るなら -v の方が短くて分かりやすいですが、パスにコロンが絡む場面(Windows のバインドマウントなど)では --mount を検討する価値があります。
名前付きボリュームを使う
docker volume create mydatadocker volume lsdocker volume inspect mydatadocker run -d --name vol-demo -v mydata:/data ubuntu:24.04 sleep infinitydocker exec vol-demo sh -c "echo hello > /data/hello.txt"docker rm -f vol-demodocker run -d --name vol-demo2 -v mydata:/data ubuntu:24.04 sleep infinitydocker exec vol-demo2 cat /data/hello.txt期待する結果: 2 つ目のコンテナ(vol-demo2)で hello が表示されます。コンテナを作り直しても、同じ名前のボリュームをマウントすれば データは引き継がれる ことが確認できます。
docker run -v <名前>:<パス> で、指定した名前のボリュームが無ければ Docker が自動的に作成してくれます。事前に docker volume create を実行しなくても動作しますが、明示的に作っておくと管理がしやすくなります。
不要になったボリュームは削除します。
docker rm -f vol-demo2docker volume rm mydataコンテナに使われているボリュームは削除できないため、先にコンテナを消してから docker volume rm を実行します。
バインドマウントを使う
Windows の PowerShell では、-v の相対パス解釈が環境によって不安定になることがあります。最初の実例は絶対パス($PWD.Path)を使い、確実に動く書き方から始めます。
New-Item -ItemType Directory -Force -Path .\bind-demo | Out-Nulldocker run -d --name bind-demo -v "$($PWD.Path)\bind-demo:/data" ubuntu:24.04 sleep infinitydocker exec bind-demo sh -c "echo from-container > /data/note.txt"コンテナ側で書いたファイルが、ホスト側のフォルダにそのまま現れます。
Get-Content .\bind-demo\note.txt逆に、ホスト側でファイルを作成・編集すれば、コンテナ内にも即座に反映されます。
"from-host" | Out-File .\bind-demo\from-host.txt -Encoding utf8docker exec bind-demo cat /data/from-host.txtbash(WSL / Git Bash)の場合は次のようになります。
mkdir -p ~/bind-demodocker run -d --name bind-demo -v ~/bind-demo:/data ubuntu:24.04 sleep infinity--mount を使う場合は次の形式です。-v と違い、存在しないソースは自動作成されない点に注意してください。
docker run -d --name bind-demo2 --mount "type=bind,source=$($PWD.Path)/bind-demo,target=/data" ubuntu:24.04 sleep infinity読み取り専用にしたい場合は末尾にオプションを付けます。
docker run -d --name bind-ro -v "$($PWD.Path)\bind-demo:/data:ro" ubuntu:24.04 sleep infinityWindows でのバインドマウントの注意点
Windows でバインドマウントを使うときは、パスの書き方と実行環境(WSL2 バックエンド)に関する特有の癖があります。
| 注意点 | 内容 |
|---|---|
| パスの渡し方 | Windows では -v の相対パスが失敗することがあるため、$($PWD.Path)\... のような絶対パスか、--mount の明示指定を基本にする |
| パス区切り文字 | \(バックスラッシュ)と /(スラッシュ)どちらでも通ることが多いが、/ に統一すると bash との差異が減る |
| WSL2 バックエンドでの実体 | Docker Desktop が Windows ホストパスを共有するときの内部パスは、おおむね /run/desktop/mnt/host/c/... 側。一般の WSL ディストリが見る /mnt/c/...(drvfs)とは別物 |
| ファイル変更通知の遅延 | ネットワークドライブや一部の同期フォルダ(OneDrive 配下など)ではファイル変更の検知が遅れることがある |
| パーミッション(実行権限など) | Windows のファイルシステムには Linux の実行ビットが無いため、コンテナ内で見えるパーミッションが実際の意図と異なることがある |
推奨: バインドマウント対象のフォルダは、OneDrive などクラウド同期の外(ローカル専用のパス)に置くと、同期処理とコンテナのファイル監視が競合しにくくなります。
-v でコロンが絡む Windows パスを渡すときに解釈が怪しいと感じたら、--mount type=bind,source=...,target=... の明示的な書き方に切り替えると、意図が伝わりやすくなります。
docker run -d --mount "type=bind,source=$($PWD.Path)/bind-demo,target=/data" ubuntu:24.04 sleep infinityCompose での volumes 設定
docker-compose.yml でも、同様に名前付きボリュームとバインドマウントを指定できます。case02/docker-compose.yml は現状ボリューム設定を持たない最小構成ですが、追加する場合のイメージは次のようになります。
services: ubuntu: build: . image: case02-ubuntu container_name: case02 restart: unless-stopped volumes: - case02-data:/data # 名前付きボリューム - ./workspace:/workspace # バインドマウント(相対パス)volumes: case02-data:トップレベルの volumes: に名前を宣言すると、Compose がそのボリュームを管理します。サービス側の volumes: では、<ソース>:<コンテナ内パス> の形式で名前付きボリュームとバインドマウントを混在させられます。相対パス(./workspace)は、docker-compose.yml が置かれているディレクトリを基準に解釈されます。
cd .\case02docker compose up -ddocker compose down# ボリュームは down だけなら残るdocker compose down -v# -v を付けると名前付きボリュームも削除されるdocker compose down だけではボリュームは削除されません。データも含めて完全に初期化したい場合は -v を付ける必要があります。
うまくいかないとき
| 症状 | 原因 | 対処 |
|---|---|---|
| コンテナを作り直したらデータが消えた | ボリューム・バインドマウントを使っていなかった | 重要なデータは名前付きボリュームかバインドマウントの先に書くようにする |
-v で Windows パスを指定するとエラーになる |
コロンの解釈がずれている | --mount type=bind,source=...,target=... の明示形式に変更 |
| バインドマウントしたフォルダが空に見える | パスの誤り、または --mount で存在しないソースを指定している |
絶対パスで指定し直し、--mount ならホスト側ディレクトリを先に作成する |
| ホストで編集した内容がコンテナに反映されない | 同期系フォルダ(クラウド同期)配下で監視が遅延している | ローカル専用のパスに移動して再試行 |
docker volume rm が volume is in use で失敗 |
ボリュームを使うコンテナが残っている(停止中でも) | 該当コンテナを docker rm してから削除 |
docker compose down -v の後にデータが必要だった |
-v がボリュームも削除する仕様だと知らなかった |
重要なデータは事前にバックアップ、または down(-v なし)を使う |
| コンテナ内で作成したファイルの所有者・権限が想定と違う | バインドマウント時のユーザー ID の不一致 | コンテナ側の実行ユーザーとホスト側の権限を確認し、必要なら chown や USER 設定を調整 |
「消えてよいデータ」と「消えてはいけないデータ」を最初に分けて考え、後者だけを確実にボリュームやバインドマウントの外側(コンテナのライフサイクルに縛られない場所)に置く、という方針がトラブルを減らす近道です。