Dockerネットワーク技術について理解を深める
Dockerコンテナ内で、名前解決ができない問題に直面し悩まされた。ホストOS側のFirewallのNAPT設定を有効にすることで解決できたが、Dockerネットワーク機能について理解が不足していたので、少し学習してみた。
問題の『事象』と『対策』
Dockerコンテナ内で名前解決ができていない。
$ docker run -it busybox ping -c 1 -4 google.com
ping: bad address 'google.com'
ホストOS側では名前解決ができている。
$ ping -c 1 -4 google.com
PING google.com (216.58.197.206) 56(84) bytes of data.
64 bytes from nrt13s48-in-f206.1e100.net (216.58.197.206): icmp_seq=1 ttl=56 time=1.16 ms
--- google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.163/1.163/1.163/0.000 ms
外部へのIP疎通はできている。
$ docker run -it busybox ping -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=55 time=2.921 ms
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 2.921/2.921/2.921 ms
どうやらDockerは外部と通信するために、IPマスカレードを使ったNATにより実現されており、ルーティングがiptablesのフォワードルールの形式で自動設定されているようだ。そこで、Firewall側でNAPT設定を有効にしてやることで解決した。
$ firewall-cmd --add-masquerade --permanent
$ firewall-cmd --reload
$ firewall-cmd --list-all --permanent | grep masquerade
masquerade: yes
Dockerコンテナ内で名前解決に成功し、疎通に成功した。
$ docker run -it busybox ping -c 1 -4 google.com
PING google.com (216.58.197.174): 56 data bytes
64 bytes from 216.58.197.174: seq=0 ttl=55 time=1.659 ms
--- google.com ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 1.659/1.659/1.659 ms
Dockerネットワークの構成
Dockerネットワークの構成を図示してみた。
各コンテナ及びホストOSは、仮想ブリッジ(docker0)がNAT機能を担うことによって、各コンテナにIPアドレスが割り振られている。
つまり、コンテナから外部へ通信する場合は、仮想ブリッジ(docker0)経由で、NAT変換(IPマスカレード)が行われることで、通信を実現している。
この仮想ブリッジ(docker0)のNAT変換機能は、ホストOSのiptablesが提供するNAT機能によって、機能を実現させている。
各コンテナ(eth0)と仮想ブリッジ(docker0)を繋いでいるのが、仮想NIC(veth)である。仮想NIC(veth)は、各コンテナ内のeth0と、仮想ブリッジ(docker0)間の通信を実現するための、レイヤ2のインターフェース機能となります。コンテナ内のeth0とvethは、スイッチを経由せずに直接LANケーブルで接続されているイメージです。
コマンドで確認
仮想ブリッジ(docker0)と、仮想NIC(veth51fab7f@if52)が生成されていることがわかる。また、ホストOSの物理NIC(eth0)と仮想ブリッジ(docker0)のネットワークセグメントが異なっていることがわかる。
$ 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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether XX:XX:XX:1b:13:95 brd ff:ff:ff:ff:ff:ff
inet XXX.XXX.19.149/23 brd XXX.XXX.19.255 scope global dynamic noprefixroute eth0
valid_lft 2868003sec preferred_lft 2868003sec
inet6 XXXX:XXXX:1801:409:XXX:XXX:19:149/128 scope global dynamic noprefixroute
valid_lft 2868005sec preferred_lft 2868005sec
inet6 XXXX::X:XXXX:fe1b:1395/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether XX:XX:XX:85:63:76 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 XXXX::XX:XXXX:fe85:6376/64 scope link
valid_lft forever preferred_lft forever
53: veth51fab7f@if52: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether XX:XX:XX:b9:e6:e0 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 XXXX::XXXX:XXXX:feb9:e6e0/64 scope link
valid_lft forever preferred_lft forever
172.17.0.0/16(docker0)のネットワーク範囲から、0.0.0.0/0のネットワーク範囲を宛先とする場合、送信元IPをホストOS側の物理NICのIPへ変換するように設定されている。
$ iptables -t nat -nL -v | grep -A 3 POSTROUTING
Chain POSTROUTING (policy ACCEPT 124 packets, 8216 bytes)
pkts bytes target prot opt in out source destination
29 1440 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
仮想ブリッジ(docker0)と仮想NIC(veth)の接続状況を確認してみる。しっかりとveth51fab7f@if52が接続されていることがわかる。
$ bridge link show docker0
53: veth51fab7f@if52: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master docker0 state forwarding priority 32 cost 2
コンテナ内からルーティングを確認してみた。
# docker run -it busybox ip route
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 scope link src 172.17.0.3
コンテナ内からgoogle.comへの通信で、NATされる様子を確認してみた。
# docker run -it busybox traceroute -n google.com
traceroute to google.com (216.58.197.206), 30 hops max, 46 byte packets
1 172.17.0.1 0.006 ms 0.003 ms 0.003 ms ← Docker側のゲートウェイ
2 XXX.XX.18.2 0.486 ms 0.165 ms 0.362 ms ← ホストOS側のゲートウェイ
3 省略