Dockerネットワーク技術について理解を深めてみた

# 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ネットワークの構成を図示してみた。

![](https://i.imgur.com/w7TEXoS.png)

各コンテナ及びホストOSは、仮想ブリッジ（docker0）がNAT機能を担うことによって、各コンテナにIPアドレスが割り振られている。

つまり、コンテナから外部へ通信する場合は、仮想ブリッジ（docker0）経由で、NAT変換（IPマスカレード）が行われることで、通信を実現している。

この仮想ブリッジ（docker0）のNAT変換機能は、ホストOSのiptablesが提供するNAT機能によって、機能を実現させている。

各コンテナ(eth0)と仮想ブリッジ（docker0）を繋いでいるのが、仮想NIC（veth）である。仮想NIC（veth）は、各コンテナ内のeth0と、仮想ブリッジ（docker0）間の通信を実現するための、レイヤ２のインターフェース機能となります。コンテナ内の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  省略
```