永続データ管理:適切なDockerボリュームタイプの選択

Dockerの名前付きボリューム、バインドマウント、tmpfsマウントを比較し、永続データ、開発、一時ストレージに最適な選択肢を解説します。

永続データ管理:適切なDockerボリュームタイプの選択

Dockerコンテナは交換可能であることを前提としています。コンテナの書き込み可能レイヤーに書き込まれたデータは、単純な停止/再起動では保持される可能性がありますが、そのコンテナに紐づいています。コンテナを削除または再作成すると、そのデータも消えてしまいます。これは、データベースファイル、アップロードされたアセット、キュー、または失うと困るものにとっては好ましくない状況です。

Dockerには、名前付きボリューム、バインドマウント、tmpfsマウントという3つの一般的なマウントオプションがあります。これらは異なる問題を解決します。本番環境のPostgresコンテナ、ローカルのNode.js開発コンテナ、一時的なシークレット用のスクラッチディレクトリは、すべて同じストレージパターンを使用すべきではありません。


Dockerストレージメカニズムの全体像

Dockerはリモートストレージにボリュームドライバーを使用できますが、日常的な判断のほとんどは、Docker Engineまたはホストカーネルによって管理されるこれら3つのマウントタイプに集約されます。

1. 名前付きボリューム:本番環境の標準

名前付きボリュームは、ほとんどの本番環境で永続データストレージに推奨されるメカニズムです。これらはDocker Engineによって完全に管理され、基盤となるホストファイルシステムのパスをユーザーから抽象化します。

機能と利点

  • 永続性: データは、作成元のコンテナが削除されても保持されます。
  • 移植性: コンテナ定義がハードコードされたホストパスに依存しないため、デプロイをマシン間で簡単に移動できます。
  • 管理: データはDockerのボリューム領域(通常Linuxでは/var/lib/docker/volumes/)に保存されます。docker volume lsdocker volume inspect、バックアップジョブで管理します。
  • バックアップと移行: ヘルパーコンテナ、ファイルシステムスナップショット、またはストレージレベルのバックアップを使用すれば、名前付きボリュームのバックアップは簡単です。データベースの場合は、整合性が重要な場合にデータベース対応のバックアップツールを優先してください。

ユースケース

  • 実際のバックアップと復元プロセスがある場合のデータベース。
  • アプリケーションの状態と重要な設定ファイル。
  • 同じホスト上のコンテナ間で共有する必要があるデータ。

実践例:名前付きボリュームの作成とアタッチ

# 1. ボリュームを作成
$ docker volume create db_storage

# 2. コンテナを実行し、ボリュームを必要なパスにマウント
$ docker run -d \
  --name postgres_db \
  -e POSTGRES_PASSWORD=securepass \
  --mount source=db_storage,target=/var/lib/postgresql/data \
  postgres:16

# 3. ボリュームの詳細を確認
$ docker volume inspect db_storage

2. バインドマウント:ローカル開発とホストとの連携

バインドマウントを使用すると、ホストマシン上の任意のファイルまたはディレクトリをコンテナ内にマッピングできます。名前付きボリュームとは異なり、バインドマウントはホストマシンの正確なディレクトリ構造に完全に依存します。

機能と制限

  • 即時更新: 主な利点はリアルタイム同期です。ホスト上で行われた変更(IDEでのコード更新など)は、実行中のコンテナ内に即座に反映されるため、開発ワークフローに最適です。
  • 非移植性: バインドマウントはホストに依存します。指定されたホストパスが別のマシンに存在しない場合、Dockerは失敗するか、構文とコンテキストに応じてディレクトリを作成する可能性があります。
  • 権限の問題: 所有権と権限(UID/GID)は、特に非rootユーザーとしてコンテナを実行する場合に、しばしば問題を引き起こします。コンテナユーザーは、ホストパスに対する読み取り/書き込み権限を持っている必要があります。
  • セキュリティリスク: コンテナプロセスが侵害された場合や、マウントが誤って書き込み可能になっている場合、ホストディレクトリを公開することは危険です。

ユースケース

  • ローカル開発: ライブデバッグやホットリロードのためにソースコードをマウントする。
  • 設定ファイル: 特定のホスト設定や認証情報(例:/etc/timezone)を注入する。
  • ホストリソースへのアクセス: ログや診断のためにローカルディレクトリをマウントする。

実践例:開発ワークフロー

カレントワーキングディレクトリ($(pwd))をコンテナ内のアプリケーションソースパスにマウントし、設定ファイルは読み取り専用に設定します。

# 開発用にカレントディレクトリをマウント
$ docker run -it --rm \
  --name dev_server \
  --mount type=bind,source=$(pwd)/src,target=/app/src \
  --mount type=bind,source=$(pwd)/config/app.conf,target=/etc/app/app.conf,readonly \
  node:22

ヒント: 特にボリュームタイプを混在させる場合は、明確さのために--mount構文(type=bind, source=..., target=...)を常に使用してください。ただし、単純なバインドマウントでは、短い-v構文(/host/path:/container/path)も依然として一般的です。

3. Tmpfsマウント:高速、非永続ストレージ

tmpfsマウントは、メモリバッキングストレージにデータを保存します。多くの一時的なワークロードでは高速ですが、データはディスクに保存されません。コンテナが停止するか、ホストシステムが再起動すると、データは失われます。

機能と制限

  • 速度: データがメモリバッキングストレージに存在するため、通常は高速です。
  • 非永続性: データは完全に揮発性です。ディスクに残してはならない機密性の高いデータに役立ちます。
  • リソース制限: ホストの利用可能なメモリによって制限されます。大規模なデータセットには適していません。
  • プラットフォーム範囲: tmpfsはLinuxの機能です。Docker DesktopはLinuxコンテナをVM内で実行する場合があるため、ネイティブのLinuxホストと同じ動作にはなりません。

ユースケース

  • 安全に消去できる一時的なセッションファイルやキャッシュファイル。
  • キャッシュメカニズム(例:Redisの一時ファイル)。
  • 実行直後にアーティファクトを破棄する必要があるセキュリティ重視の操作。

実践例:一時ファイルのキャッシュ

# /app/cacheディレクトリにtmpfsを使用してコンテナを実行
$ docker run -d \
  --name fast_cache \
  --mount type=tmpfs,destination=/app/cache,tmpfs-size=512m \
  my_web_server:latest

比較サマリーと決定マトリックス

適切なボリュームタイプの選択は、必要な永続性、移植性、アクセス要件に完全に依存します。

機能 名前付きボリューム バインドマウント Tmpfsマウント
永続性 高(Docker管理) 高(ホストFSに依存) なし(揮発性、RAMのみ)
移植性 優れている 低い(ホストパス依存) N/A(Linuxホストのみ)
パフォーマンス 通常良好、バッキングストレージに依存 変動あり、ホストパスとファイルシステム共有に依存 一時的なI/Oでは通常最速
データの場所 Docker内部ディレクトリ 特定のホストディレクトリ ホストメモリ(RAM)
管理 Docker CLIツール(docker volume ホストOSで管理 自動
主なユースケース 本番データ、データベース、共有ストレージ ローカル開発、設定注入 キャッシュ、セッション管理、安全な一時データ

データ管理のベストプラクティス

永続ストレージの標準化

永続性を必要とするほとんどのシングルホスト本番コンテナでは、名前付きボリュームがクリーンなデフォルトです。ハードコードされたホストパスを回避し、コンテナ定義を再利用しやすくします。オーケストレーション環境では、ローカルのDockerボリュームで十分だと想定するのではなく、プラットフォームの永続ボリュームシステムを使用してください。

ファイル権限の処理

バインドマウントを使用する場合、権限の不一致はよくある頭痛の種です。コンテナ内のユーザーが、ホスト上で別のユーザー/グループによって所有されているボリュームパスに書き込もうとすると、操作は失敗します。

コンテナ内のユーザーをマウントされたファイルの所有権に一致させるか、ホストディレクトリを意図的に調整してください。すべての権限問題をrootコンテナで解決することは避けてください。開発者のマシン中にroot所有のビルドアーティファクトが散らばるまで機能しますが、その後問題が発生します。

セキュリティのための読み取り専用マウントの使用

コンテナが変更すべきではない設定ファイル、静的リソース、または認証情報をマウントする場合は、常にボリュームを読み取り専用として指定してください。これにより、重要なファイルの誤った削除や変更を防ぎます。

# 読み取り専用マウントの例
$ docker run -d \
  --mount type=bind,source=/etc/my_key.pem,target=/app/key.pem,readonly \
  my_app

ホストルートのバインドマウントの回避

機密性の高い、または大きなルートディレクトリ(例:-v /:/host)をバインドすることは強く推奨されません。このプラクティスは重大なセキュリティ脆弱性を生み出し、意図しない副作用によりコンテナ管理を不安定にする可能性があります。

ボリュームのクリーンアップ

Dockerは、コンテナが削除されても名前付きボリュームを自動的に削除しません。匿名ボリュームも、コンテナが繰り返し再作成されると蓄積される可能性があります。特に共有ホストでは、プルーニング前に確認してください:

$ docker volume ls
$ docker system df -v

# 不要であることを確認した後、未使用のローカルボリュームを削除
$ docker volume prune

バックアップと復元が選択を左右する

マウントタイプは決定の半分に過ぎません。残りの半分は、問題が発生したときにデータをどのように復元するかです。

通常のファイルを保存する名前付きボリュームの場合、ヘルパーコンテナを使用してtarアーカイブを作成できます:

$ docker run --rm \
  --mount source=db_storage,target=/data,readonly \
  --mount type=bind,source=$(pwd),target=/backup \
  alpine:3.20 \
  tar -czf /backup/db_storage.tar.gz -C /data .

このパターンは、静的ファイルや停止したサービスには適しています。データベースが一貫した状態にない限り、稼働中のデータベースには不十分です。Postgres、MySQL、MongoDB、および同様のシステムでは、データベースネイティブのバックアップツール、またはデータベースと連携したストレージスナップショットを使用してください。稼働中のデータベースディレクトリのtarballはバックアップのように見えても、復元時に失敗する可能性があります。

名前付きボリュームの復元は逆の考え方です:

$ docker volume create db_storage_restored
$ docker run --rm \
  --mount source=db_storage_restored,target=/data \
  --mount type=bind,source=$(pwd),target=/backup,readonly \
  alpine:3.20 \
  tar -xzf /backup/db_storage.tar.gz -C /data

必要になる前にこれをテストしてください。一度も復元されたことのないボリューム戦略は戦略ではなく、推測に過ぎません。

実際のプロジェクト向けCompose例

Composeでは、名前付きボリュームはシンプルで読みやすいです:

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:

ローカル開発では、ホスト上のソース変更をコンテナ内に表示させたいため、通常はバインドマウントの方が適しています:

services:
  app:
    image: node:22
    working_dir: /app
    command: npm run dev
    volumes:
      - ./src:/app/src
      - ./package.json:/app/package.json:ro

package.jsonの読み取り専用フラグに注目してください。小さな習慣ですが、コンテナが読み取り専用にすべきファイルを書き換えるのを防ぎます。

Composeでのtmpfsの場合:

services:
  worker:
    image: my-worker:latest
    tmpfs:
      - /run/secrets:size=64m

これはスクラッチデータに使用し、クラッシュ後に検査することを期待するものには使用しないでください。

よくある障害モード

最も一般的なDockerストレージ障害は、間違ったパスをマウントすることです。アプリケーションが/var/lib/mysqlに書き込むが、イメージが/var/lib/mysql/dataを期待している場合、コンテナは実行され続け、再作成時にデータは消えます。常にイメージのドキュメントを確認し、実行中のコンテナを検査してください:

$ docker inspect my_container --format '{{json .Mounts}}'

もう一つのよくある障害は、匿名ボリュームと名前付きボリュームを混同することです。イメージがVOLUMEを宣言し、名前付きボリュームを提供しない場合、Dockerは匿名ボリュームを作成する可能性があります。データは保持されますが、名前が意味を持たないため、クリーンアップや移行中に見落とされることがあります。

権限は次の頭痛の種です。バインドマウントされたディレクトリがmacOSではUID 501、LinuxではUID 1000によって所有されているが、コンテナプロセスがUID 999として実行される場合、書き込みが失敗する可能性があります。名前付きボリュームはホストパスの混乱を回避することが多いですが、ボリューム内の所有権は依然として重要です。エラーがなくなるまで権限を変更するのではなく、意図的に所有権を初期化してください。

最後に、ローカルのDockerボリュームはローカルであることを忘れないでください。それらは自動的に別のホストにコンテナを追跡しません。Swarm、Kubernetes、Nomad、またはクラウドコンテナプラットフォームでは、永続ストレージにはプラットフォーム対応のボリューム、リモートストレージ、またはその環境向けに設計されたデータベースサービスが必要です。

ツールがサポートする場合は重要なボリュームにラベルを付け、どのサービスが各ボリュームを所有しているかを文書化してください。明確な所有権は、単に使用されていないように見えるデータを削除するクリーンアップスクリプトを防ぎます。

シンプルな決定ルール

迷ったときは、誰がデータを所有しているかを考えてください。Dockerが所有し、ホストパスが重要でない場合は、名前付きボリュームを使用します。人間またはホスト上の外部ツールが所有している場合は、バインドマウントを使用します。コンテナ終了後に誰も所有すべきでない場合は、tmpfsを使用します。

このルールはほとんどのケースをカバーします。データベースディレクトリはコンテナが所有するため、名前付きボリュームが適しています。ソースコードは開発者が所有するため、バインドマウントが適しています。1つのジョブのための一時的な復号化ディレクトリは消えるべきなので、tmpfsが適しています。混乱するケースは、共有アップロード、ログ、生成されたレポートです。これらの場合は、マウントタイプを選択する前に、コンテナプラットフォーム、ホスト、または外部ストレージサービスのどれが本当の所有者かを判断してください。

簡潔に言うと、コンテナが所有する永続データには名前付きボリュームを、ホストパス自体がワークフローの一部である場合はバインドマウントを、高速で使い捨て可能なデータにはtmpfsを使用します。そして、各重要なボリュームがどのようにバックアップおよび復元されるかを文書化してください。復元テストのない永続性は、マウントポイントに過ぎない希望です。