こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。
この記事ではタイトルにもあるように、dockerのブリッジの新規作成方法であったり、
異なるdockerネットワーク上にあるコンテナ同士で通信をするためにはどうすればいいか、
的なことをつらつらと書いていきたいと思います。
検証環境
今回用意している環境は以下の記事で作ったものを流用しています。
ホストのOSはubuntu22.04になります。
イメージに起こすと以下のような状態です。
dockerをインストールした際に自動生成されるdocker0(内部的にはLinuxBridge)があり、そこにnginx,apache,nextcloudコンテナが紐づいている状態です。
IPアドレスとかはこれから調べます。
現行のコマンドの結果も出力します。
- ip aの出力結果
root@docker:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 36:8b:75:5e:46:a8 brd ff:ff:ff:ff:ff:ff
altname enp0s18
inet 192.168.2.187/24 brd 192.168.2.255 scope global ens18
valid_lft forever preferred_lft forever
inet6 fe80::348b:75ff:fe5e:46a8/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:74:19:f9:df brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:74ff:fe19:f9df/64 scope link
valid_lft forever preferred_lft forever
5: vetheb1516c@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether d2:f5:55:85:c6:ba brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::d0f5:55ff:fe85:c6ba/64 scope link
valid_lft forever preferred_lft forever
7: vetheea41a8@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether aa:20:50:09:32:02 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::a820:50ff:fe09:3202/64 scope link
valid_lft forever preferred_lft forever
9: veth1d9a1bc@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 42:c5:df:b8:ed:fa brd ff:ff:ff:ff:ff:ff link-netnsid 2
inet6 fe80::40c5:dfff:feb8:edfa/64 scope link
valid_lft forever preferred_lft forever
- docker psの出力結果
root@docker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
83f27d094780 nextcloud "/entrypoint.sh apac…" 3 days ago Up 27 minutes 80/tcp sad_tu
6c498e8d2477 httpd "httpd-foreground" 3 days ago Up 27 minutes 80/tcp flamboyant_bhaskara
55ef2eae198a nginx "/docker-entrypoint.…" 3 days ago Up 27 minutes 80/tcp busy_poincare
- docker network lsの出力結果
root@docker:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
8aaf0c310fb9 bridge bridge local
e80bd3a263ec host host local
93abcdf649c5 none null local
- docker network inspectの出力結果
このコマンドを使うことでどのブリッジにどのdockerコンテナがあるかや、IPアドレスなどが表示されるので、
環境を把握したいときは便利だなと思いました。お恥ずかしながら初めて知りました。
root@docker:~# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "8aaf0c310fb984798bd2d67c9d3b82285c464fbe20b0b9c2604d205bab229166",
"Created": "2023-04-19T06:10:41.150803824+09:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"55ef2eae198ac8dbe76aaeaf538869eb12419976bcd3f514ad3d305663b194c4": {
"Name": "busy_poincare",
"EndpointID": "e8419aa5b4384d138ed000c259b0430236ecb8692b478a0728ec663a4282ddab",
"MacAddress": "02:42:ac:11:00:04",
"IPv4Address": "172.17.0.4/16",
"IPv6Address": ""
},
"6c498e8d24774cf7250df99fd945344a3e68c7200400ade3a756dd7bb6af2f93": {
"Name": "flamboyant_bhaskara",
"EndpointID": "51c72739df2dac30fa4c5c29fa1d276ac51d2db3e20b7e9eb77f5891ffd14756",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"83f27d094780b7dbab4b0b36f4c44afbfc8f4c82ddd01a3da0ba929fe74f34e1": {
"Name": "sad_tu",
"EndpointID": "2fe4bffb8473f5aaf3e4bd287d2fcc54e0f8bfaab20c414d42c3bb0df0f78c50",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
IPアドレスとvethを確認する
dockerコンテナはNetwork Namespace~ってことは、以前から私が書いている記事で何度か話しておりますが、
そうなってくると気になるのがvethですよね。
早速どのdockerコンテナが、どのvethを使っているのか確認してみよう!と思ったのですがコマンドをちょちょいと叩いてもわからないようであります。。。
そのため、上記Webサイトで記載されているdocker-netns.shというシェルをgitから引っ張ってきて叩いてみようと思います。
このシェルはvethだけではなく、IPアドレスも確認出来るので結構便利だと思いました!
root@docker:~# git clone https://github.com/pujoheadsoft/docker-netns.git
root@docker:~# ls
docker-netns snap
root@docker:~# cd docker-netns
root@docker:~/docker-netns# ls -l
total 8
-rw-r--r-- 1 root root 3972 Apr 19 06:56 docker-netns.sh
-rw-r--r-- 1 root root 280 Apr 19 06:56 README.md
root@docker:~/docker-netns# chmod +x docker-netns.sh
root@docker:~/docker-netns# ./docker-netns.sh visible
container's network namespace to visible, you can show container's nemespace by [ip netns] command.
root@docker:~/docker-netns# ./docker-netns.sh showveth
VETH CONTAINER ID NAMES
vetheb1516c 83f27d094780 /sad_tu
vetheea41a8 6c498e8d2477 /flamboyant_bhaskara
veth1d9a1bc 55ef2eae198a /busy_poincare
root@docker:~/docker-netns# ./docker-netns.sh showip
CONTAINER ID IP NAMES
83f27d094780 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 /sad_tu
6c498e8d2477 inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0 /flamboyant_bhaskara
55ef2eae198a inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0 /busy_poincare
シェルの実行結果から、私のdocker環境は以下であるといえます。
docker0につながっているコンテナ同士で疎通が取れることを確認してみます。
nextcloudコンテナから他2つのコンテナに対してpingを実行してみます。
コンテナの中にpingコマンドはないので以下でインストールします
root@docker:~# docker exec -it sad_tu /bin/bash
root@83f27d094780:/var/www/html# apt-get update
root@83f27d094780:/var/www/html# apt-get install iputils-ping net-tools
pingしてみると疎通が取れることがわかります。
root@83f27d094780:/var/www/html# ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.120 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.104 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.146 ms
64 bytes from 172.17.0.3: icmp_seq=4 ttl=64 time=0.117 ms
64 bytes from 172.17.0.3: icmp_seq=5 ttl=64 time=0.246 ms
--- 172.17.0.3 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4075ms
rtt min/avg/max/mdev = 0.104/0.146/0.246/0.051 ms
root@83f27d094780:/var/www/html# ping 172.17.0.4
PING 172.17.0.4 (172.17.0.4) 56(84) bytes of data.
64 bytes from 172.17.0.4: icmp_seq=1 ttl=64 time=0.491 ms
64 bytes from 172.17.0.4: icmp_seq=2 ttl=64 time=0.226 ms
64 bytes from 172.17.0.4: icmp_seq=3 ttl=64 time=0.159 ms
64 bytes from 172.17.0.4: icmp_seq=4 ttl=64 time=0.153 ms
64 bytes from 172.17.0.4: icmp_seq=5 ttl=64 time=0.104 ms
--- 172.17.0.4 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4077ms
rtt min/avg/max/mdev = 0.104/0.226/0.491/0.137 ms
新しいbridgeを作って、そこにコンテナを作成してみる
イメージ的には以下のような状態を目指したいと思います。
docker0ではなくmy-dk-brという名前のブリッジを作成。そこに対してコンテナを立ち上げてみます。
ブリッジの作成の仕方は簡単でdocker network createで作成できるようです。
root@docker:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
8aaf0c310fb9 bridge bridge local
e80bd3a263ec host host local
93abcdf649c5 none null local
root@docker:~# docker network create --driver bridge my-dk-br
4336e1dedc25b70101173337e680480d91bbe64d85ad015be39190c23e3633fc
root@docker:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
8aaf0c310fb9 bridge bridge local
e80bd3a263ec host host local
4336e1dedc25 my-dk-br bridge local
93abcdf649c5 none null local
ip aコマンドを叩いてみると10項目目にbr-から始まり、IPレンジが172.18.0.1/16のものがあり、
これが先ほど作成したmy-dk-brのものになります。
root@docker:~# ip a
10: br-4336e1dedc25: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:2c:42:bb:3f brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-4336e1dedc25
valid_lft forever preferred_lft forever
docker networkやbrctlコマンドでも存在を確認することができます。
root@docker:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
8aaf0c310fb9 bridge bridge local
e80bd3a263ec host host local
4336e1dedc25 my-dk-br bridge local
93abcdf649c5 none null local
root@docker:~# brctl show
bridge name bridge id STP enabled interfaces
br-4336e1dedc25 8000.02422c42bb3f no
docker0 8000.02427419f9df no veth1d9a1bc
vetheb1516c
vetheea41a8
ブリッジを指定した状態でコンテナを立ち上げるためにはdocker runコマンドで--netオプションをつけるといいようであります。
今回はdjangoのコンテナを立ち上げてみます。
root@docker:~# docker run -itd --name django-con --net my-dk-br django:latest
Unable to find image 'django:latest' locally
latest: Pulling from library/django
75a822cd7888: Pull complete
e4665cede9d1: Pull complete
202a45aa091c: Pull complete
7799136eb561: Pull complete
7a7f9ca3fd40: Pull complete
412f2d081014: Pull complete
Digest: sha256:5bfd3f442952463f5bc97188b7f43cfcd6c2f631a017ee2a6fca3cb8992501e8
Status: Downloaded newer image for django:latest
4fd58c1ecdd0354b55c9a71e0f1d2face81843555f127f005a0b4fe07af979dc
root@docker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4fd58c1ecdd0 django:latest "python3" 11 seconds ago Up 4 seconds django-con
83f27d094780 nextcloud "/entrypoint.sh apac…" 3 days ago Up About an hour 80/tcp sad_tu
6c498e8d2477 httpd "httpd-foreground" 3 days ago Up About an hour 80/tcp flamboyant_bhaskara
55ef2eae198a nginx "/docker-entrypoint.…" 3 days ago Up About an hour 80/tcp busy_poincare
インストールしたシェルで現在の状態を確認してみます。
root@docker:~/docker-netns# ./docker-netns.sh showveth
VETH CONTAINER ID NAMES
veth8580f3c 4fd58c1ecdd0 /django-con
vetheb1516c 83f27d094780 /sad_tu
vetheea41a8 6c498e8d2477 /flamboyant_bhaskara
veth1d9a1bc 55ef2eae198a /busy_poincare
root@docker:~/docker-netns# ./docker-netns.sh showip
CONTAINER ID IP NAMES
4fd58c1ecdd0 inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0 /django-con
83f27d094780 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 /sad_tu
6c498e8d2477 inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0 /flamboyant_bhaskara
55ef2eae198a inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0 /busy_poincare
出力された内容をイメージに落とし込むと以下のようになります。
異なるネットワーク上にあるコンテナ同士で疎通する
上記の状態では異なるブリッジに接続されているコンテナ同士で通信はできません。
ネットワークが異なるため、間にルータ的なのがいないといけないということですね。
実際にdjangoコンテナからdocker0側のコンテナに対して疎通が出来ないことを確認します。
何も反応がないので、疎通が取れないことがわかります。
root@4fd58c1ecdd0:/# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
^C--- 172.17.0.2 ping statistics ---
7 packets transmitted, 0 packets received, 100% packet loss
どうも異なるブリッジのコンテナ同士で疎通をするためには、以下Qiita記事のように
双方のネットワークにつながっているコンテナを用意して、そのコンテナをルータ的に使用しなければならないようです。
routerコンテナを立ち上げて2つのネットワークに接続します。
root@docker:~# docker run -itd --name router-con --net="bridge" centos:centos7
Unable to find image 'centos:centos7' locally
centos7: Pulling from library/centos
2d473b07cdd5: Pull complete
Digest: sha256:be65f488b7764ad3638f236b7b515b3678369a5124c47b8d32916d6487418ea4
Status: Downloaded newer image for centos:centos7
949d5842b7411444b5d830e03b4efaa7c08e0006e0ee4a2e5f1021b473441a00
root@docker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
949d5842b741 centos:centos7 "/bin/bash" 11 seconds ago Up 9 seconds router-con
4fd58c1ecdd0 django:latest "python3" 23 hours ago Up 23 hours django-con
83f27d094780 nextcloud "/entrypoint.sh apac…" 4 days ago Up 25 hours 80/tcp sad_tu
6c498e8d2477 httpd "httpd-foreground" 4 days ago Up 25 hours 80/tcp flamboyant_bhaskara
55ef2eae198a nginx "/docker-entrypoint.…" 4 days ago Up 25 hours 80/tcp busy_poincare
root@docker:~# docker network connect my-dk-br router-con
docker inspectで2つのネットワークにつながっているか確認します。
root@docker:~/docker-netns# docker inspect router-con
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "8aaf0c310fb984798bd2d67c9d3b82285c464fbe20b0b9c2604d205bab229166",
"EndpointID": "c06e7d4546a2c431cffc198b85a078efcb3266199393f3de7a1013b74af30c5b",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.5",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:05",
"DriverOpts": null
},
"my-dk-br": {
"IPAMConfig": {},
"Links": null,
"Aliases": [
"949d5842b741"
],
"NetworkID": "4336e1dedc25b70101173337e680480d91bbe64d85ad015be39190c23e3633fc",
"EndpointID": "ebeada3751575b8dd6df05eb6b28e63fe29a98f5bc815483f4c6813b83e5fdac",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:12:00:03",
"DriverOpts": {}
}
根拠となるログを若干端折りますが、現在は以下のような感じになります。
routerコンテナの現在のルーティングテーブルを確認します。
また、net.ipv4.ip_forwardが1になっていることを確認します。
これが1になっていないとIPフォワーディングが有効になっておらず、パケットを転送しないようですので。。。
root@docker:~# docker exec -it router-con /bin/bash
[root@949d5842b741 /]# yum -y install net-tools tcpdump traceroute
[root@949d5842b741 /]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.17.0.1 0.0.0.0 UG 0 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth1
[root@949d5842b741 /]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
続いて、djangoコンテナとnextcloudコンテナのルーティングテーブルに変更を加えていきます。
まずdjangoコンテナから。。。と思ったのですがエラーを吐かれてしまいました。
root@docker:~# docker exec -it django-con /bin/bash
root@4fd58c1ecdd0:/# ip route add 172.17.0.0/16 via 172.18.0.3
RTNETLINK answers: Operation not permitted
原因を確認していると以下の記事にあたりました。
どうやら--privilegedオプションが必要となる様子ですね。。。
djangoとNextcloudを作り直していきます。
これは少しだるい(笑)
root@docker:~# docker stop django-con
root@docker:~# docker stop sad_tu
root@docker:~# docker rm django-con sad_tu
root@docker:~# docker run -itd --privileged --net="bridge" --name nextcloud-con nextcloud:latest
root@docker:~# docker run -itd --privileged --net="my-dk-br" --name django-con django:latest
djangoコンテナから設定を入れていきます。
設定と言っても172.17.0.0/16に対するスタティックルートを設定するだけですが・・・
root@b8036464df51:/# ip route show
default via 172.18.0.1 dev eth0
172.17.0.0/16 via 172.18.0.3 dev eth0
172.18.0.0/16 dev eth0 proto kernel scope link src 172.18.0.2
root@b8036464df51:/# ip route add 172.17.0.0/16 via 172.18.0.3
root@b8036464df51:/# ip route show
default via 172.18.0.1 dev eth0
172.17.0.0/16 via 172.18.0.3 dev eth0
172.18.0.0/16 dev eth0 proto kernel scope link src 172.18.0.2
続いてnextcloudコンテナの設定を入れていきます。
root@docker:~# docker exec -it nextcloud-con /bin/bash
root@b0002d24b3c4:/var/www/html# uname -a
Linux b0002d24b3c4 5.15.0-69-generic #76-Ubuntu SMP Fri Mar 17 17:19:29 UTC 2023 x86_64 GNU/Linux
root@b0002d24b3c4:/var/www/html# apt update
root@b0002d24b3c4:/var/www/html# apt install -y iproute2
root@b0002d24b3c4:/var/www/html# ip route add 172.18.0.0/16 via 172.17.0.5
root@b0002d24b3c4:/var/www/html# ip route show
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2
172.18.0.0/16 via 172.17.0.5 dev eth0
疎通確認を行います。まずdjangoコンテナからnextcloudコンテナに対して疎通確認をします。
出力結果的に疎通が出来ていることが分かりますね!
root@b8036464df51:/# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: icmp_seq=0 ttl=63 time=1.926 ms
64 bytes from 172.17.0.2: icmp_seq=1 ttl=63 time=0.137 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=63 time=0.179 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=63 time=0.140 ms
64 bytes from 172.17.0.2: icmp_seq=4 ttl=63 time=0.133 ms
^C--- 172.17.0.2 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.133/0.503/1.926/0.712 ms
反対もやってみます。上記同様に出来ていますね!
root@b0002d24b3c4:/var/www/html# ping 172.18.0.2
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
64 bytes from 172.18.0.2: icmp_seq=1 ttl=63 time=0.271 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=63 time=0.135 ms
64 bytes from 172.18.0.2: icmp_seq=3 ttl=63 time=0.112 ms
64 bytes from 172.18.0.2: icmp_seq=4 ttl=63 time=0.154 ms
64 bytes from 172.18.0.2: icmp_seq=5 ttl=63 time=0.121 ms
^C
--- 172.18.0.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4075ms
rtt min/avg/max/mdev = 0.112/0.158/0.271/0.057 ms