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
...
まとめ
主要な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
- Agent Node #0 には
-
SERVICE の Cluster-IP は
10.11.0.0/16
の範囲で振られる
コンテナのネットワーク。別々のホスト上にいるコンテナはこう↓ルーティングされます。
- Destination=
10.8.0.0/16
へのアクセスは Default Gateway10.146.0.1
へ送信されます - 物理ゲートウェイのルートテーブルで物理ホストのIPアドレスへルーティングされます
- 例えば、Destination=
10.8.2.0/24
→ Next Hop=10.146.0.3
- 例えば、Destination=
- 物理ホストに到達すると、ホストOSのルートテーブルで仮想NIC
cbr0
に送信 - 仮想NIC
cbr0
の先に、宛先のコンテナのNICがあります
SERVICE の Cluster-IP 宛に送信すると...
-
10.11.0.0/16
は全て ホストOSの iptables で解決されます (このアドレス範囲に該当する NIC はない) - 宛先
10.11.0.0/16
は DNAT で宛先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
はホストの Bridgecbr0
のこと。
# 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 のうちどちらかで名前解決されることになります。