Dockerストレージ

この講座では、Dockerにおけるデータの永続化について学びます。

  • コンテナの特性とデータ消失の問題
  • ボリューム(Volume)によるデータ永続化
  • バインドマウントの仕組みと使い方
  • ボリュームの作成・確認・削除

1. なぜデータの永続化が必要なのか

コンテナを利用する場合、「コンテナを削除するとコンテナ内のデータも消える」という重要な特性があります。

コンテナは、イメージをもとに作成される一時的な実行環境です。コンテナ内でファイルを作成したり、データベースにデータを保存したりしても、そのコンテナを削除すると、それらのデータはすべて失われてしまいます。

これは、開発中に意図せずコンテナを削除してしまった場合や、コンテナを新しいバージョンに更新する際に問題となります。

しかし実際のアプリケーションでは、データを永続的に保持する必要があります。例えば以下のような用途で永続性が求められます。

  • ユーザ情報や取引履歴などのデータベースのデータ
  • ユーザがアップロードした画像やドキュメントなどのファイル
  • アプリケーションの動作履歴を残すログファイル
  • アプリケーションの動作を制御する設定ファイル

これらのデータをコンテナの外部に保存することで、コンテナを削除・再作成してもデータを維持できるようになります。

この仕組みを実現するのが、Dockerストレージの機能です。

2. Dockerのストレージ方式

Dockerでは、データを永続化するための方式がいくつか用意されています。

Dockerの主なストレージ方式とデータの保存先の関係を以下の図に示します。

flowchart TB
    subgraph ホストマシン
        direction TB
        V["ボリューム<br>Docker管理領域<br>/var/lib/docker/volumes/"]
        B["バインドマウント<br>ホスト上の任意のディレクトリ"]
    end
    subgraph コンテナ
        direction TB
        C["コンテナ内のファイルシステム"]
    end
    subgraph 外部サービス
        direction TB
        E["RDS / S3 など"]
    end
    V -->|"-v volume:/path"| C
    B -->|"-v ./host:/path"| C
    C -->|ネットワーク経由| E

2.1 ボリューム(Volume)

ボリュームは、Dockerが管理する専用の保存領域です。Docker推奨のデータ永続化方法であり、以下の特徴があります。

ボリュームはDockerが自動的に管理するため、ホストマシンのディレクトリ構造を意識する必要がありません。また、複数のコンテナ間でデータを共有することもできます。さらに、ボリュームドライバを使用することで、リモートストレージにも対応できます。バックアップや移行も容易に行えるため、ローカル環境でのデータ永続化や、開発・テスト環境でのデータベース運用に適しています。

2.2 バインドマウント(Bind Mount)

バインドマウントは、ホストマシン上の特定のディレクトリやファイルをコンテナ内にマウントする方式です。

ホストの特定のパスを直接コンテナに公開するため、開発時にソースコードをコンテナと共有する場合に便利です。ホスト側でファイルを編集すると、コンテナ内にも即座に反映されます。ただし、ホストのファイルシステムに依存するため、環境間での移植性は低くなります。開発環境でのソースコード共有や、設定ファイルの注入に適しています。

2.3 外部サービスへのデータ切り出し

本番環境では、ボリュームやバインドマウントを使わず、コンテナ外部のマネージドサービスにデータを保存することが一般的です。たとえば、データベースにはAmazon RDS、ファイルストレージにはAmazon S3などを使用します。

コンテナはステートレス(状態を持たない)に保ち、データの永続化は専用のサービスに任せることで、以下のメリットがあります。

  • コンテナを増減してもデータは外部サービスで一元管理され、スケーラビリティを確保できる
  • マネージドサービスがバックアップや冗長化を自動的に行い、運用が安定する
  • コンテナが停止してもデータは影響を受けず、障害耐性が高まる

このアプローチは、ECSやKubernetesなどのコンテナオーケストレーション環境で特に重要になります。

💡 ポイント
外部サービスへのデータ切り出しについては、別の講座で詳しく説明します。この講座では、ボリュームとバインドマウントの使い方を学んでいきましょう。

3. ハンズオン:ボリュームを使ってみよう

ストレージ管理については、実際に操作しながら理解するのが一番です。

まずはボリュームから見ていきます。ここでは、MySQLコンテナを使って、ボリュームによるデータ永続化を体験します。コンテナを削除しても、ボリュームに保存されたデータベースのデータが残ることを確認しましょう。

📝 MySQLとは
MySQLは、世界で最も広く使われているオープンソースのリレーショナルデータベース管理システム(RDBMS)です。Webアプリケーションのバックエンドとして、アプリケーションで利用する様々なデータを保存するために使用されます。

3.1 ボリュームの作成

まず、MySQLのデータを保存するためのボリュームを作成します。

docker volume create mysql-data

docker volume createコマンドは、Dockerが管理するボリュームを作成します。引数にはボリューム名を指定します。ここではmysql-dataという名前のボリュームを作成しています。作成したボリュームは、後でコンテナにマウントして使用します。

3.2 MySQLコンテナの起動

ボリュームをマウントしたMySQLコンテナを起動します。

docker container run -d \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=password \
  -e MYSQL_DATABASE=testdb \
  --name mysql-container \
  mysql:8.0

-v mysql-data:/var/lib/mysqlは、先ほど作成したボリュームをコンテナ内のディレクトリにマウントするオプションです。コロンの左側(mysql-data)がボリューム名、右側(/var/lib/mysql)がコンテナ内のマウント先パスです。MySQLはデータを/var/lib/mysqlに保存するため、ここにボリュームをマウントすることで、データベースのデータがボリュームに保存されるようになります。

その他のオプションは、MySQLコンテナの起動に必要な設定です。

  • -d: コンテナをバックグラウンドで実行します
  • -e MYSQL_ROOT_PASSWORD=password: rootユーザのパスワードを設定します
  • -e MYSQL_DATABASE=testdb: 起動時に作成するデータベース名を指定します
  • --name mysql-container: コンテナに名前を付けます

3.3 データの登録

MySQLに接続して、テストデータを登録します。

docker container exec -it mysql-container mysql -u root -ppassword testdb

mysql -u root -ppassword testdbの部分は、コンテナ内で実行するMySQLクライアントのコマンドです。-u rootでrootユーザとして接続し、-ppasswordでパスワードを指定しています(-pの直後にスペースなしでパスワードを記述します)。最後のtestdbは接続先のデータベース名です。

接続に成功するとmysql>というプロンプトが表示されます。これはMySQLがコマンドの入力を待っている状態です。このプロンプトが表示されたら、テーブルを作成してデータを登録します。

データベースの操作にはSQL(Structured Query Language)というデータベース専用の言語を使用します。SQLの詳細な説明は省略しますが、以下の手順でテーブルの作成とデータの登録を行います。

まず、CREATE TABLEでテーブルを作成します。

CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50));

次に、INSERT INTOでデータを登録します。

INSERT INTO users VALUES (1, 'Taro'), (2, 'Hanako');

最後に、SELECTで登録されたデータを確認します。

SELECT * FROM users;

以下のように表示されれば、データが登録されています。

+----+-------+
| id | name  |
+----+-------+
|  1 | Taro   |
|  2 | Hanako |
+----+-------+

MySQLから抜けます。

exit

3.4 コンテナの削除

ここで一度、MySQLコンテナを停止・削除します。通常であれば、このタイミングでデータも一緒に消えてしまいますが、ボリュームを使っているためデータを残すことができます。

docker container stop mysql-container
docker container rm mysql-container

3.5 データが残っていることを確認

データが再び利用できることを確認するために、同じボリュームをマウントして、新しいMySQLコンテナを起動します。

docker container run -d \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=password \
  --name mysql-container-2 \
  mysql:8.0

起動を待ってから、MySQLに接続してデータを確認します。

docker container exec -it mysql-container-2 mysql -u root -ppassword testdb

SQLのSELECT分を実行して、データを確認します。

SELECT * FROM users;

先程登録したデータと同じデータが検索できます。これにより、ボリュームにデータが残っており、再び起動したときにそれを利用できます。

+----+-------+
| id | name  |
+----+-------+
|  1 | Taro   |
|  2 | Hanako |
+----+-------+

確認が終わったら、MySQLから抜けておきます。

exit

これがボリュームの最大のメリットです。データベースコンテナをバージョンアップする際や、コンテナに問題が発生して再作成する際にも、データを失うことなく運用を継続できます。

3.6 ボリュームの保存場所を確認する

ボリュームのデータは、どこに保存されているのでしょうか。docker volume inspectコマンドで確認できます。

docker volume inspect mysql-data
[
    {
        "CreatedAt": "2024-01-15T10:30:00Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/mysql-data/_data",
        "Name": "mysql-data",
        "Options": null,
        "Scope": "local"
    }
]

Mountpointにボリュームの保存場所が表示されています。WindowsやmacOSでDocker Desktopを使っている場合、このパスはDocker Desktopの仮想マシン内のパスです。そのため、FinderやExplorerから直接このフォルダを開くことはできません。

ボリューム内のファイルを確認したい場合は、コンテナ経由でアクセスします。以下のコマンドで、ボリューム内のファイル一覧を確認できます。

docker container run --rm -v mysql-data:/data alpine ls -la /data

以下のようにMySQLのデータファイルが表示されます。

total 96780
drwxrwxrwt    8 999      999           4096 Jan 15 10:35 .
drwxr-xr-x    1 root     root          4096 Jan 15 10:40 #
-rw-r-----    1 999      999             56 Jan 15 10:30 auto.cnf
-rw-r-----    1 999      999           3085 Jan 15 10:30 ca-key.pem
-rw-r-----    1 999      999           1112 Jan 15 10:30 ca.pem
-rw-r-----    1 999      999           1112 Jan 15 10:30 client-cert.pem
-rw-r-----    1 999      999           1680 Jan 15 10:30 client-key.pem
-rw-r-----    1 999      999        3955928 Jan 15 10:35 ib_buffer_pool
-rw-r-----    1 999      999       12582912 Jan 15 10:35 ibdata1
drwxr-x---    2 999      999           4096 Jan 15 10:30 mysql
drwxr-x---    2 999      999           4096 Jan 15 10:30 performance_schema
-rw-r-----    1 999      999           1680 Jan 15 10:30 private_key.pem
-rw-r-----    1 999      999            452 Jan 15 10:30 public_key.pem
-rw-r-----    1 999      999           1112 Jan 15 10:30 server-cert.pem
-rw-r-----    1 999      999           1680 Jan 15 10:30 server-key.pem
drwxr-x---    2 999      999           4096 Jan 15 10:30 sys
drwxr-x---    2 999      999           4096 Jan 15 10:32 testdb

testdbディレクトリが、先ほど作成したデータベースです。このように、MySQLのデータがボリューム内に保存されていることが確認できます。

💡 ポイント
Linux上でDockerを直接動かしている場合は、Mountpointのパス(/var/lib/docker/volumes/mysql-data/_data)に直接アクセスしてファイルを確認できます。

3.7 不要リソースの削除

ハンズオンが終わったら、作成したリソースを削除します。ボリュームはコンテナにマウントされている間は削除できないため、まずコンテナを停止・削除してからボリュームを削除します。

コンテナを停止します。

docker container stop mysql-container-2

次に、コンテナを削除します。

docker container rm mysql-container-2

最後に、ボリュームを削除します。

docker volume rm mysql-data

docker volume rmは、指定したボリュームを削除するコマンドです。コンテナを削除しても、ボリュームは自動的には削除されません。これは、データを誤って消してしまうことを防ぐための仕様です。ボリュームを削除するには、このコマンドを明示的に実行する必要があります。

3.8 ボリューム関連コマンドまとめ

今回のハンズオンで使用したボリューム関連のコマンドをまとめます。

コマンド 説明
docker volume create <ボリューム名> 新しいボリュームを作成する
docker volume inspect <ボリューム名> ボリュームの詳細情報を表示する
docker volume rm <ボリューム名> ボリュームを削除する
docker container run -v <ボリューム名>:<コンテナ内パス> コンテナ起動時にボリュームをマウントする

4. ハンズオン:バインドマウントを使ってみよう

続いて、もうひとつのデータ共有方法であるバインドマウントを使ってみましょう。

ここでは、ホスト側のHTMLファイルをNginxコンテナにバインドマウントし、ホスト側でファイルを編集するとコンテナ内にも即座に反映されることを確認します。

4.1 作業フォルダの準備

任意の場所にbind-mount-demoフォルダを作成し、Visual Studio Codeの「ファイル」→「フォルダーを開く」から、作成したbind-mount-demoフォルダを開きます。

bind-mount-demo  ← このフォルダを作成

index.htmlファイルを作成します。Visual Studio Codeのエクスプローラーでbind-mount-demoフォルダを右クリックし、「新しいファイル」を選択してindex.htmlという名前で作成してください。

bind-mount-demo
└── index.html  ← このファイルを作成

作成したファイルに以下の内容を記述して保存します。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Bind Mount Demo</title>
</head>
<body>
    <h1>Hello from Bind Mount!</h1>
</body>
</html>

4.2 ターミナルの起動

Visual Studio Codeのメニューから「ターミナル」→「新しいターミナル」を選択して、ターミナルを開きます。ターミナルは自動的に作業フォルダをカレントディレクトリとして開くため、フォルダの移動は不要です。

4.3 Nginxコンテナの起動

作成したフォルダをNginxコンテナにバインドマウントして起動します。

Macの場合:

docker container run -d \
  -v $(pwd):/usr/share/nginx/html \
  -p 8080:80 \
  --name nginx-bindmount \
  nginx:alpine

Windowsの場合(PowerShell):

docker container run -d -v ${PWD}:/usr/share/nginx/html -p 8080:80 --name nginx-bindmount nginx:alpine

-v $(pwd):/usr/share/nginx/html(Windowsでは -v ${PWD}:/usr/share/nginx/html)で、現在のディレクトリ(bind-mount-demoフォルダ)をNginxのドキュメントルートにマウントしています。ボリュームとの違いは、マウント元にホストマシンの絶対パスを指定する点です。$(pwd) / ${PWD} は現在のディレクトリの絶対パスに展開されます。

ブラウザで http://localhost:8080 にアクセスすると、「Hello from Bind Mount!」と表示されます。

4.4 ホスト側でファイルを編集する

ホスト側でHTMLファイルを編集してみましょう。Visual Studio Codeでindex.htmlを開き、<p>タグを追加します。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Bind Mount Demo</title>
</head>
<body>
    <h1>Hello from Bind Mount!</h1>
    <p>This file was edited on the host machine.</p>
</body>
</html>

ファイルを保存し、ブラウザをリロードすると、変更が即座に反映されていることが確認できます。コンテナを再起動する必要はありません

これがバインドマウントの最大のメリットです。開発中にソースコードを編集するたびにコンテナを再起動する必要がないため、開発効率が大幅に向上します。

4.5 不要リソースの削除

ハンズオンが終わったら、作成したリソースを削除します。

docker container stop nginx-bindmount
docker container rm nginx-bindmount

作成したbind-mount-demoフォルダは、不要であれば手動で削除してください。

バインドマウントはボリュームと異なり、Dockerが管理する領域ではなくホストのディレクトリを直接使用します。そのため、コンテナを削除してもホスト側のファイルはそのまま残ります。

4.6 どのようなコードが即時反映されるか

バインドマウントでファイルを編集すると、コンテナ内のファイルも即座に更新されます。ただし、アプリケーションに変更が反映されるかどうかは、言語やフレームワークの特性によって異なります。

静的ファイル(HTML、CSS、JavaScript、画像など)

NginxやApacheなどのWebサーバは、リクエストのたびにファイルを読み込みます。そのため、ファイルを編集するとブラウザをリロードするだけで変更が反映されます。今回のハンズオンはこのケースに該当します。

インタプリタ言語(Python、Ruby、PHP、Node.jsなど)

多くのフレームワークは、ファイルの変更を検知して自動的にリロードする機能を持っています。たとえば、PythonのFastAPIでは--reloadオプションを指定すると、RubyのRailsでは開発モードで起動すると、コードの変更が自動的に反映されます。この自動リロード機能が無効の場合は、プロセスの再起動が必要です。なお、本番環境では自動リロードを無効にするのが一般的です。ファイル監視によるオーバーヘッドを避け、意図しないコードの変更が反映されるリスクを防ぐためです。

コンパイル言語(Go、Rust、Java、C/C++など)

コンパイル言語では、ソースコードを変更しても自動的には反映されません。変更を反映するには、コードを再コンパイルし、アプリケーションを再起動する必要があります。開発効率を上げるために、ファイルの変更を検知して自動的に再ビルド・再起動するツール(Goのairなど)を使用することもあります。

5. まとめ

この講座では、Dockerストレージについて学びました。

  • コンテナを削除すると、コンテナ内のデータは消失する
  • データを永続化するには、ボリュームまたはバインドマウントを使用する
  • ボリュームはDockerが管理する保存領域で、本番環境での使用に適している
  • バインドマウントはホストのディレクトリをマウントし、開発環境での使用に適している
  • -vオプションや--mountオプションでコンテナにストレージをマウントできる