Dockerのデータ管理（ボリュームマウントについて）

#Dockerにおける4つのデータ管理方法

##コンテナ内の読み書き可能レイヤーに置く

これは最も自然な考え方ですが、コンテナ中の読み書き可能なレイヤーにデータを置くケースです。
この場合、コンテナが削除された時点でそのデータは消えてしまいます。

また、ホスト内で書き込む速度と比較した場合、コンテナ内の書き込みのパフォーマンスは良くありません。
これは、ユニオンファイルシステム（UnionFS）の影響のようです。
※DockerはAUFS、btrfs、vfs、DeviceMapper などの複数のユニオンファイルシステム派生のファイルシステムを利用

##ボリュームマウント

Docker Engine上の領域にボリュームを作成し、Dockerホストにマウントします。
Docker Engine上の領域とは、Dockerの管理領域であるディレクトリを指します。

Docker Engine上の領域というと難しいですが、ふつうにホスト上の特定のディレクトリです。
dockerに権限があるフォルダと思っておくと良いと思います。

今回の環境ならVirtualBoxがDockerホストで、VirtualBoxはMacのリソースを使用しているので、結局Macのストレージです。

##バインドマウント

マウントと聞いて、通常想像されるのはコチラだと思います。

ホスト上の特定の領域をマウントし、コンテナ上で扱うことが出来ます。
ホストのディレクトリを編集すると、コンテナにそれが反映されるのでとても便利です。
当然コンテナが消えてもディレクトリは消えないので、コンテナだけ差し替えることも可能です。

尚、マウントする先に元々あったファイルがあった場合、それらは見えなくなります。
マウントを解除すると出てきますので、消えている訳ではないらしいです。

##tempfs（メモリ領域のマウント）

メモリ領域をマウントします。オプションで容量を指定することも可能です。
メモリはストレージと違いデータを保管する場所ではありません。
ワンタイムパスワードやキャッシュなど、永続的に保管しない類のデータの置き場所として使えます。

今回の主題はボリュームマウントです。
さっそく見ていきましょう。

#環境

- MBP
- catalina
- DockerForMac
- VirtualBox

#VirtualBox上にDocker machineを作成する

--driver で指定しなければ、デフォルトでVirtualBox上に作成されます。

```
% docker-machine create default
Running pre-create checks...
Creating machine...
(default) Copying /Users/hogehoge/.docker/machine/cache/boot2docker.iso to /Users/hogehoge/.docker/machine/machines/default/boot2docker.iso...
(default) Creating VirtualBox VM...
(default) Creating SSH key...
(default) Starting the VM...
(default) Check network to re-create if needed...
(default) Waiting for an IP...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with boot2docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env default
% 
```

##ボリュームの作成

vol1がボリューム名です。これは任意のもので構いません。

```
% docker volume create vol1
vol1
```

##作成したボリュームの確認

```
% docker volume ls
DRIVER    VOLUME NAME
local     vol1
```

##作成したボリュームの詳細確認

Dockerマシン上の「/mnt/sda1/var/lib/docker/volumes/vol1/_data」にボリュームが作成された事が確認出来ます。
尚、docker for macにおいて、dokcerマシン上で動作しているOSの正体は[TIPS](https://qiita.com/siba4848/items/42d806831654f4b429a8#tips)を御覧ください。

```
% docker volume inspect vol1
[
    {
        "CreatedAt": "2021-03-13T03:28:13Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/mnt/sda1/var/lib/docker/volumes/vol1/_data",
        "Name": "vol1",
        "Options": {},
        "Scope": "local"
    }
]
```

##Docker machineにssh接続

```
% docker-machine ssh default
   ( '>')
  /) TC (\   Core is distributed with ABSOLUTELY NO WARRANTY.
 (/-_--_-\)           www.tinycorelinux.net

```

##作成されたフォルダの存在を確認する

```
$ sudo ls -la /mnt/sda1/var/lib/docker/volumes/vol1/                                                             
total 12
drwxr-xr-x    3 root     root          4096 Mar 13 03:28 .
drwx------    3 root     root          4096 Mar 13 03:28 ..
drwxr-xr-x    2 root     root          4096 Mar 13 03:28 _data
```

##ボリュームを削除する

```
$ docker volume rm vol1
vol1
```

なくなっている

```
$ docker volume ls                                                                                               
DRIVER              VOLUME NAME
```

次は実際にマウントしてみる。

##ボリュームの再作成とnginxコンテナへのマウント

-dit オプションでバックグラウンドでnginxコンテナを起動する。
--name はコンテナにつける名前である。
-v でボリューム名を指定し、「:」の後にコンテナのマウント先を指定する。（現在は --mounts の使用が推奨されている）
最後の「nginx」はDockerHubからpullするDockerイメージ名である。

```
$ docker run -dit --name nginx.app -v vol1:/app nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
6f28985ad184: Pull complete 
29f7ebf60efd: Pull complete 
879a7c160ac6: Pull complete 
de58cd48a671: Pull complete 
be704f37b5f4: Pull complete 
158aac73782c: Pull complete 
Digest: sha256:d2925188effb4ddca9f14f162d6fba9b5fab232028aa07ae5c1dab764dca8f9f
Status: Downloaded newer image for nginx:latest
55e4e5ede92219fd363da3d01b48977a1d8ce458fd22333bcc75287772d0dad1
```

再作成された事が確認出来る

```
$ docker volume ls                                                                                               
DRIVER              VOLUME NAME
local               vol1
```

##実際にマウントされたか確認する

docker inspect コマンドを使うとMountsの欄からマウント情報が確認出来る。

```
$ docker inspect nginx.app                                                                                       
[
    {
        "Id": "55e4e5ede92219fd363da3d01b48977a1d8ce458fd22333bcc75287772d0dad1",
        "Created": "2021-03-13T03:36:55.10128817Z",
        "Path": "/docker-entrypoint.sh",
        "Args": [
            "nginx",
            "-g",
            "daemon off;"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 3027,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2021-03-13T03:36:55.242174584Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:6084105296a952523c36eea261af38885f41e9d1d0001b4916fa426e45377ffe",
        "ResolvConfPath": "/mnt/sda1/var/lib/docker/containers/55e4e5ede92219fd363da3d01b48977a1d8ce458fd22333bcc75287772d0dad1/resolv.conf",
        "HostnamePath": "/mnt/sda1/var/lib/docker/containers/55e4e5ede92219fd363da3d01b48977a1d8ce458fd22333bcc75287772d0dad1/hostname",
        "HostsPath": "/mnt/sda1/var/lib/docker/containers/55e4e5ede92219fd363da3d01b48977a1d8ce458fd22333bcc75287772d0dad1/hosts",
        "LogPath": "/mnt/sda1/var/lib/docker/containers/55e4e5ede92219fd363da3d01b48977a1d8ce458fd22333bcc75287772d0dad1/55e4e5ede92219fd363da3d01b48977a1d8ce458fd22333bcc75287772d0dad1-json.log",
        "Name": "/nginx.app",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": [
                "vol1:/app"
            ],
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Capabilities": null,
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/mnt/sda1/var/lib/docker/overlay2/f1f8df6a254a9b42220524eabd6883087c9da3b82b715d9fde18b39983ba96d9-init/diff:/mnt/sda1/var/lib/docker/overlay2/0b056390f6c4f9a7900cd9282772e9a940bfc4fc50efe77484300c8657e3d1e7/diff:/mnt/sda1/var/lib/docker/overlay2/0b708c03647f7327ce2235601630eb8a106bdd51ae61122707db35e14b02782d/diff:/mnt/sda1/var/lib/docker/overlay2/d6317e1cde679e3dcda0d17aae7a66899196ae6a2100d8c29f5bcca25d8456b8/diff:/mnt/sda1/var/lib/docker/overlay2/4a850e33565beb966bd441890200a7b655510a192a4ff760a833fb44c00a03fd/diff:/mnt/sda1/var/lib/docker/overlay2/9b5500beda76c7aa12001d291737214227f9429a59cd166127db0e640348b2cb/diff:/mnt/sda1/var/lib/docker/overlay2/907a25cc97e4b5bbf6bb16f0b58b9ada6d982808439d41b3b47eb7def574f65f/diff",
                "MergedDir": "/mnt/sda1/var/lib/docker/overlay2/f1f8df6a254a9b42220524eabd6883087c9da3b82b715d9fde18b39983ba96d9/merged",
                "UpperDir": "/mnt/sda1/var/lib/docker/overlay2/f1f8df6a254a9b42220524eabd6883087c9da3b82b715d9fde18b39983ba96d9/diff",
                "WorkDir": "/mnt/sda1/var/lib/docker/overlay2/f1f8df6a254a9b42220524eabd6883087c9da3b82b715d9fde18b39983ba96d9/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [
            {
                "Type": "volume",
                "Name": "vol1",
                "Source": "/mnt/sda1/var/lib/docker/volumes/vol1/_data",
                "Destination": "/app",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
        ],
        "Config": {
            "Hostname": "55e4e5ede922",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "80/tcp": {}
            },
            "Tty": true,
            "OpenStdin": true,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "NGINX_VERSION=1.19.8",
                "NJS_VERSION=0.5.2",
                "PKG_RELEASE=1~buster"
            ],
            "Cmd": [
                "nginx",
                "-g",
                "daemon off;"
            ],
            "Image": "nginx",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": [
                "/docker-entrypoint.sh"
            ],
            "OnBuild": null,
            "Labels": {
                "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
            },
            "StopSignal": "SIGQUIT"
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "4a5bbe2760898b2439f551ab0c922a876864648f535191b1844160353a9311f1",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {
                "80/tcp": null
            },
            "SandboxKey": "/var/run/docker/netns/4a5bbe276089",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "c7db99fed287c7da8b43cbba77203f90f52f7ad7083b04d4403eddd241eb345a",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "e3c91adec95ab425483b25bf84a17fbc732f0924b68d35f9745e2c82a565571b",
                    "EndpointID": "c7db99fed287c7da8b43cbba77203f90f52f7ad7083b04d4403eddd241eb345a",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]
```

長っ

##コンテナに接続する

docker execでコンテナに接続する。
-it を指定して、コンテナにシェルのコマンドを渡さないと、コンテナ側でシェルが起動しないので、操作出来ないので注意して下さい。
シェルは省略するとエラーが出ます。

Bashじゃなきゃいけない訳じゃないと思いますが、Bashは比較的どのイメージにも予めインストールされているようです。
Bash以外のシェルはコンテナに入ったときに確認してみましょう、[TIPS](https://qiita.com/siba4848/items/42d806831654f4b429a8#tips)に記載します。

```
$ docker exec -it nginx.app /bin/bash 
```

##作成されたボリュームを確認する

「/dev/sda1       18714000 190076  17534796   2% /app」があることが確認できます。

```
# df         
Filesystem     1K-blocks   Used Available Use% Mounted on
overlay         18714000 190076  17534796   2% /
tmpfs              65536      0     65536   0% /dev
tmpfs             504516      0    504516   0% /sys/fs/cgroup
shm                65536      0     65536   0% /dev/shm
/dev/sda1       18714000 190076  17534796   2% /app
tmpfs             504516      0    504516   0% /proc/asound
tmpfs             504516      0    504516   0% /proc/acpi
tmpfs             504516      0    504516   0% /proc/scsi
tmpfs             504516      0    504516   0% /sys/firmware
```

#作成されたボリューム内に適当なファイルを作成する

「touch hogehoge」でhogehogeファイルを作成しました。

```
root@55e4e5ede922:/# cd /app
root@55e4e5ede922:/app# touch hogehoge
root@55e4e5ede922:/app# ls
hogehoge
```

先程docker inspectで確認したDockerホスト上から確認したマウントポイントのパスを指定してみるとボリューム上にファイルが作成されていることが確認出来ます。

```
$ sudo ls /var/lib/docker/volumes/vol1/_data                                                       
hogehoge
```

#別のコンテナに同じボリュームをマウントしてみる

今回は推奨されている --mounts オプションでボリュームマウントを行います。
「source=ボリューム名」カンマで区切って「target=コンテナ内のマウント先」という書式です。

起動しているのはApacheです。タグ名は省略したので「latest」がダウンロードされます。
ボリュームの検証が目的なので、特にポートマッピングのオプションは指定しません。

```
$ docker run -dit --name apache.app --mount source=vol1,target=/app httpd                                        
Unable to find image 'httpd:latest' locally
latest: Pulling from library/httpd
6f28985ad184: Already exists 
3a141a09d1d0: Pull complete 
1633384edb75: Pull complete 
acb3e3b931b8: Pull complete 
f6dc6b8b1d70: Pull complete 
Digest: sha256:9625118824bc2514d4301b387c091fe802dd9e08da7dd9f44d93ee65497e7c1c
Status: Downloaded newer image for httpd:latest
b199265a5aa892e518fe2a1b135f882893fa8a7e74af68f35cafb79179a7105e
```

##コンテナに接続する

```
$ docker exec -it apache.app /bin/bash                                                                           
```

##マウント情報を確認する

```
root@b199265a5aa8:/usr/local/apache2# df
Filesystem     1K-blocks   Used Available Use% Mounted on
overlay         18714000 259896  17464976   2% /
tmpfs              65536      0     65536   0% /dev
tmpfs             504516      0    504516   0% /sys/fs/cgroup
shm                65536      0     65536   0% /dev/shm
/dev/sda1       18714000 259896  17464976   2% /app
tmpfs             504516      0    504516   0% /proc/asound
tmpfs             504516      0    504516   0% /proc/acpi
tmpfs             504516      0    504516   0% /proc/scsi
tmpfs             504516      0    504516   0% /sys/firmware
```

##hogehogeファイルがあることを確認する

```
root@b199265a5aa8:/usr/local/apache2# ls -la /app
total 8
drwxr-xr-x 2 root root 4096 Mar 13 03:50 .
drwxr-xr-x 1 root root 4096 Mar 13 03:53 ..
-rw-r--r-- 1 root root    0 Mar 13 03:50 hogehoge

root@b199265a5aa8:/usr/local/apache2# 
```

#TIPS
##ApacheのDockerイメージのOS

Apacheのベースイメージはdebianらしい。
使ったことなかったのに知らぬ間に使っていた。

```
# cat /etc/os-release 
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
```

##nginxのDockerイメージのOS

同じくベースイメージはdebianらしい。

```
$ docker exec -it nginx.app /bin/bash     
                                                                       
root@55e4e5ede922:/# cat /etc/os-release 
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
```

##DockerForMac上で動作しているLinuxOSの正体

boot2dockerというLinuxディストリビューションが使用されています。
TinyCoreLinuxという軽量なOSがベースとなっています。

```
$ cat /etc/os-release                                                                                            
NAME=Boot2Docker
VERSION=19.03.12
ID=boot2docker
ID_LIKE=tcl
VERSION_ID=19.03.12
PRETTY_NAME="Boot2Docker 19.03.12 (TCL 10.1)"
ANSI_COLOR="1;34"
HOME_URL="https://github.com/boot2docker/boot2docker"
SUPPORT_URL="https://blog.docker.com/2016/11/introducing-docker-community-directory-docker-community-slack/"
BUG_REPORT_URL="https://github.com/boot2docker/boot2docker/issues"
```

##Apacheのイメージにインストールされているシェルの確認

```
$ docker exec -it apache.app /bin/bash                                                                           
root@b199265a5aa8:/usr/local/apache2# cat /etc/shells 
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/bin/rbash
/bin/dash
```