Dock Stay
データを永続化する - docker volume / bind mount の使い方・オプション・サンプル

データを永続化する - docker volume / bind mount

データ永続化の基本。Docker が管理する volume と、ホスト側の任意パスを使う bind mount の違い、`:ro` / `:z` / `:Z` オプション、パーミッション問題の対処を整理する。

概念図

docker volume / bind mount diagram

構文

bash
docker volume COMMAND
# COMMAND: create, ls, inspect, rm, prune

docker run -v NAME:/path IMAGE            # named volume
docker run -v /host/path:/container IMAGE  # bind mount

実例

名前付きボリュームを作成

bash
docker volume create pgdata

名前付きボリュームで Postgres のデータを永続化

bash
docker run -v pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=x postgres:16

カレントディレクトリを読み取り専用で bind mount

bash
docker run -v "$(pwd)":/app:ro node:20 node /app/index.js

volume と bind mount

名前付き volume は Docker が管理する領域(Linux では /var/lib/docker/volumes/<name>/_data)に実体が置かれる。

docker volume createdocker volume lsdocker volume rm で管理し、--volumes-from で他コンテナから共有することもできる。

本番でデータベースの永続化に使うなら第一候補。

bind mount はホストの任意のパスをコンテナの任意のパスにマウントする。

-v /home/user/app:/app-v "$(pwd)":/app のように書く。

ホスト側の編集が即座にコンテナに反映されるため、開発時のソース同期に最適。

一方、ホストのディレクトリ構造やパーミッションに依存するため、本番や他環境に持ち込むと壊れやすい。

bash
docker volume create pgdata
docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:16

# bind mount for dev
docker run --rm -v "$(pwd)":/app -w /app node:20 npm test

`:ro` / `:z` / `:Z` オプション

マウント指定の末尾にオプションを付けられる。

:ro(read-only)はコンテナ側から書き込みを禁止する。

設定ファイルや静的アセットを渡すときは必ず付ける。

書き込み禁止にしておけばコンテナ内のプロセスが誤って上書きする事故を防げる。

:z:Z は SELinux が有効な環境(RHEL / Fedora / Rocky / 一部の CentOS Stream など)で必要になる。

:z は共有ラベル(複数コンテナから読み書き可)、:Z はプライベートラベル(そのコンテナ専用)を付与する。

SELinux 有効な環境で bind mount して Permission denied が出たら、ほぼこれで解決する。

Ubuntu / Debian デフォルトや Docker Desktop では不要。

bash
docker run --rm -v "$(pwd)/nginx.conf":/etc/nginx/nginx.conf:ro nginx

# SELinux host
docker run -v /srv/data:/data:Z myapp

データ永続化のパターン

データベースの永続化では名前付き volume がほぼ唯一の正解。

bind mount はホスト OS のファイルシステム(特に macOS / Windows)とデータベースの書き込みパターンの相性が悪く、性能低下やファイル破損の原因になる。

バックアップは docker run --rm -v pgdata:/data -v "$(pwd)":/backup alpine tar czf /backup/pg.tgz /data のような使い捨てコンテナで取得する。

ログやキャッシュはそもそも永続化が必要かを見直す。

ログは stdout に流して log driver に任せる、キャッシュは名前付き volume にして prune で明示的に消す、などの整理ができれば、コンテナの使い捨て性が保たれる。

UID / GID のズレで書き込めない場合は、Dockerfile で USER 1000:1000 を指定するか、docker run --user "$(id -u):$(id -g)" でホストのユーザーに揃える。

bash
# backup a named volume
docker run --rm \
  -v pgdata:/data \
  -v "$(pwd)":/backup \
  alpine tar czf /backup/pg-$(date +%F).tgz -C /data .

関連トピック