Contents
概要
コンテナからボリュームを利用します。
はじめに
Dockerにはバインドマウントとボリュームがあり、何れもデータの永続化が目的ですが別の機能です。Dockerのボリュームは歴史的な経緯のせいかコマンドオプションも冗長で少々ややこしくなっています。
種類 | 説明 |
---|---|
バインドマウント | バインドマウントはホストOSのディレクトリを指定してマウントします。バインドマウントは管理が楽になるケースもあると思いますが、ホストに依存するためDockerは推奨していないようです。厳密にはボリュームではなくバインドマウントと言います。 |
ボリューム | ボリュームは完全にDockerによって管理されます。 |
バインドマウント
Dockerの公式サイトのサンプルに基づいてテストします。
テスト用コンテナイメージ
テスト用のコンテナイメージにNginxを使用します。
myadmin@ubuntu:~$ docker run -d -it --name nginx -p 80:80 nginx:latest myadmin@ubuntu:~$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 61609d8bf776 nginx:latest "/docker-entrypoint.…" 14 seconds ago Up 14 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx
Nginxコンテナのドキュメントルートは/usr/share/nginx/htmlです。デフォルトでindex.htmlが配置されています。
myadmin@ubuntu:~$ docker exec -it nginx bash root@61609d8bf776:/# cat /etc/nginx/conf.d/default.conf server { location / { root /usr/share/nginx/html; index index.html index.htm; } } root@61609d8bf776:/# ls -l /usr/share/nginx/html total 8 -rw-r--r-- 1 root root 497 Jan 25 15:03 50x.html -rw-r--r-- 1 root root 615 Jan 25 15:03 index.html
ホストのバインドマウント用ディレクトリが存在しない場合の–mountと-vの挙動の違い
公式サイトの説明通りの挙動となります。–mountはホストのバインドマウント用ディレクトリが存在しないとコンテナの起動に失敗しますが、-vは同じ条件の場合にバインドマウント用ディレクトリを自動生成します。–mountと-vの違いはこれだけです。
Dockerとしては–mountを推奨しているようですが、–mountは記述量が多くなる点、前述のように違いが明確である点から、以降は-vで記載します。
–mount
–mountの場合はコンテナの起動に失敗します。
myadmin@ubuntu:~$ docker run -d -it --name test1 --mount type=bind,source=/container/data,target=/app nginx:latest
docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /container/data.
See 'docker run --help'.
-v
-vの場合は/container/dataが自動的に生成されコンテナが起動します。sudoは不要なようです。
myadmin@ubuntu:~$ docker run -d -it --name test1 -v /container/data:/app nginx:latest
コンテナとバインドマウント用ディレクトリを削除します。
myadmin@ubuntu:~$ docker rm -f test1 myadmin@ubuntu:~$ sudo rm -r /container/data
Nginxのドキュメントルートをバインドマウント
コンテナの/usr/share/nginx/htmlがホストの/container/dataに置き換わるため空になります。
myadmin@ubuntu:~$ docker run -d -it --name test1 -p 80:80 -v /container/data:/usr/share/nginx/html nginx:latest myadmin@ubuntu:~$ docker exec test1 ls -l /usr/share/nginx/html total 0
ホストでindex.htmlを作成すると外部からアクセス可能になります。
myadmin@ubuntu:~$ echo "test1" | sudo tee /container/data/index.html
total 0
コンテナとテストデータを削除します。
myadmin@ubuntu:~$ docker rm -f test1 myadmin@ubuntu:~$ sudo rm -r /container/data
ボリューム
ボリュームの生成と管理
ボリュームの生成方法は下記の通りです。コンテナ生成時に合わせて作成することもできます。
ボリュームを生成します。
myadmin@ubuntu:~$ docker volume create my-vol
ボリュームを確認します。
myadmin@ubuntu:~$ docker volume ls
DRIVER VOLUME NAME
local my-vol
ボリュームの詳細を確認します。
myadmin@ubuntu:~$ docker volume inspect my-vol
[
{
"CreatedAt": "2022-06-10T16:00:18+09:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
ボリュームを削除します。
myadmin@ubuntu:~$ docker volume rm my-vol
ボリュームを使用したコンテナの起動
存在しないボリュームを指定してコンテナを起動します。/を付けるとバインドマウント、付けないとボリュームとして認識するようです。
myadmin@ubuntu:~$ docker run -d --name test1 -p 80:80 -v myvol2:/app nginx:latest
ボリュームが自動生成されます。
myadmin@ubuntu:~$ docker volume ls
DRIVER VOLUME NAME
local myvol2
ボリュームの詳細を確認します。
myadmin@ubuntu:~$ docker inspect test1
"Mounts": [
{
"Type": "volume",
"Name": "myvol2",
"Source": "/var/lib/docker/volumes/myvol2/_data",
"Destination": "/app",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
コンテナとボリュームを削除します。
myadmin@ubuntu:~$ docker rm -f test1 myadmin@ubuntu:~$ docker volume rm myvol2
コンテナデータの同期
バインドマウントではマウントしたホストのディレクトリの状態になりますが、ボリュームの場合はコンテナのデータがボリュームにコピーされます。データが保存された同じボリュームを使用する場合はボリューム内のデータが優先されます。マージは行われません。
myadmin@ubuntu:~$ docker run -d --name test1 -p 80:80 -v myvol2:/usr/share/nginx/html nginx:latest
myadmin@ubuntu:~$ docker run -d --name test1 -p 80:80 -v myvol2:/usr/share/nginx/html nginx:latest
ディレクトリ等を準備します。
myadmin@ubuntu:~$ sudo mkdir -p /container/data myadmin@ubuntu:~$ echo "host data" | sudo tee /container/data/host_data.txt
コンテナを起動します。ホストの/container/dataをコンテナの/appにマウントしています。
myadmin@ubuntu:~$ docker run -d -it --name test1 --mount type=bind,source=/container/data,target=/app nginx:latest myadmin@ubuntu:~$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9272bd2208bd nginx:latest "/docker-entrypoint.…" 26 seconds ago Up 24 seconds 80/tcp test1
コンテナからホストで準備したファイルを確認できます。
myadmin@ubuntu:~$ docker exec test1 ls -l /app
total 4
-rw-r--r-- 1 root root 10 Jun 10 03:26 host_data.txt
コンテナからホストのディレクトリに書き込み可能です。
myadmin@ubuntu:~$ docker exec test1 touch /app/container_data.txt myadmin@ubuntu:~$ ls -l /container/data total 4 -rw-r--r-- 1 root root 0 Jun 10 12:32 container_data.txt -rw-r--r-- 1 root root 10 Jun 10 12:26 host_data.txt
コンテナとテストデータを削除します。
myadmin@ubuntu:~$ docker rm -f test1 myadmin@ubuntu:~$ sudo rm -fr /container/data/*
ケース#2
コンテナを起動します。ホストの/container/dataをコンテナの/usr/share/nginx/htmlにマウントしています。
myadmin@ubuntu:~$ docker run -d -it --name test2 --mount type=bind,source=/container/data,target=/usr/share/nginx/html nginx:latest myadmin@ubuntu:~$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES aa67248a5204 nginx:latest "/docker-entrypoint.…" 8 seconds ago Up 6 seconds 80/tcp test2
コンテナの/usr/share/nginx/htmlを確認すると空です。期待値としてはコンテナの/usr/share/nginx/html内のファイルが/container/dataにコピーされ、コンテナから見るとマウントを意識しない状態になると思いましたが違うようです。-vと–mountの動作の違いによるものなのでしょうか。
myadmin@ubuntu:~$ docker exec test2 ls -l /usr/share/nginx/html
total 0
コンテナとテストデータを削除します。
myadmin@ubuntu:~$ docker rm -f test2 myadmin@ubuntu:~$ sudo rm -fr /container/data/*
ケース#3
コンテナを起動します。ホストの/container/dataをコンテナの/usr/share/nginx/htmlにマウントしています。
myadmin@ubuntu:~$ docker run -d -it --name test3 -v /container/data:/usr/share/nginx/html nginx:latest myadmin@ubuntu:~$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ea0396f9c618 nginx:latest "/docker-entrypoint.…" 10 seconds ago Up 9 seconds 80/tcp test3
コンテナの/usr/share/nginx/htmlを確認すると空です。期待値としてはコンテナの/usr/share/nginx/html内のファイルが/container/dataにコピーされ、コンテナから見るとマウントを意識しない状態になると思いましたが違うようです。-vと–mountの動作の違いによるものなのでしょうか。
myadmin@ubuntu:~$ docker exec test3 ls -l /usr/share/nginx/html
total 0
コンテナとテストデータを削除します。
myadmin@ubuntu:~$ docker rm -f test2 myadmin@ubuntu:~$ sudo rm -fr /container/data/*
バインドマウントを用いたコンテナの起動
マニュアル語が何言ってるか分からないので箇条書きにして整理します。
- ホスト上のsourceディレクトリにソースコードが置かれている。
- ホスト上のsource/targetディレクトリに成果物が置かれている。
- コンテナは/appディレクトリから成果物にアクセスできるようにする。つまり最新の成果物にコンテナがアクセスできるようにする。
- 以下のコマンドはsource/targetディレクトリをコンテナ内の/appにバインドマウントしている。
- $(pwd)はカレントディレクトリを展開する為、このコマンドはsourceディレクトリからの実行を想定している。
ディレクトリ等を準備します。
myadmin@ubuntu:~$ mkdir -p source/target myadmin@ubuntu:~$ touch source/target/test.txt myadmin@ubuntu:~$ cd source
コンテナを起動します。イメージはubuntu:22.04にしました。
myadmin@ubuntu:~$ docker run -d -it --name devtest --mount type=bind,source="$(pwd)"/target,target=/app ubuntu:22.04
コンテナからホスト側で準備したファイルを確認します。
myadmin@ubuntu:~$ docker exec devtest ls -l /app
total 0
-rw-rw-r-- 1 1000 1000 0 Jun 10 01:03 test.txt
コンテナからホスト側のディレクトリに書き込めるか確認します。
myadmin@ubuntu:~$ docker exec devtest touch /app/test2.txt myadmin@ubuntu:~$ ls -l source/target total 0 -rw-r--r-- 1 root root 0 Jun 10 10:32 test2.txt -rw-rw-r-- 1 myadmin myadmin 0 Jun 10 10:03 test.txt
バインドマウントの詳細を確認します。Mountsの箇所を抜粋しています。
myadmin@ubuntu:~$ docker inspect devtest
"Mounts": [
{
"Type": "bind",
"Source": "/home/myadmin/source/target",
"Destination": "/app",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
コンテナを削除します。
myadmin@ubuntu:~$ docker rm -f devtest
コンテナ上の空ではないディレクトリへのマウント
これは公式サイトの通りにnginxコンテナを使用しています。ホスト側の/tmpをコンテナの/usrにバインドマウントしていますが、/usrの中身がホストの/tmpとなり起動しません。
myadmin@ubuntu:~$ docker run -d -it --name broken-container --mount type=bind,source=/tmp,target=/usr nginx:latest myadmin@ubuntu:~$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 47b8bdd2bb1d nginx:latest "/docker-entrypoint.…" 42 seconds ago Exited (127) 41 seconds ago broken-container
コンテナを削除します。
myadmin@ubuntu:~$ docker rm -f broken-container
コンテナ上の空ではないディレクトリへのマウント
コンテナの起動に関係しない/usr/share/nginx/htmlをホストのsource/targetに置き換えてみます。source/targetは空にしています。
myadmin@ubuntu:~$ rm -fr source/target/* myadmin@ubuntu:~$ cd source myadmin@ubuntu:~$ docker run -d -it --name broken-container --mount type=bind,source="$(pwd)"/target,target=/usr/share/nginx/html nginx:latest
ディレクトリ等を準備します。
myadmin@ubuntu:~$ mkdir -p source/target myadmin@ubuntu:~$ echo "bind mount test" source/target/index.html myadmin@ubuntu:~$ cd source
myadmin@ubuntu:~$ docker run -d -it --name broken-container --mount type=bind,source=/tmp,target=/usr nginx:latest myadmin@ubuntu:~$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 47b8bdd2bb1d nginx:latest "/docker-entrypoint.…" 42 seconds ago Exited (127) 41 seconds ago broken-container
ボリュームの作成
myadmin@ubuntu:~$ docker volume create --opt type=nfs --opt o=addr=msrv.mgmt.si1230.com,ro,nfsvers=4 --opt device=:/home/k8s/pki nfs-pki