こんにちは。takapy(@takapy0210)です。
本記事は、転職カウントダウンカレンダー 5日目の記事です。
はじめに
本記事は下記記事の続編です。
本記事では、Dockerのネットワーク、Dockerのデータ管理などについてまとめてみようと思います。
Dockerのネットワーク
準備として、docker-machineでDockerホストを1台起動させておきます。
$ docker-machine create nw-vm1 $ docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS default - virtualbox Running tcp://192.168.99.100:2376 v18.09.2 nw-vm1 - virtualbox Running tcp://192.168.99.101:2376 v18.09.2
sshでdocker-machineにログイン
$ docker-machine ssh nw-vm1
Dockerのネットワーク一覧はdocker network
コマンドで確認できます。デフォルトでは下記3つのネットワークが構成されています。
docker@nw-vm1:~$ docker network ls NETWORK ID NAME DRIVER SCOPE dffd451dfe8d bridge bridge local f3c0a422255a host host local 70a8f2a8b23f none null local
ブリッジネットワーク
単一のDockerホスト内で構築されるネットワークです。比較的小規模なネットワークを構成する際によく用いられます。また、コンテナ起動時にデフォルトで設定されるのもこのブリッジネットワークです。
ブリッジネットワークの詳細情報を確認してみます。
docker@nw-vm1:~$ docker network inspect bridge ~~~ "Config": [ { "Subnet": "172.17.0.0/16", "Gateway": "172.17.0.1" } ] ~~~
上記から、コンテナに割り当てられているIP帯域は172.17.0.0/16
だということが分かります。ブリッジネットワークに接続されたコンテナは、このIP帯が割り当てられます。
そして今回のnw-vm1
にはデフォルトゲートウェイとして172.17.0.1
が割り当てられていることが分かります。
試しにalpineLinuxのコンテナを2つ起動し、そこからpingで通信できるか確認してみます。
$ docker@nw-vm1:~$ docker run -itd --name alpine1 alpine /bin/sh
これで、alpine1のコンテナがブリッジネットワークに接続されます。
docker@nw-vm1:~$ docker inspect bridge ~~~ "Containers": { "8b9d6310738547cce85c80eaf4b70a7b43babba617c78d006b4154d725b6823e": { "Name": "alpine1", "EndpointID": "0e8f3e55fad7b344145b8f4cf0df8fc9092ffbe630eae28b97d4ad7978eaf18d", "MacAddress": "02:42:ac:11:00:02", "IPv4Address": "172.17.0.2/16", "IPv6Address": "" } } ~~~
このalpineコンテナには172.17.0.2/16
のIPアドレスが割り当てられてることが分かります。
2つ目のコンテナを起動します。
docker@nw-vm1:~$ docker run -itd --name alpine2 alpine /bin/sh docker@nw-vm1:~$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c34bd9a3f866 alpine "/bin/sh" 8 seconds ago Up 8 seconds alpine2 8b9d63107385 alpine "/bin/sh" 4 minutes ago Up 4 minutes alpine1
コンテナにattachして、alpine2→alpine1にpingを飛ばしてみます。
$ docker attach alpine2 # ping -w 3 172.17.0.2 PING 172.17.0.2 (172.17.0.2): 56 data bytes 64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.088 ms 64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.068 ms 64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.068 ms --- 172.17.0.2 ping statistics --- 4 packets transmitted, 3 packets received, 25% packet loss round-trip min/avg/max = 0.068/0.074/0.088 ms
alpine1と通信できていることが確認できました。
今度はコンテナ名でpingを飛ばしてみます。
# ping -w 3 alpine1 ping: bad address 'alpine1'
上記のように、DNSが存在しないため名前解決はできません。名前解決させたい場合に必要なのがユーザ定義のブリッジネットワークです。
ここで「Ctrl+P → Ctrl+Q」でコンテナを停止させることなくシェルを抜けて、Dockerホストに切り替えておきましょう。
ユーザ定義のブリッジネットワークを作成する
docker network create ネットワーク名
で作成できます。
docker@nw-vm1:~$ docker network create my_nw docker@nw-vm1:~$ docker network ls NETWORK ID NAME DRIVER SCOPE e4e91679ca88 bridge bridge local c831bb29ba5c host host local 419285d60fbd my_nw bridge local 3feb4208d73d none null local
ここで作成したブリッジネットワークに、alpine1とalpine2のコンテナを接続してみます。
docker@nw-vm1:~$ docker network connect my_nw alpine1 docker@nw-vm1:~$ docker network connect my_nw alpine2
上記以外にも、例えば最初からmy_nw
に接続した状態でコンテナを起動することもできます。--network ネットワーク
オプションを追加します。
docker@nw-vm1:~$ docker run -itd --name alpine3 --network my_nw alpine
my_nwにどのようなコンテナが紐づいているか確認してみます。
docker@nw-vm1:~$ docker inspect my_nw ~~~ "Containers": { "8b9d6310738547cce85c80eaf4b70a7b43babba617c78d006b4154d725b6823e": { "Name": "alpine1", "EndpointID": "bf038e6873981d3ab76e25ba3e4d829dfa5b1efece4b55c12b5c6993d99b725a", "MacAddress": "02:42:ac:12:00:02", "IPv4Address": "172.18.0.2/16", "IPv6Address": "" }, "a6d5894c8c3e88ab826eb11350106c998950c9df4059efd3e01175e42bc74b86": { "Name": "alpine3", "EndpointID": "1f70f8959ce2a101cbf7146d340ad1b07ed1b5b7e700765c9bfca9288d65bc0d", "MacAddress": "02:42:ac:12:00:04", "IPv4Address": "172.18.0.4/16", "IPv6Address": "" }, "c34bd9a3f8663c3e757a7c25b17ee2c1719aeea294ddad64af80b7d0518417b4": { "Name": "alpine2", "EndpointID": "37f1fa549a99258533a55febdd9eb9a8272a31769be7bd268aaebaae4b0f1063", "MacAddress": "02:42:ac:12:00:03", "IPv4Address": "172.18.0.3/16", "IPv6Address": "" } } ~~~
作成したalpine1〜alpine3のコンテナが接続されていることが確認できます。
ではコンテナ名で通信できるか確認してみます。
docker@nw-vm1:~$ docker attach alpine2 # ping -w 3 alpine1 PING alpine1 (172.18.0.2): 56 data bytes 64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.051 ms 64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.077 ms 64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.069 ms --- alpine1 ping statistics --- 4 packets transmitted, 3 packets received, 25% packet loss round-trip min/avg/max = 0.051/0.065/0.077 ms
無事できました!!
最後にコンテナをネットワークから切断してみます。現在alpine2
が接続されているネットワークはbridge
とmy_nw
の2つです。
docker@nw-vm1:~$ docker inspect alpine2 ~~~ "Networks": { "bridge": { "IPAMConfig": null, "Links": null, "Aliases": null, "NetworkID": "e4e91679ca8810e1766c4d7930e9f51c8ef7e7473ecb57247fee13cc121611f1", "EndpointID": "141e582499c24de99801ce01978a0ebd2bf182c1d9cda8a44f366ff654719d83", "Gateway": "172.17.0.1", "IPAddress": "172.17.0.3", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:11:00:03", "DriverOpts": null }, "my_nw": { "IPAMConfig": {}, "Links": null, "Aliases": [ "c34bd9a3f866" ], "NetworkID": "419285d60fbdb93c41e20cc5deb0db8fad04ff496a924c914d00b88978a5b726", "EndpointID": "37f1fa549a99258533a55febdd9eb9a8272a31769be7bd268aaebaae4b0f1063", "Gateway": "172.18.0.1", "IPAddress": "172.18.0.3", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:12:00:03", "DriverOpts": null } ~~~
ここからbridge
ネットワークを切断させるには、disconnect
を使用します。
docker@nw-vm1:~$ docker network disconnect bridge alpine2
Dockerのデータ管理
コンテナで扱う動的なデータは、起動中のコンテナの読み書き可能なレイヤーに置くこともできますが、下記のようなデメリットがあります。
- コンテナ間でデータ共有できない
- 書き込みのパフォーマンスがよくない
- コンテナが破棄されたタイミングでデータも破棄されてしまう。
そこでコンテナでは、下記のようなホスト上のディレクトリをマウントしてデータ管理する手法が一般的となっています。
volumeを使用したデータ管理
volumeを利用したマウントは、同一のディレクトリやファイルを複数コンテナにマウントすることができたり、コンテナが破棄されてもデータが破棄されることはありません。(明示的に破棄することもできます)
volumeを使用するとホスト上のDockerエリアにディレクトリが生成されますが、ホスト上でこれらのファイルを編集したり移動したりすることは推奨されておらず、あくまでコンテナ上でファイルを管理するための機能して提供されています。
bind mountを使用したデータ管理
volumeとの違いとしては、ユーザの管理しているファイルやディレクトリをコンテナにマウントできるという点です。こちらも同一のディレクトリやファイルを複数のコンテナにマウントすることができます。
基本的には、マウント元のファイルやディレクトリは事前に用意しておき、そのパスを指定してコンテナにマウントします。
プロジェクトのソースコードや設定ファイルをマウントしておくことにより、ホスト上で開発しながらその動作をコンテナで確認することができます。
tmpfsを使用したデータ管理
ホストのメモリ領域をファイルシステムとしてコンテナにマウントします。コンテナが停止した場合にはデータも合わせて破棄されてしまいます。用途としては、永続的なデータではなく一時的に保持したいデータを置く場所に使ったりします。(キャッシュやワンタイムパスワード)
volumeを使用したデータ管理
volumeの作成
docker-machine
で仮想環境を作成して、その中で確認していきます。
$ docker-machine create vol-test ~~~ $ docker-machine ssh vol-test ( '>') /) TC (\ Core is distributed with ABSOLUTELY NO WARRANTY. (/-_--_-\) www.tinycorelinux.net
volumeの作成はdocker volume create ボリューム名
で行えます。また、存在するvolume一覧を確認したい場合はdocker volume ls
コマンドで確認できます。
docker@vol-test:~$ docker volume create my-vol docker@vol-test:~$ docker volume ls DRIVER VOLUME NAME local my-vol
作成したvolumeの詳細を確認したい場合はinspect ボリューム名
です。
docker@vol-test:~$ docker volume inspect my-vol [ { "CreatedAt": "2019-02-22T23:28:26Z", "Driver": "local", "Labels": {}, "Mountpoint": "/mnt/sda1/var/lib/docker/volumes/my-vol/_data", "Name": "my-vol", "Options": {}, "Scope": "local" } ]
volumeの削除
作成したvolumeを削除する場合はrm ボリューム名
です。
docker@vol-test:~$ docker volume rm my-vol
volumeのマウント
実際にコンテナにマウントしてみる
マウントする方法としては2通りあります。
-v
オプションを指定してコンテナを起動する--mount
オプションを指定してコンテナを起動する
-v
オプションは、-v ボリューム名:マウントディレクトリ
という形で指定してあげます。下記例の場合だとvol1
というボリュームは存在しないので、新規に作成されます。
docker@vol-test:~$ docker run -itd --name mount-c1 -v vol1:/app nginx:latest docker@vol-test:~$ docker volume ls DRIVER VOLUME NAME local vol1
コンテナを詳細を確認することで、マウントの詳細も確認できます。
docker@vol-test:~$ docker inspect mount-c1 ~~~ "Mounts": [ { "Type": "volume", "Name": "vol1", "Source": "/mnt/sda1/var/lib/docker/volumes/vol1/_data", "Destination": "/app", "Driver": "local", "Mode": "z", "RW": true, "Propagation": "" } ]
コンテナにログインして、ファイルシステムを確認してみます。
docker@vol-test:~$ docker exec -it mount-c1 /bin/bash root@d2b3bf2a7e84:/# df Filesystem 1K-blocks Used Available Use% Mounted on overlay 18714044 165844 17559072 1% / tmpfs 65536 0 65536 0% /dev tmpfs 506548 0 506548 0% /sys/fs/cgroup /dev/sda1 18714044 165844 17559072 1% /app shm 65536 0 65536 0% /dev/shm tmpfs 506548 0 506548 0% /proc/asound tmpfs 506548 0 506548 0% /proc/acpi tmpfs 506548 0 506548 0% /proc/scsi tmpfs 506548 0 506548 0% /sys/firmware
/app
がファイルシステムとしてマウントされていることがわかります。試しに、/app
上にファイルを作ってみます。
root@d2b3bf2a7e84:/# cd app/ root@d2b3bf2a7e84:/app# touch hogehoge root@d2b3bf2a7e84:/app# ls hogehoge
この作ったファイルが、別のコンテナからも参照できるかどうか確認してみます。次は別コンテナを--mount
オプションでマウントさせてみます。(Ctrl+P, Ctrl+Q でコンテナからログアウトしてください)
--mount
コマンドでマウントする場合は、マウント元のボリュームをsource
、マウント先をtarget
に設定します。
docker@vol-test:~$ docker run -itd --name mount-c2 --mount source=vol1,target=/app nginx:latest b7f098b3b774253d10890a157efd694b5cd9ecaade330b0ac562f95bfd4a1e5e
/app
にファイルが存在しているか確認してみます。
docker@vol-test:~$ docker exec -it mount-c2 /bin/bash root@ad5bac9c2be3:/# cd app/ root@ad5bac9c2be3:/app# ls hogehoge
確認できました!コンテナを削除しても、ボリュームは残り続けるので破棄されません。
readonlyでマウントする
readonly
オプションをつけてコンテナを起動できます。,
の後ろに半角スペースなどをつけるとエラーになるので、気をつけてください。(ここで少しハマりました)
docker@vol-test:~$ docker run -itd --name mount-c4 --mount source=copy-vol,destination=/etc/nginx,readonly nginx
bind mountを使用したデータ管理
bind mountでは、任意のファイルやディレクトリをマウントすることができます。
コンテナにマウントしてみる
-v によるマウント
試しにカレントディレクトリに存在しないディレクトリsource
を指定してマウントしてみます。
docker@vol-test:~$ ls docker@vol-test:~$ docker run -itd --name bind-test1 -v "$(pwd)"/source:/app nginx a20485241f54076a8155d0286308465cfc8a64c0b335cf91bacc4a227fdae31c docker@vol-test:~$ ls source
--mount によるマウント(推奨)
-v
によるマウントと比較すると、--mount
によるマウントは存在しないディレクトリなどを指定できないようになっています。存在しないディレクトリを指定すると、下記のようにエラーになります。
docker@vol-test:~$ docker run -itd --name bind-test2 --mount type=bind,source="$(pwd)"/source2,target=/app nginx docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /home/docker/source2. See 'docker run --help'.
ちなみに、マウントの詳細を確認したい場合はinspect
を使用します。
docker@vol-test:~$ docker inspect bind-test1 ~~~ "Mounts": [ { "Type": "bind", "Source": "/home/docker/source", "Destination": "/app", "Mode": "", "RW": true, "Propagation": "rprivate" } ] ~~~
tmpfsを使用したデータ管理
tmpfsでは、-v
によるマウントは推奨されていないため、--mount
によるマウントを確認してみます。
コンテナにマウントしてみる
docker@vol-test:~$ docker run -itd --name tmptest --mount type=tmpfs,destination=/app nginx
また、マウントサイズやファイルモードをオプションで指定することもできます。
docker@vol-test:~$ docker run -itd --name tmptest2 --mount type=tmpfs,destination=/app,tmpfs-size=5000000000, tmpfs-mode=700 nginx
最後に
ボリュームが多くなってしまったので本記事では触れていませんが、Docker ComposeやDocker Swarmについても、後日まとめようと思っています。