手当たり次第に書くんだ

飽きっぽいのは本能

Ubuntu 22.04 Docker ボリュームの利用

目次に戻る

概要

コンテナからボリュームを利用します。

はじめに

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

目次に戻る

Ubuntu 22.04 Docker ボリュームの利用

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

トップへ戻る