Docker マウント(データの永続化)
お久しぶりです。H.Uでございます。
今回は現在勉強中であるDockerの機能の一部をご紹介したいと思います。
データの永続化について
まず導入として、データの永続化についてご説明します。
コンテナ内に生成されるファイルは、コンテナ本体が削除されるとデータ保持ができなくなります。
これは、ライフサイクルを一番に考えているコンテナ環境には不都合が生じます。
その為、Dockerコンテナ内ファイルをホストマシン上に保存することが必要になります。
保存方法は2つあり、バインドマウントとボリュームがあります。
この2つを利用することで、コンテナ削除後にデータを維持することができます。
これをデータの永続化と呼びます。
バインドマウントについて
バインドマウントは、ホストマシン上のファイルまたはディレクトリをコンテナにマウントする方法です。
コンテナ外部にあるファイルを読み書きが可能で、ホスト上のどこにでもマウントして保存できます。
機能はボリュームと比較して限定的である為、公式はボリュームを推奨していることが多いようです。
ボリュームについて
ボリュームは、ホストマシン内にあるDockerが管理する領域をコンテナ上にマウントする方法です。
バインドマウントとは対照的にDockerが管理する領域内のみにマウントすることができ、データは完全にDockerにより管理されます。
多くの機能がバインドマウントより優れています。
今回は紹介だけですが、複数のコンテナ間でのデータ共有、バックアップまたは移行が容易など様々な機能が用意されています。
バインドマウント
それでは実際にバインドマウントから確認してみます。
簡単なDockerfileを作成し、イメージビルドを実施します。
[root@hu-test ~]# cat Dockerfile
#CentOS7 base image
FROM centos:7
#yum install epel-release
RUN yum -y install epel-release
#yum install nginx
RUN yum -y install nginx
#local file copy index.html
COPY index.html /usr/share/nginx/html
#entrypoint command
ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"]
上記のDockerfileには、バインドマウントやボリュームを確認する為、
nginxのインストールと起動、ホスト内のindex.html をコピーするなどの設定が入っています。
[root@hu-test ~]# docker image build -t hunetassist/exsample-nginx .
Sending build context to Docker daemon 35.84kB
Step 1/5 : FROM centos:7
---> eeb6ee3f44bd
Step 2/5 : RUN yum -y install epel-release
---> Running in 096e65a1df7f
Loaded plugins: fastestmirror, ovl
略)
Complete!
[root@hu-test ~]# docker image ls hunetassist/exsample-nginx
REPOSITORY TAG IMAGE ID CREATED SIZE
hunetassist/exsample-nginx latest 1a9b6ef77a7f 12 seconds ago 641MB
イメージ作成ができたので、コンテナを立ち上げていきます。
この際に、バインドするファイルまたはディレクトリを指定することでバインドマウントが可能となります。
(バインドはホストマシン内のどこにでも作成することが可能)
場所は問わないので、下記の様に /root/test/ にindex.htmlファイルを作成します。
[root@hu-test~]# cat test/index.html
<!DOCTYPE html>
<html>
<head>
<title>Docker Bind mount</title>
</head>
<body>
<h1>mount html on the Container Host</h1>
</body>
index.htmlファイルをDocker内にバインドして、nginxによるページ公開を実施していきます。
現在は ssh 接続用の22ポートしか開放しておらず、当然ページ表示はまだできません。
[root@hu-test ~]# netstat -tlnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 723/sshd
tcp6 0 0 :::22 :::* LISTEN 723/sshd
[root@hu-test ~]# curl http://localhost:8080/index.html
curl: (7) Failed to connect to localhost port 8080: 接続を拒否されました
ここからバインドを利用してコンテナを立ち上げると、どのようになるかを確認してみます。
[root@hu-test ~]# docker container run --name bind-nginx -d -p 8080:80 --mount type=bind,source=/root/test,target=/usr/share/nginx/html hunetassist/exsample-nginx
8dc0fc628018a0dccb21bfb70e62f4840e82268fc403149300266389962debb3
[root@hu-test ~]# docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8dc0fc628018 hunetassist/exsample-nginx "/usr/sbin/nginx -g …" 23 seconds ago Up 22 seconds 0.0.0.0:8080->80/tcp, :::8080->80/tcp bind-nginx
コマンドが長くなったので、内容を簡単に解説します。
● docker container run –name bind-nginx -d -p 8080:80
ここまでは ホストの8080ポートを開放し、ホストに対して80ポートと接続します。
また、–nameにより、コンテナの名前を指定します。
● –mount type=bind,source=/root/test,target=/usr/share/nginx/html
ここからがbindを利用する為の構文です。
–mountはバインドマウントまたはボリュームを使用するオプションであり、
“type=” によりbindを指定します。
“source= ” がホスト上のマウント元を選択し、
“target= ” により、コンテナ内のマウントする場所を指定できます。
これで、コンテナが立ち上がりました。
それでは同じようにcurlコマンドでページを確認します。
[root@hu-test ~]# netstat -tlnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 58884/docker-proxy
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 723/sshd
tcp6 0 0 :::8080 :::* LISTEN 58890/docker-proxy
tcp6 0 0 :::22 :::* LISTEN 723/sshd
[root@hu-test ~]# curl http://localhost:8080/index.html
<!DOCTYPE html>
<html>
<head>
<title>Docker Bind mount</title>
</head>
<body>
<h1>mount html on the Container Host</h1>
</body>
nginxはコンテナ内にのみ存在していますが、
ホスト上にある /root/test/index.html curl でページの確認できました。
これは、コンテナのディレクトリがホストマシンにバインドマウントされているからです。
コンテナ内ではどのようになっているか、内部に入って確認してみます。
[root@hu-test ~]# docker container exec -it bind-nginx /bin/sh
sh-4.2# cat /usr/share/nginx/html/index.html
<!DOCTYPE html>
<html>
<head>
<title>Docker Bind mount</title>
</head>
<body>
<h1>mount html on the Container Host</h1>
</body>
しっかりとコンテナ内部にバインドされていることが確認できました。
また今回は実施しませんが、バインドマウントとボリュームの両方でホスト側またはコンテナ側で修正したファイルの内容は一方にも同期されています。
最後にコンテナを削除し、バインドしたファイルがホスト内に残っているかを確認してみます。
つまり、データの永続化ができているかの確認です。
[root@hu-test ~]# docker container stop bind-nginx
bind-nginx
[root@hu-test ~]# docker container rm bind-nginx
bind-nginx
[root@hu-test ~]# ll /root/test/index.html
-rw-r--r-- 1 root root 128 9月 8 07:20 /root/test/index.html
当然ですが、ホスト内ではバインドしたファイルは削除されていないことが確認できます。
これで、データの永続化は達成されました。
ここまではバインドマウントの紹介でした。
次はいよいよボリュームについてです。
ボリューム
Dockerが管理しているデータ領域はデフォルトでは /var/lib/docker です。
ボリュームはこの領域にのみデータを共有することができます。
まずは /var/lib/docker/volumes/ ボリューム専用領域にボリューム”test”を作成して、
バインドと同様にこの領域をマウントして、ページを表示を確認することを目標にします。
[root@hu-test ~]# docker volume create test
test
[root@hu-test ~]# docker volume ls
DRIVER VOLUME NAME
local test
root@hu-test ~]# ll -AR /var/lib/docker/volumes/test/
/var/lib/docker/volumes/test/:
合計 4
drwxr-xr-x 2 root root 4096 9月 6 02:03 _data
/var/lib/docker/volumes/test/_data:
合計 0
確認用ボリューム”test”を専用領域に作成しました。
バインドと同じイメージを用いてコンテナを立ち上げます。
type=volume,とsource=test に変更するだけで、コマンドはほとんど変わりません。
sourceはバインドマウントはホスト上のフルパスでしたが、ボリュームは専用領域を用いる為、
ボリューム名のみで指定できます。
[root@hu-test ~]# docker container run --name volume-nginx -d -p 8080:80 --mount type=volume,source=test,target=/usr/share/nginx/html hunetassist/exsample-nginx
59c0a01a7d3146bed61fdf0bf52480e717848ca4f87d55c286b0a896f7d34ffb
これで、専用領域にある”test”ボリュームはDockerが全て管理するデータ領域になりました。
この領域に volume.html を作成します。
[root@hu-test ~]# vi /var/lib/docker/volumes/test/_data/volume.html
[root@hu-test ~]# cat /var/lib/docker/volumes/test/_data/volume.html
<!DOCTYPE html>
<html>
<head>
<title>Docker Volume</title>
</head>
<body>
<h1>Volume test file</h1>
</body>
</html>
volume.html 以外にも既にDocker内にあったファイルなどがすでに”test”領域に生成されていることが確認できます。
[root@hu-test ~]# ll /var/lib/docker/volumes/test/_data/
合計 24
-rw-r--r-- 1 root root 3650 10月 19 2021 404.html
-rw-r--r-- 1 root root 3693 10月 19 2021 50x.html
lrwxrwxrwx 1 root root 20 9月 2 08:33 en-US -> ../../doc/HTML/en-US
drwxr-xr-x 2 root root 4096 9月 8 08:19 icons
lrwxrwxrwx 1 root root 18 9月 2 08:33 img -> ../../doc/HTML/img
-rw-r--r-- 1 root root 113 9月 2 08:26 index.html
-rw-r--r-- 1 root root 368 10月 19 2021 nginx-logo.png
lrwxrwxrwx 1 root root 14 9月 2 08:33 poweredby.png -> nginx-logo.png
-rw-r--r-- 1 root root 116 9月 6 02:49 volume.html
バインドマウント時と同様にページを確認を行います。
[root@hu-te ~]# netstat -tlnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 64243/docker-proxy
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 723/sshd
tcp6 0 0 :::8080 :::* LISTEN 64248/docker-proxy
tcp6 0 0 :::22 :::* LISTEN 723/sshd
[root@hu-te ~]# curl http://localhost:8080/volume.html
<!DOCTYPE html>
<html>
<head>
<title>Docker Volume</title>
</head>
<body>
<h1>Volume test file</h1>
</body>
</html>
ページ表示は確認できました。
最後にデータの永続化ができているかを確認の為に、コンテナを削除します。
[root@hu-test ~]# docker container stop volume-nginx
volume-nginx
[root@hu-test ~]# docker container rm volume-nginx
volume-nginx
[root@hu-test ~]# docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@hu-test ~]# curl http://localhost:8080/volume.html
curl: (7) Failed to connect to localhost port 8080: 接続を拒否されました
これで、コンテナは消えていることが確認できました。
Docker管理領域とvolume”test”はどのようになっているでしょうか。
[root@hu-test ~]# ll /var/lib/docker/volumes/test/_data/
合計 24
-rw-r--r-- 1 root root 3650 10月 19 2021 404.html
-rw-r--r-- 1 root root 3693 10月 19 2021 50x.html
lrwxrwxrwx 1 root root 20 9月 2 08:33 en-US -> ../../doc/HTML/en-US
drwxr-xr-x 2 root root 4096 9月 8 08:19 icons
lrwxrwxrwx 1 root root 18 9月 2 08:33 img -> ../../doc/HTML/img
-rw-r--r-- 1 root root 113 9月 2 08:26 index.html
-rw-r--r-- 1 root root 368 10月 19 2021 nginx-logo.png
lrwxrwxrwx 1 root root 14 9月 2 08:33 poweredby.png -> nginx-logo.png
-rw-r--r-- 1 root root 116 9月 6 02:49 volume.html
[root@hu-test ~]# docker volume ls
DRIVER VOLUME NAME
local test
volume “test”は消えず、volume.html も残った状態です。
コンテナが利用しているファイルやディレクトリはコンテナが消えても、
マウントバインドまたはボリュームを利用することでデータの永続化ができることを確認できました。
今回はバインドマウントとボリュームだけの説明でしたが、次回もDockerについてを解説できたらと考えています。
機会がございましたら、紹介ができなかったボリュームがバインドマウントより優れている機能、複数コンテナでのデータ共有、バックアップや移行の容易性などについてを記事にしていきたいと考えています。
今回はここまで、H.Uでした。
最後まで読んでいただき、ありがとうございます。
今後ともネットアシストをよろしくお願いいたします。