Docker コンテナが起動中に新たなデバイスを bind mount 先にマウントしてもコンテナは認識しない
はじめに
お疲れ様です。
Acompany の R&D チーム所属の qweoaiu です。
本記事では、Docker の mount 機能に関する豆知識を紹介します。
本記事では、ホストで行うマウントをカタカナで「マウント」、Docker のマウント機能を英字で「mount」と表記します。
まとめ
内容を簡潔にまとめると、次のとおりです。
Docker コンテナが起動中のときに、mount したディレクトリにホスト側で新たなデバイスをマウントすると、コンテナはそのマウントポイントを、
bind mount のときは認識しない
volume mount のときは認識する
ディスク容量不足が予期されるときの対処
Bind mount は便利ですが、ディスク容量が足りなくなる可能性がある場合は volume mount を使用しましょう。
Docker 公式は volume mount の使用を推奨しています。
エラーから復帰できる環境であれば、次のエラーが出てきても、ディスクを追加してマウントすればなんとかなる可能性があります。
OSError: [Errno 28] No space left on device
そのような状況に陥る前に、容量に余裕のあるディスクを最初から使いましょう。
検証
検証環境を用意して、実際に確認します。
コマンドの出力は一部加工しています。
OS は Debian 11 bullseye です。
デバイスの準備
root ファイルシステムが /dev/sdaX にあるホスト環境に、新たなデバイス /dev/sdb を追加し、/dev/sdb1 に ext4 パーティションを用意します。
/dev/sdb1 が Docker コンテナの mount 先にマウントするパーティションです。
まず、fdisk コマンドで、/dev/sdb が存在するか確認します。
$ sudo fdisk -l
Disk /dev/sda: 30 GiB, 32212254720 bytes, 62914560 sectors
Disk model: PersistentDisk
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier:
Device Start End Sectors Size Type
...
Disk /dev/sdb: 10 GiB, 10737418240 bytes, 20971520 sectors
Disk model: PersistentDisk
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
/dev/sdb が認識されているので、GPT パーティションを作成し、ext4 でフォーマットします。
$ sudo fdisk /dev/sdb
Welcome to fdisk (util-linux 2.36.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0x0a2696a7.
Command (m for help): g
Created a new GPT disklabel (GUID: ).
Command (m for help): n
Partition number (1-128, default 1):
First sector (2048-20971486, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-20971486, default 20971486):
Created a new partition 1 of type 'Linux filesystem' and of size 10 GiB.
Command (m for help): p
Disk /dev/sdb: 10 GiB, 10737418240 bytes, 20971520 sectors
Disk model: PersistentDisk
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: 674D4DE8-37A0-5444-9E02-C26287EA2A3C
Device Start End Sectors Size Type
/dev/sdb1 2048 20971486 20969439 10G Linux filesystem
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
$ sudo mkfs.ext4 /dev/sdb1
mke2fs 1.46.2 (28-Feb-2021)
Discarding device blocks: done
Creating filesystem with 2621179 4k blocks and 655360 inodes
Filesystem UUID: 5bc9e73e-58d2-4731-b02a-c7c8dea250fa
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632
Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
これでデバイスの準備は完了です。
Bind mount で実験
Docker コンテナ起動中に bind mount 先に /dev/sdb1 をマウントして、コンテナがマウントポイントを認識できないことを確認します。
次の docker-compose.yaml を使用します。
ホストの ./data がコンテナの /data に bind mount されます。
version: '3.8'
services:
srv:
container_name: cont
image: debian:bookworm
restart: always
volumes:
- ./data:/data
コンテナの中に入り、/data/mount_point を作成し、/data/mount_point/a.txt を作成します。
$ sudo docker compose run srv bash
[+] Creating 1/0
✔ Network bind_mount_test_default Created 0.1s
$ ls data
$ mkdir data/mount_point
$ ls data
mount_point
$ echo 'a' > data/mount_point/a.txt
ホストで、./data/a.txt が存在することを確認します。
$ ls data/mount_point
a.txt
$ cat data/mount_point/a.txt
a
確認作業に入ります。
ホストで、/dev/sdb1 を ./data/mount_point にマウントし、a.txt が見えないことを確認します。
$ sudo mount -t ext4 /dev/sdb1 data/mount_point
$ ls data
mount_point
$ ls data/mount_point/
lost+found
コンテナが新たな /data/mount_point を認識していれば、a.txt は存在しないはずです。
実際に確認すると、a.txt が存在します。
Bind mount だと、コンテナは新たなマウントポイントを認識してくれないことがわかりました。
$ ls data
mount_point
$ ls data/mount_point/
a.txt
Volume mount
同様の実験を volume mount で行います。
次の docker-compose yaml を使用します。
/data はボリューム data_vol に volume mount されます。
version: '3.8'
services:
srv:
container_name: cont
image: debian:bookworm
restart: always
volumes:
- data_vol:/data
volumes:
data_vol:
コンテナの中に入り、/data/mount_point を作成し、/data/mount_point/a.txt を作成します。
$ sudo docker compose run srv bash
[+] Creating 1/0
✔ Network bind_mount_test_default Created 0.1s
$ ls data
$ mkdir data/mount_point
$ ls data
mount_point
$ echo 'a' > data/mount_point/a.txt
ホストで、a.txt が存在することを確認します。
$ sudo ls /var/lib/docker/volumes/bind_mount_test_data_vol/_data/mount_point
a.txt
$ sudo cat /var/lib/docker/volumes/bind_mount_test_data_vol/_data/mount_point/a.txt
a
確認作業に入ります。
ホストで、/dev/sdb1 を /var/lib/docker/volumes/bind_mount_test_data_vol/_data/mount_point にマウントし、a.txt が見えないことを確認します。
$ sudo mount -t ext4 /dev/sdb1 /var/lib/docker/volumes/bind_mount_test_data_vol/_data/mount_point
$ sudo ls /var/lib/docker/volumes/bind_mount_test_data_vol/_data/mount_point
lost+found
コンテナが新たな /data/mount_point を認識していれば、a.txt は存在しません。
実際に確認すると、a.txt ではなく lost+found が表示されます。
Volume mount であれば、コンテナは新たなマウントポイントを認識してくれることがわかりました。
$ ls /data/mount_point
lost+found
おわりに
本記事では、Docker コンテナが起動中に、mount 先に新たなデバイスをマウントしたときの挙動が、bind mount と volume mount で異なることを確認しました。
最後に宣伝です。
Acompany では現在メンバを募集中です。
気になる方は以下のリンク先の採用情報をご覧ください。
No more OSError: [Errno 28] No space left on device!