概要
前回 docker stack を使って Swarm 環境に docker-compose で定義したコンテナやネットワーク、ボリュームをデプロイしました
その際にボリュームがコンテナホスト間で共有できない問題を取り上げました
解決策の一つとして nfs があるようなので今回は nfs を使ったボリューム共有の方法を紹介したいと思います
環境
- macOS 10.14.5
- Vagrant 2.1.1
- Ubuntu 16.04 LTS
- docker 18.09.7
- nfs-kernel-server 1.2.8
nfs サーバ構築
Vagrant で構築した Ubuntu 16.04 上に構築します
過去の記事で紹介しているのでそちらを参考にして構築してください
なお今回は動作確認用に nfs で exports したパスに root ユーザでファイルを 1 つ作成しています
date > /opt/nfs/hoge.txt
とりあえず docker で nfs ボリュームを触ってみる
docker-compose.yml を書く前にまずは docker コマンドだけで nfs ボリュームを試してみます
というかそもそも docker コマンドでの挙動を理解していないと docker-compose にしても理解不能になってしまうと思います
nfs ボリューム作成
volume create
で作成できます
nfs の場合 --opt
でいろいろと指定する必要があります
docker volume create --driver local --opt type=nfs --opt o=addr=172.28.128.3,rw,nfsvers=4 --opt device=:/opt/nfs nfs-volume
今回主に使用している --opt
は以下の通りです
--opt type=nfs
--opt o=addr=172.28.128.3,rw,nfsvers=4
--opt device=:/opt/nfs
1 つ目はタイプを指定します
docker 18.09.7 の場合、特にボリューム用のプラグインなどを別途インストールしなくても上記の形式で記載することで nfs が使用できます
2 つ目は nfs をマウントする際のオプションを指定します
これは nfs 自体のオプションとほぼ同じになります
詳しくはこの辺りを御覧ください
なお今回は「nfs サーバの IP アドレス」「Read/Write の許可」「nfs サーバのバージョン」を指定しています
3 つ目は nfs で export したパスを指定します
これで nfs ボリュームが作成されます
inspect した結果は以下の通りです
docker volume inspect nfs-volume
[
{
"CreatedAt": "2019-07-04T01:49:07Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/nfs-volume/_data",
"Name": "nfs-volume",
"Options": {
"device": ":/opt/nfs",
"o": "addr=172.28.128.3,rw,nfsvers=4",
"type": "nfs"
},
"Scope": "local"
}
]
コンテナでマウントしてみる
では作成した nfs ボリュームをマウントしてみましょう
docker コマンドでボリュームをマウントするには -v
(or --mount
) オプションを使います
今回は挙動を確認しやすい redis イメージを使っています
docker run -d -v nfs-volume:/opt/nfs --name redis redis
これでマウントできます
コンテナが起動したら df
コマンドでマウント状況を確認してみましょう
docker exec redis df -h
Filesystem Size Used Avail Use% Mounted on
overlay 9.7G 2.0G 7.7G 21% /
tmpfs 64M 0 64M 0% /dev
tmpfs 496M 0 496M 0% /sys/fs/cgroup
/dev/sda1 9.7G 2.0G 7.7G 21% /data
:/opt/nfs 9.7G 2.2G 7.6G 22% /opt/nfs
shm 64M 0 64M 0% /dev/shm
tmpfs 496M 0 496M 0% /proc/acpi
tmpfs 496M 0 496M 0% /proc/scsi
tmpfs 496M 0 496M 0% /sys/firmware
こんな感じで /opt/nfs
にマウントされていれば OK です
読込できるか確認してみます
docker exec redis cat /opt/nfs/hoge.txt
=> Thu Jul 4 01:36:43 UTC 2019
こんな感じで事前に作成したファイルの内容が表示されれば OK です
書き込みしてみる
次に書き込みしてみましょう
先程のコンテナ上で以下のコマンドを実行してみます
docker exec redis /bin/sh -c "date > /opt/nfs/hoge2.txt"
問題なく書き込めると思います
また nfs サーバ側にもデータあることを確認できると思います
権限に注意
過去の記事の手順で nfs サーバを構築した場合、基本的には root ユーザでのみ書き込みができるようになります
例えば vagrant 環境なので vagrant ユーザからの読み書きができるようにする場合は nfs サーバ側でマウントポイント (/opt/nfs) の所有者を以下のように変更する必要があります
chown -R vagrant:vagrant /opt
また exports する場合にも no_root_squash
を削除しなければいけません
vim /etc/exports
/opt/nfs 172.28.128.0/24(rw,no_subtree_check,async)
この設定をすると vagrant ユーザで読み書きできる nfs のパスが作成できるのですがもしこのパスをコンテナで使おうとすると読みはできますが書き込みはできません
なぜなら redis コンテナ上では root ユーザで読み書きが行われる上記の nfs サーバの設定だと権限がないためです
docker は基本的に root ユーザでコンテナは動作するため今回の nfs サーバの設定で基本的には大丈夫だと思いますがもしユーザを変える場合には注意しましょう
もし別のユーザで動作させる場合には nfs サーバで別のパスをそのユーザ専用に作成してあげると良いと思います
既存のボリュームにマウントはできないのか
redis コンテナの場合 /data
にダンプデータが出力されます
基本的にはこのパスをホスト側でマウントしたりしてデータの永続化を行います
なら /data
を nfs ボリュームでマウントできないか試してみました
docker run -d -v nfs-volume:/data --name redis redis
こんな感じでコンテナ側のマウントパスを /data
に変更してあげます
これで df を実行すると以下のようになります
docker exec redis df -h
Filesystem Size Used Avail Use% Mounted on
overlay 9.7G 2.0G 7.7G 21% /
tmpfs 64M 0 64M 0% /dev
tmpfs 496M 0 496M 0% /sys/fs/cgroup
172.28.128.3:/opt/nfs 9.7G 2.2G 7.6G 22% /data
/dev/sda1 9.7G 2.0G 7.7G 21% /etc/hosts
shm 64M 0 64M 0% /dev/shm
tmpfs 496M 0 496M 0% /proc/acpi
tmpfs 496M 0 496M 0% /proc/scsi
tmpfs 496M 0 496M 0% /sys/firmware
これでダンプファイルが出力されるかテストしてみましょう
docker exec redis redis-cli set a a
docker stop redis
nfs サーバ側を直接確認すると dump.rdb がちゃんと作成されていました
ls -l /opt/nfs/dump.rdb
-rw-r--r-- 1 999 docker 102 Jul 4 02:13 /opt/nfs/dump.rdb
が気になったのは 999:docker というユーザの権限で保存されていたことです
更に上位の階層を見ると nfs フォルダが 999 ユーザの所有権になっていました
ls -l /opt/
drwxr-xr-x 2 999 root 4096 Jul 4 02:13 nfs
詳しく調査していないので、おそらくですが dockerd が都合の良いように nfs 側のマウントパスの所有者を書き換えたんだと思います
一応コンテナの root ユーザからは問題なく読み書きできるので挙動的にバグることはないのですが少し気持ち悪い感じはします
なおこの後再度 redis コンテナを起動してデータを確認しましたがちゃんと前のデータが復元されていました
docker start redis
docker exec redis redis-cli get a
=> a
本題: docker-compose.yml で使ってみる
ではいよいよ docker-compose.yml で nfs ボリュームを使ってみます
vim docker-compose.yml
version: '3.4'
services:
redis:
image: redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
deploy:
placement:
constraints: [node.role == worker]
volumes:
redis_data:
driver_opts:
type: nfs
o: "addr=172.28.128.3,rw,nfsvers=4"
device: ":/opt/nfs"
docker コマンド時に最後にやった redis + nfs ボリュームを実現するための docker-compse.yml になります
これで stack deploy してみましょう
docker stack deploy -c docker-compose.yml test
docker stack ls
NAME SERVICES ORCHESTRATOR
test 1 Swarm
docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
yszxwqr2fjsn test_redis replicated 1/1 redis:latest *:6379->6379/tcp
こんな感じになれば OK です
redis-cli でデータを保存しておきます
redis-cli set a aa
なお今回は worker 側にコンテナを立ち上げているので worker 側のホストで exec してあげれば df などでマウント状況も確認できます (前述と同じマウント結果なので省略)
あとは stack を削除してデータが nfs 上に残っているかとそれを復元できるか再度確認してみましょう
docker stack rm test
docker stack deploy -c docker-compose.yml test
redis-cli get a
=> aa
こんな感じで復元できれば成功です
なお nfs サーバの /opt/nfs に dump.rdb があるのも確認できました
動作するコンテナを manager 側にした場合どうなるのか
ここで疑問に思ったのは nfs ボリュームと言えど結局ドライバは local ドライバなので作成されているボリュームは worker 側にしかありません
なので結局ボリュームをコンテナホスト間で共有できていないことになってしまいます
では nfs ボリュームを manager 側にも作成すれば結局見ている nfs サーバは同じなのでボリュームが違えどデータは同じなのではどうことで試してみました
docker-compose.yml の placement.constraints
を manager に変更します
vim docker-compose.yml
version: '3.4'
services:
redis:
image: redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
deploy:
placement:
constraints: [node.role == manager]
volumes:
redis_data:
driver_opts:
type: nfs
o: "addr=172.28.128.3,rw,nfsvers=4"
device: ":/opt/nfs"
これで再度 redis コンテナを立ち上げてみてデータが復元できるか確認したところちゃんと復元できていました
docker stack rm test
docker stack deploy -c docker-compose.yml test
redis-cli get a
=> aa
なおこの後で worker 側と manager 側で docker volume ls
をしてみると両方に nfs ボリュームが作成されているのが確認できると思います
docker volume ls
DRIVER VOLUME NAME
local test_redis_data
未検証: 気になったこと
- nfs サーバ側の
/etc/exports
などで設定を変更した場合に docker の nfs ボリュームは作成し直さないとダメなのか
最後に
docker Swarm + docker-compose でボリュームがコンテナホスト間で共有できない問題を nfs サーバを構築し各コンテナホストに nfs ボリュームを作成することで解決してみました
おそらくこれが Swarm を使ってボリューム共有する最善手なのではないかと思います
nfs サーバは今回は VM 上に直接インストールしましたがコンテナでもいいしクラウド上に作成しても良いかなと思います
ただレイテンシーが少なからず発生するのでできればコンテナの近くに置いてあげたほうがコンテナ自体のパフォーマンスも良くなると思います
0 件のコメント:
コメントを投稿