Kubernetes ネットワークの仕組み (Azure)


[Kubernetes] のネットワークがどの様に実現されているか知りたかったので調べてメモしました。ここに書いているのは...

* コンテナ (POD) のIPアドレスがどのようにルーティングされるか
* [SERVICE] の Cluster-IP が、複数いる POD の IPアドレスにどうルーティングされるか
* コンテナ (POD) 内のDNS名前解決はどうされるか

> 普通の Docker のネットワークについては [こちら](https://qiita.com/megmogmog1965/items/f5682de2068a79d196a2)

> Kubernetes ネットワークの仕組み (GKE) は [こちら](https://qiita.com/megmogmog1965/items/0077766cc422d91d8088) 。ほぼ同じです。

## 検証環境

[Azure Container Service] です。[GKE] ではありません。

2台の Agent Node と、1台の Master Node。

```console
# kubectl get nodes

NAME                    STATUS    AGE       VERSION
k8s-agent-XXXXXXXX-0    Ready     4h        v1.7.7
k8s-agent-XXXXXXXX-1    Ready     4h        v1.7.7
k8s-master-XXXXXXXX-0   Ready     4h        v1.7.7
```

特に何も追加でデプロイはしませんでした。Kubernetes 標準の POD ``kube-dns`` が、いい感じに2台いて [SERVICE] で分散されているので、これを題材にします。

```console
# kubectl get pods --namespace=kube-system

NAME                                READY     STATUS    RESTARTS   AGE
kube-dns-v20-3003781527-lc299       3/3       Running   0          4h
kube-dns-v20-3003781527-njvzk       3/3       Running   0          4h
...
```

```console
# kubectl get services --namespace=kube-system

NAME               CLUSTER-IP    EXTERNAL-IP   PORT(S)         AGE
kube-dns           10.0.0.10     <none>        53/UDP,53/TCP   4h
...
```


## まとめ

まずはまとめから。↓は [Azure Container Service] の場合で [GKE] はまたちょっと違うと思います。

> Agent Node のみ。Master Node は省略。

![Kubernetes (Azure).png](https://qiita-image-store.s3.amazonaws.com/0/60131/e3b12080-7353-23c6-d23e-5e18e1cf8556.png "Kubernetes (Azure).png")

主要な3つのネットワーク範囲があります。

* 物理ホストのネットワークが ``10.240.0.0/16``
* それぞれの物理ホスト上に、コンテナのネットワーク ``10.244.X.0/24`` がある
    * Agent Node #0 には ``10.244.0.0/24``
    * Agent Node #1 には ``10.244.1.0/24``
* [SERVICE] の Cluster-IP は ``10.0.0.0/16`` の範囲で振られる

コンテナのネットワーク。別々のホスト上にいるコンテナはこう↓ルーティングされます。

* Destination=``10.244.0.0/16`` へのアクセスは Default Gateway ``10.240.0.1`` へ送信されます
* 物理ゲートウェイのルートテーブルで物理ホストのIPアドレスへルーティングされます
    * 例えば、Destination=``10.244.1.0/24`` → Next Hop=``10.240.0.5``
* 物理ホストに到達すると、ホストOSのルートテーブルで仮想NIC ``cbr0`` に送信
* 仮想NIC ``cbr0`` の先に、宛先のコンテナのNICがあります

[SERVICE] の Cluster-IP 宛に送信すると...

* ``10.0.0.0/16`` は全て **ホストOSの iptables** で解決されます (このアドレス範囲に該当する NIC はない)
* 宛先 ``10.0.0.0/16`` は [DNAT] で宛先 ``10.244.X.0/24`` (コンテナIP) に書き換えられます
* コンテナ (POD) が複数いる時は ``random probability 0...`` でランダムに選択されます

また、コンテナ内の ``/etc/resolv.conf`` を見ると nameserver は固定で ``10.0.0.10`` です。
これは ``kube-dns`` [SERVICE] の Cluster-IP です。


## 実際に追ってみた編

書いてあるIPとかは *今回たまたまこうだった* です。

### Gateway (10.240.0.1) のルートテーブル

コンテナネットワーク ``10.244.X.0/24`` へのアクセスが、そのネットワークが存在する物理ホスト宛にルーティングされています。

| NAME | ADDRESS PREFIX | NEXT HOP |
|:----:|:--------------:|:--------:|
| k8s-agent-154d19d7-0 | 10.244.0.0/24 | 10.240.0.4 |
| k8s-agent-154d19d7-1 | 10.244.1.0/24 | 10.240.0.5 |
| k8s-master-154d19d7-0 | 10.244.2.0/24 | 10.240.255.5 |


### ホスト (Agent Node) のネットワーク

ifconfig の結果。

* cbr0 ... この仮想NICの先にコンテナのNICがあります
* docker0 ... Docker 標準のNICだけど [Kubernetes] 環境では使われていない?
* eth0 ... ホストOSの物理NICです
* vethXXXX ... ホストNIC→コンテナNIC間の仮想Ethernetケーブル

```console
# ifconfig

cbr0      Link encap:Ethernet  HWaddr 0a:58:0a:f4:00:01  
          inet addr:10.244.0.1  Bcast:0.0.0.0  Mask:255.255.255.0
          inet6 addr: fe80::808c:97ff:feb1:3bd5/64 Scope:Link
          UP BROADCAST RUNNING PROMISC MULTICAST  MTU:1500  Metric:1
          RX packets:187644 errors:0 dropped:0 overruns:0 frame:0
          TX packets:228056 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:17175759 (17.1 MB)  TX bytes:150372583 (150.3 MB)

docker0   Link encap:Ethernet  HWaddr 02:42:bf:91:4b:33  
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:bfff:fe91:4b33/64 Scope:Link
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:6 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:424 (424.0 B)  TX bytes:508 (508.0 B)

eth0      Link encap:Ethernet  HWaddr 00:0d:3a:51:da:e1  
          inet addr:10.240.0.4  Bcast:10.240.255.255  Mask:255.255.0.0
          inet6 addr: fe80::20d:3aff:fe51:dae1/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:853884 errors:0 dropped:0 overruns:0 frame:0
          TX packets:568944 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:891926509 (891.9 MB)  TX bytes:78778540 (78.7 MB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:164 errors:0 dropped:0 overruns:0 frame:0
          TX packets:164 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1 
          RX bytes:12040 (12.0 KB)  TX bytes:12040 (12.0 KB)

veth16050088 Link encap:Ethernet  HWaddr be:b7:5f:05:e5:8e  
          inet6 addr: fe80::bcb7:5fff:fe05:e58e/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:6741 errors:0 dropped:0 overruns:0 frame:0
          TX packets:12518 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:400888 (400.8 KB)  TX bytes:939070 (939.0 KB)

...
```

ルートテーブル。

* 外部への通信は Default Gateway ``10.240.0.1`` へ
* 物理ホストへの通信は ``eth0`` NIC に投げる
* このホスト上にあるコンテナへの通信は ``cbr0`` NIC に投げる
* 別のホスト上にあるコンテナへの通信は Default Gateway ``10.240.0.1`` へ

```console
# route -n

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.240.0.1      0.0.0.0         UG    0      0        0 eth0
10.240.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0
10.244.0.0      0.0.0.0         255.255.255.0   U     0      0        0 cbr0
...
```

### コンテナのネットワーク

ubuntu ベースのコンテナで試したので、ネットワークコマンドは ``apt-get install net-tools`` で入れました

ifconfig の結果。

* eth0 ... コンテナのIPアドレスが振られている仮想NICです。この先にホストOSの ``cbr0`` 仮想NICがあります

```console
# ifconfig

eth0      Link encap:Ethernet  HWaddr 0a:58:0a:f4:00:06  
          inet addr:10.244.0.6  Bcast:0.0.0.0  Mask:255.255.255.0
          inet6 addr: fe80::c41c:2dff:fe41:4752/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:12441 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6701 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:934720 (912.8 KiB)  TX bytes:398536 (389.1 KiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
```

> コンテナのIPは ``10.244.{ホスト毎の連番}.{ホスト上のコンテナ毎の連番}`` になります

ルートテーブル。

* 外部への通信は Gateway ``10.244.0.1`` へ
* 同じホスト上のコンテナへの通信は ``eth0`` NIC へ
* 違うホスト上のコンテナへの通信は Gateway ``10.244.0.1`` へ

```console
# route -n

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.244.0.1      0.0.0.0         UG    0      0        0 eth0
10.244.0.0      0.0.0.0         255.255.255.0   U     0      0        0 eth0
```

### SERVICE の Cluster-IP

題材として ``kube-dns`` [SERVICE] の Cluster-IP ``10.0.0.10`` に着目しました。ホストOSの ``iptables`` を見てみます。

```console
# iptables -t nat -L -n -v
...

Chain KUBE-SERVICES (2 references)
 pkts bytes target                     prot opt in     out   source        destination
    ...
    0     0 KUBE-MARK-MASQ             tcp  --  *      *    !10.244.0.0/16  10.0.0.10    tcp dpt:53
    0     0 KUBE-SVC-ERIFXISQEP7F7OF4  tcp  --  *      *     0.0.0.0/0      10.0.0.10    tcp dpt:53
```

Chain ``KUBE-SVC-ERIFXISQEP7F7OF4``. ランダムで別々のルールに転送される様になっています。今回は ``kube-dns`` POD は2台だったので、それぞれ 50% の確率が割り当てられていますね。

```console
# iptables -t nat -L -n -v
...

Chain KUBE-SVC-ERIFXISQEP7F7OF4 (1 references)
 pkts bytes target                     prot opt in     out   source        destination
    ...
    0     0 KUBE-SEP-IT2ZTR26TO4XFPTO  all  --  *      *     0.0.0.0/0      0.0.0.0/0 statistic mode random probability 0.50000000000
    0     0 KUBE-SEP-BWHGELGX6BITPZVO  all  --  *      *     0.0.0.0/0      0.0.0.0/0
```

確率50% その１ --> Chain ``KUBE-SEP-IT2ZTR26TO4XFPTO``. [DNAT] で宛先をコンテナ (POD) のIPアドレスに書き換え。

```console
# iptables -t nat -L -n -v
...

Chain KUBE-SEP-IT2ZTR26TO4XFPTO (1 references)
 pkts bytes target                     prot opt in     out     source        destination
    0     0 KUBE-MARK-MASQ             all  --  *      *       10.244.0.2     0.0.0.0/0
    0     0 DNAT                       tcp  --  *      *       0.0.0.0/0      0.0.0.0/0 tcp to:10.244.0.2:53
```

確率50% その２ --> Chain ``KUBE-SEP-BWHGELGX6BITPZVO``. これは *その１* とは異なる物理ホスト上のコンテナIPを示しています。

```console
# iptables -t nat -L -n -v
...

Chain KUBE-SEP-BWHGELGX6BITPZVO (1 references)
 pkts bytes target                     prot opt in     out     source        destination
    0     0 KUBE-MARK-MASQ             all  --  *      *       10.244.1.2     0.0.0.0/0
    0     0 DNAT                       tcp  --  *      *       0.0.0.0/0      0.0.0.0/0 tcp to:10.244.1.2:53
```

### コンテナの DNS Nameserver

コンテナに入って ``/etc/resolv.conf`` を確認。

```console
# cat /etc/resolv.conf

nameserver 10.0.0.10
search default.svc.cluster.local svc.cluster.local cluster.local tbshyrd11cuundvieskktqf3ib.lx.internal.cloudapp.net
options ndots:5
```

この ``10.0.0.10`` は前述の通り ``kube-dns`` [SERVICE] の Cluster-IP なので、２台の ``kube-dns`` POD のうちどちらかで名前解決されることになります。


[Kubernetes]:https://kubernetes.io/
[SERVICE]:https://kubernetes.io/docs/concepts/services-networking/service/
[GKE]:https://cloud.google.com/kubernetes-engine/?hl=ja
[Azure Container Service]:https://azure.microsoft.com/ja-jp/pricing/details/container-service/
[GKE Tutorial]:https://cloud.google.com/kubernetes-engine/docs/tutorials/guestbook
[GitHub kubernetes/examples/guestbook]:https://github.com/kubernetes/kubernetes/tree/release-1.9/examples/guestbook
[DNAT]:http://www.asahi-net.or.jp/~aa4t-nngk/ipttut/output/dnattarget.html