Edited at

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

More than 1 year has passed since last update.

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


  • コンテナ (POD) のIPアドレスがどのようにルーティングされるか


  • SERVICE の Cluster-IP が、複数いる POD の IPアドレスにどうルーティングされるか

  • コンテナ (POD) 内のDNS名前解決はどうされるか


普通の Docker のネットワークについては こちら

Kubernetes ネットワークの仕組み (Azure) は こちら 。ほぼ同じです。



検証環境

新規に Project を作りました。Zone は asia-northeast1-a です。

# gcloud init

> [34] asia-northeast1-a

Enable Kubernetes APIs する。

コンテナクラスタを作ります。

# gcloud container clusters create gke-test

クラスタに認証する。

# gcloud container clusters get-credentials gke-test

3台の Agent Node。

# kubectl get nodes


NAME STATUS ROLES AGE VERSION
gke-gke-test-default-pool-5dd7587d-jfwx Ready <none> 2h v1.8.10-gke.0
gke-gke-test-default-pool-5dd7587d-lwgr Ready <none> 2h v1.8.10-gke.0
gke-gke-test-default-pool-5dd7587d-v1t8 Ready <none> 2h v1.8.10-gke.0

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

# kubectl get pods --namespace=kube-system


NAME READY STATUS RESTARTS AGE
kube-dns-778977457c-89f5w 3/3 Running 0 2h
kube-dns-778977457c-c27bf 3/3 Running 0 2h
...

# kubectl get services --namespace=kube-system


NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.11.240.10 <none> 53/UDP,53/TCP 2h
...


まとめ

Kubernetes (GKE).png

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


  • 物理ホストのネットワークが 10.146.0.0/20

  • それぞれの物理ホスト上に、コンテナのネットワーク 10.8.X.0/24 がある


    • Agent Node #0 には 10.8.0.0/24

    • Agent Node #1 には 10.8.1.0/24




  • SERVICE の Cluster-IP は 10.11.0.0/16 の範囲で振られる

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


  • Destination=10.8.0.0/16 へのアクセスは Default Gateway 10.146.0.1 へ送信されます

  • 物理ゲートウェイのルートテーブルで物理ホストのIPアドレスへルーティングされます


    • 例えば、Destination=10.8.2.0/24 → Next Hop=10.146.0.3



  • 物理ホストに到達すると、ホストOSのルートテーブルで仮想NIC cbr0 に送信

  • 仮想NIC cbr0 の先に、宛先のコンテナのNICがあります

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



  • 10.11.0.0/16 は全て ホストOSの iptables で解決されます (このアドレス範囲に該当する NIC はない)

  • 宛先 10.11.0.0/16DNAT で宛先 10.8.X.0/24 (コンテナIP) に書き換えられます

  • コンテナ (POD) が複数いる時は random probability 0... でランダムに選択されます

また、コンテナ内の /etc/resolv.conf を見ると nameserver は固定で 10.11.240.10 です。

これは kube-dns SERVICE の Cluster-IP です。


実際に追ってみた編

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


Gateway (10.146.0.1) のルートテーブル

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

$ gcloud compute routes list

NAME NETWORK DEST_RANGE NEXT_HOP...
default-route-1318533de36be5ea default 10.146.0.0/20
...
gke-gke-test-a8a70139-3ee5fa90-621b-11e8-b095-42010a920113 default 10.8.0.0/24 10.146.0.2
gke-gke-test-a8a70139-3f4bd69a-621b-11e8-b095-42010a920113 default 10.8.1.0/24 10.146.0.4
gke-gke-test-a8a70139-443440a0-621b-11e8-b095-42010a920113 default 10.8.2.0/24 10.146.0.3


分かりにくかったので、NEXT_HOP は インスタンス名 → IP に書き直しました。



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

ifconfig の結果。


  • cbr0 ... ホストOS上の仮想Bridge。この先にコンテナのNICがあります

  • docker0 ... Docker 標準のNICだけど Kubernetes 環境では使われていない?

  • eth0 ... ホストOSの物理NICです

  • vethXXXX ... ホストNIC→コンテナNIC間の仮想Ethernetケーブル

# ifconfig


cbr0: flags=4419<UP,BROADCAST,RUNNING,PROMISC,MULTICAST> mtu 1460
inet 10.8.0.1 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::a878:31ff:fe18:b268 prefixlen 64 scopeid 0x20<link>
ether 0a:58:0a:08:00:01 txqueuelen 1000 (Ethernet)
RX packets 76160 bytes 18592370 (17.7 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 79863 bytes 17812809 (16.9 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 169.254.123.1 netmask 255.255.255.0 broadcast 0.0.0.0
ether 02:42:bf:98:cf:97 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1460
inet 10.146.0.2 netmask 255.255.255.255 broadcast 10.146.0.2
inet6 fe80::4001:aff:fe92:2 prefixlen 64 scopeid 0x20<link>
ether 42:01:0a:92:00:02 txqueuelen 1000 (Ethernet)
RX packets 103819 bytes 109670772 (104.5 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 102925 bytes 19371896 (18.4 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

veth44c43d08: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1460
inet6 fe80::f074:dcff:fe85:89cd prefixlen 64 scopeid 0x20<link>
ether f2:74:dc:85:89:cd txqueuelen 0 (Ethernet)
RX packets 6183 bytes 591309 (577.4 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 6536 bytes 589035 (575.2 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

...

ルートテーブル。


  • 外部への通信は Default Gateway 10.146.0.1

  • 別の物理ホストへの通信は Default Gateway 10.146.0.1

  • このホスト上にあるコンテナへの通信は cbr0 NIC に投げる

  • 別のホスト上にあるコンテナへの通信は Default Gateway 10.146.0.1

# route -n


Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.146.0.1 0.0.0.0 UG 1024 0 0 eth0
10.8.0.0 0.0.0.0 255.255.255.0 U 0 0 0 cbr0
...


コンテナのネットワーク

ifconfig の結果。


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

# ifconfig


eth0 Link encap:Ethernet HWaddr 0A:58:0A:08:00:04
inet addr:10.8.0.4 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::88b8:61ff:fe87:588e/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1460 Metric:1
RX packets:48015 errors:0 dropped:0 overruns:0 frame:0
TX packets:43494 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:10550980 (10.0 MiB) TX bytes:10681974 (10.1 MiB)
...


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


ルートテーブル。


  • 外部への通信は Gateway 10.8.0.1

  • 同じホスト上のコンテナへの通信は eth0 NIC へ

  • 違うホスト上のコンテナへの通信は Gateway 10.8.0.1


コンテナ内部から見た Gateway 10.8.0.1 はホストの Bridge cbr0 のこと。


# route -n


Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.8.0.1 0.0.0.0 UG 0 0 0 eth0
10.8.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0


SERVICE の Cluster-IP

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

# 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.8.0.0/14 10.11.240.10 tcp dpt:53
0 0 KUBE-SVC-ERIFXISQEP7F7OF4 tcp -- * * 0.0.0.0/0 10.11.240.10 tcp dpt:53

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

# 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-AETEL5TCYEIYP6LO all -- * * 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.50000000000
0 0 KUBE-SEP-O3HFREXOOMGV2B4D all -- * * 0.0.0.0/0 0.0.0.0/0

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

# iptables -t nat -L -n -v

...

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

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

# iptables -t nat -L -n -v

...

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


コンテナの DNS Nameserver

コンテナを一つ作って中に入ってみます。

# kubectl run --image=nginx:alpine nginx-app

# kubectl exec -it nginx-app-0000000000-xxxxx sh

このコンテナの /etc/resolv.conf を確認。

# cat /etc/resolv.conf


nameserver 10.11.240.10
search default.svc.cluster.local svc.cluster.local cluster.local c.gke-test-000000.internal google.internal
options ndots:5

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