Chapter 16

3部: バインドマウント

ほげさん
ほげさん
2022.03.21に更新

バインドマウントもボリュームと同じくコンテナのデータをホストマシンと共有する方法です。

このページではバインドマウントの使い方とボリュームとの使い分けについて学びます。

全体構成とハイライト

image

やることの確認

やること できるようになること
App イメージをビルド ✅ PHP が準備できる
✅ メール送信が準備できる
App コンテナを起動しビルド結果を確認
Web サーバを起動
✅ Dockerfile の妥当性が確認できる
✅ Web サーバが起動できる
App 👉 ソースコードをバインドマウント ホストマシンの .php 編集が即反映される
App コンテナのポートを公開 ブラウザからアクセスできる
App コンテナをネットワークに接続
データベースサーバの接続設定
メールサーバの接続設定
DB コンテナに接続できる
Mail コンテナに接続できる
App Docker Compose 化 これらを1コマンドで再現できる
DB イメージをビルド ✅ 文字コードとログの設定ができる
DB 環境変数を指定してコンテナを起動 ✅ Dockerfile の妥当性が確認できる
✅ MySQL サーバが起動できる
✅ ユーザとデータベースを作成できる
DB データ置場にボリュームをマウント ✅ テーブルがコンテナ削除で消えなくなる
DB 👉 初期化クエリをバインドマウント コンテナ起動時にテーブルが作成される
DB コンテナをネットワークに接続
コンテナにエイリアスを設定
App コンテナからホスト名で接続できる
DB Docker Compose 化 これらを1コマンドで再現できる
Mail イメージを選定 ✅ SMTP サーバが起動できる
✅ Web サーバが起動できる
Mail コンテナを起動 ✅ Web サーバが起動できる
✅ SMTP サーバが起動できる
Mail コンテナのポートを公開 ブラウザからアクセスできる
Mail コンテナをネットワークに接続
コンテナにエイリアスを設定
App コンテナからホスト名で接続できる
Mail Docker Compose 化 これらを1コマンドで再現できる
ほか ボリュームを作成 ✅ マウントする準備ができる
ほか ネットワークを作成 コンテナを接続する準備ができる

バインドマウントとは

バインドマウントは ホストマシンの任意のディレクトリをコンテナにマウントする 仕組みです。

ホストマシンとコンテナ双方がファイルの変更に関心がある という場合に有用で、たとえばソースコードの共有などに活用できます。

image

ソースコードのディレクトリをコンテナにバインドマウントしてホストマシン側と共有すれば、ホストマシンでコードを変更した際に同期や転送が不要 になります。

構築

App コンテナに .php をバインドマウントして、ホストマシンで PHP のコーディングをする準備を整えます。

App コンテナにソースコードをマウントする

バインドマウントはボリュームと違い 既存のディレクトリをそのままマウントする ので、事前作成などはありません。

バインドマウントも --volume オプションと --mount オプションどちらでも行えます。
【 3部: ボリューム 】で --mount オプションを使う方が良いと結論づけましたが、せっかくなので両方説明します。

--volume オプションによるマウント

バインドマウントを行う場合も、: 区切りで設定を列挙します。

1つめを ボリューム名ではなく絶対パスにする とバインドマウントと判断されます。値はソースコードのある $(pwd)/src を指定します。
2つめはマウント先であり、これは自分で都合の良い場所にします。今回は /src とします。
3つめのオプションは特に指定しません。

また、ビルトインウェブサーバーのドキュメントルートをソースコードをマウントして配置する /src に変更したいため、<command> の末尾に -t /src を付け加えます。

Host Machine
$ docker container run          \
    --name app                  \
    --rm                        \
    --detach                    \
    --interactive               \
    --tty                       \
    --volume $(pwd)/src:/src    \
    docker-practice:app         \
    php -S 0.0.0.0:8000 -t /src

--mount オプションによるマウント

バインドマウントを行う場合も、同じキーを使って key=value 方式で設定します。

typevolume ではなく bind を指定します。
source はソースコードのある $(pwd)/src を指定します。
destination/src を指定します。

Host Machine
$ docker container run                        \
    --name app                                \
    --rm                                      \
    --detach                                  \
    --interactive                             \
    --tty                                     \
    --mount type=bind,src=$(pwd)/src,dst=/src \
    docker-practice:app                       \
    php -S 0.0.0.0:8000 -t /src

ソースコードがマウントできたことを確認する

App コンテナに接続して /src ディレクトリが確認できれば大丈夫です。

Host Machine
$ docker container exec  \
    --interactive        \
    --tty                \
    app                  \
    bash

# ls /src

form.php  history.php  index.php  mail.php

ホストマシンの変更がコンテナ内に、コンテナ内の変更がホストマシンに、即時反映されることが確認できます。

Host Machine
$ echo 'Hello World' > src/hello.txt
Container
# cat /src/hello.txt

Hello World

# rm /src/hello.txt
Host Machine
$ ls src

form.php  history.php  index.php  mail.php

ドキュメントルートを変更したので、Web サーバの応答を確認しておきます。

Container
# curl -sS localhost:8000 | grep '<title>'
<title>Hello | Docker Practice</title>

ホストマシンに置いた index.php を使ったレスポンスが得られれば大丈夫です。

DB コンテナに初期化クエリをマウントする

mysql:5.7 イメージは、コンテナ起動時に /docker-entrypoint-initdb.d に存在する .sql を実行してくれる拡張がされています。

そこに create table 文をバインドマウントしてコンテナを起動すれば、【 3部: コンテナの起動 】で環境変数で作ったデータベースとユーザに加えてテーブルも初期化することができます。

まずは実行したい .sql を作成します。
ルールはありませんが Dockerfile の近くが妥当でしょう。

docker/db/init.sql
create table event.mail
(
    sent_to varchar(32) not null,
    subject varchar(32) not null,
    body    varchar(64) not null,
    sent_at varchar(32) not null,
    result  char(1) not null
);

これを --mount オプションでバインドマウントして DB コンテナを起動します。

ただし初期化クエリは既にデータが存在する場合は実行されないため、docker-practice-db-volume の削除と再作成をする必要があります。
【 3部: ボリューム 】の最後で再作成をしていない方は確認してください。

まっさらなボリュームのマウントと初期化クエリのバインドマウントの2つを指定して、次のようにコンテナを起動します。

Host Machine
$ docker container run                                                                       \
    --name db                                                                                \
    --rm                                                                                     \
    --detach                                                                                 \
    --platform linux/amd64                                                                   \
    --env MYSQL_ROOT_PASSWORD=rootpassword                                                   \
    --env MYSQL_USER=hoge                                                                    \
    --env MYSQL_PASSWORD=password                                                            \
    --env MYSQL_DATABASE=event                                                               \
    --mount type=volume,src=docker-practice-db-volume,dst=/var/lib/mysql                     \
    --mount type=bind,src=$(pwd)/docker/db/init.sql,dst=/docker-entrypoint-initdb.d/init.sql \
    docker-practice:db

初期化クエリが実行されたことを確認する

DB コンテナに接続して event.mail テーブルがあることを確認できれば大丈夫です。

Host Machine
$ docker container exec                         \
    --interactive                               \
    --tty                                       \
    db                                          \
    mysql -h localhost -u hoge -ppassword event
    
mysql> show tables;
+-----------------+
| Tables_in_event |
+-----------------+
| mail            |
+-----------------+

バインドマウントの実体と注意

バインドマウントの実体は そのままホストマシンのファイルシステム です。

つまりバインドマウントはボリュームと比べると 実体の面倒を見ているのが Docker ではなく自分 であり、それが ホストマシン上 であるという違いがあります。

「仮想環境だから」と安易に rm -rf * でもして、もしそこにバインドマウントしたディレクトリが含まれていたら、削除はホストマシンに波及します
Git を使っていれば事故があってもなんとかなりますし、そもそも // にマウントするような極端なことをしなければ危険は全然ありませんが、違いは正しく把握しておくと良いでしょう。

Docker の公式も まずはボリュームを検討し、どうしてもだめならバインドマウントを使え と言っています。

COPY とバインドマウントの使い分け

COPY とバインドマウントはどちらもホストマシンのファイルをコンテナで扱えるようにする機能ですが、用途反映タイミング を理解しておかないと扱いを間違えやすいので整理しましょう。

COPYimage build をするタイミングでイメージにファイルを含める ため、コンテナが起動すればファイルが存在 します。
また 元ファイルの変更を行ってもコンテナには反映されない ため、image build の再実行が必要 になります。

image

COPY の用途には次のようなものが挙げられるでしょう。

  • 設定ファイルなど、コンテナによって変えない かつ 滅多に変更しない ものを配置する場合
  • 本番デプロイ時のソースコードなど、即起動できる配布物を作る 場合

対してバインドマウントは イメージではなくコンテナに行う ため、同じイメージを使ってもファイルの存在はコンテナ起動のオプションによって異なります。
また ホストマシンでファイルを変更するとコンテナに即時影響します

image

バインドマウントの用途には次のようなものが挙げられるでしょう。

  • 開発時のソースコードなど、ホストマシンで変更したいがコンテナに随時反映したい ものがある場合
  • 初期化クエリなど、イメージを配布する時点では用意できない ものがある場合

2つは イメージに対して行っているコンテナに対して行っている かが明確に違います。
その点をちゃんと理解しておけば、使い分け変更したいならどうすべきか も判断できます。

まとめ

このページの手順書と成果物は次のディレクトリで公開されています。

https://github.com/suzuki-hoge/docker-practice/tree/master/samples/3-5-バインドマウント

混乱してしまったときに参考にしてください。

ポイント

  • バインドマウントは ホストマシンでもファイルに関心がある という用途に向いている
  • コンテナ内でのファイル削除などが ホストマシンに影響する 可能性がある
  • イメージに対して行うCOPY とは コンテナに対して行う 点が大きく違う

できるようになったことの確認

やること できるようになること
App イメージをビルド ✅ PHP が準備できる
✅ メール送信が準備できる
App コンテナを起動しビルド結果を確認
Web サーバを起動
✅ Dockerfile の妥当性が確認できる
✅ Web サーバが起動できる
App 👉 ソースコードをバインドマウント ✅ ホストマシンの .php 編集が即反映される
App コンテナのポートを公開 ブラウザからアクセスできる
App コンテナをネットワークに接続
データベースサーバの接続設定
メールサーバの接続設定
DB コンテナに接続できる
Mail コンテナに接続できる
App Docker Compose 化 これらを1コマンドで再現できる
DB イメージをビルド ✅ 文字コードとログの設定ができる
DB 環境変数を指定してコンテナを起動 ✅ Dockerfile の妥当性が確認できる
✅ MySQL サーバが起動できる
✅ ユーザとデータベースを作成できる
DB データ置場にボリュームをマウント ✅ テーブルがコンテナ削除で消えなくなる
DB 👉 初期化クエリをバインドマウント ✅ コンテナ起動時にテーブルが作成される
DB コンテナをネットワークに接続
コンテナにエイリアスを設定
App コンテナからホスト名で接続できる
DB Docker Compose 化 これらを1コマンドで再現できる
Mail イメージを選定 ✅ SMTP サーバが起動できる
✅ Web サーバが起動できる
Mail コンテナを起動 ✅ Web サーバが起動できる
✅ SMTP サーバが起動できる
Mail コンテナのポートを公開 ブラウザからアクセスできる
Mail コンテナをネットワークに接続
コンテナにエイリアスを設定
App コンテナからホスト名で接続できる
Mail Docker Compose 化 これらを1コマンドで再現できる
ほか ボリュームを作成 ✅ マウントする準備ができる
ほか ネットワークを作成 コンテナを接続する準備ができる

image

次のページに進む前に

起動オプションをどんどん増やしていくため、このページで起動したコンテナは終了します。

Host Machine
$ docker container rm --force \
    app db

中途半端にボリュームにデータが残っているとこの先の【 3部: Docker Compose 】で都合が悪いので、ボリュームを削除しておきます。

Host Machine
$ docker volume rm \
    docker-practice-db-volume