こんにちは。サイオステクノロジー技術部 武井です。以前、この記事でKubernetesを構築しました。その時、Pod宛の通信をロードバランスするClusterIPなるものがありましたが、ClusterIP宛の通信がどういう原理で、各Podにロードバランスされるかを調べてみました。また、合わせて、nodeのインターフェース宛の通信がどういう原理でPodに到達するかも調べてみました。
ネットワーク構成
Kubernetesのネットワーク構成のイメージ図は以下のとおりです。
上記は、この記事で構築した環境となります。以下、詳細です。
- Pod間のネットワークはflannelというVXLANを管理するソフトウェアにより、172.30.0.0/16というネットワーク空間を持ち、さらにそのネットワークをPodごとに24ビットのネットワーク空間に区切っている。
- flannel.1というデバイスがVXLANデバイスである。
- 別ノード宛のパケットは、VXLANデバイスflannel.1によりVXLANヘッダが付与された形でカプセル化され、nodeのeth0を通り、別ノードに届く。(このあたりの仕組みは別記事で後述します)
- doker0という仮想ブリッジ(IPアドレスをもっている)があり、Podはこのブリッジにつながっている。
- Cluster IPというIPアドレスがある。ネットワークインターフェースは持たず、iptablesの中で定義された仮想的なIPアドレスである。このIPアドレス宛のパケットは各nodeにロードバランスされる。
node01のネットワーク関連の情報
この記事でServiceを作成しましたが、Serviceを作成すると、たくさんのiptablesのルールが作成されます。そのルールが複雑に絡み合い、ClusterIP宛の通信がきちんとロードバランスされる仕組みとなっています。
ここでは、node01のRouting Table、iptablesのチェイン、ネットワークインターフェースは以下のとおり記載します。以降にnode02の情報も合わせて記載しますが、これらは、通信を調査する上で重要な情報になります。これらの情報をひつつずつ分析した上で、Podにパケットがどのように届くかを調べてみたいと思います。
ネットワークインターフェース
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 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 pfifo_fast state UP qlen 1000 link/ether 00:0d:3a:50:0f:74 brd ff:ff:ff:ff:ff:ff inet 10.4.4.5/24 brd 10.4.4.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::20d:3aff:fe50:f74/64 scope link valid_lft forever preferred_lft forever 3: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN link/ether ea:48:c7:70:4d:6d brd ff:ff:ff:ff:ff:ff inet 172.30.31.0/32 scope global flannel.1 valid_lft forever preferred_lft forever inet6 fe80::e848:c7ff:fe70:4d6d/64 scope link valid_lft forever preferred_lft forever 4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP link/ether 02:42:cc:c4:90:01 brd ff:ff:ff:ff:ff:ff inet 172.30.31.1/24 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:ccff:fec4:9001/64 scope link valid_lft forever preferred_lft forever 6: vethbbd9a05@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master docker0 state UP link/ether 8a:e7:a3:a6:3c:bd brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::88e7:a3ff:fea6:3cbd/64 scope link valid_lft forever preferred_lft forever
Routing Table
Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default gateway 0.0.0.0 UG 0 0 0 eth0 10.4.4.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 168.63.129.16 gateway 255.255.255.255 UGH 0 0 0 eth0 link-local 0.0.0.0 255.255.0.0 U 1002 0 0 eth0 169.254.169.254 gateway 255.255.255.255 UGH 0 0 0 eth0 172.30.0.0 0.0.0.0 255.255.0.0 U 0 0 0 flannel.1 172.30.31.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0
iptablesのチェイン
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER -A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER -A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING -A POSTROUTING -s 172.30.31.0/24 ! -o docker0 -j MASQUERADE -A DOCKER -i docker0 -j RETURN -A KUBE-MARK-DROP -j MARK --set-xmark 0x8000/0x8000 -A KUBE-MARK-MASQ -j MARK --set-xmark 0x40000x4000 -A KUBE-NODEPORTS -p tcp -m comment --comment "default/dev-np:https" -m tcp --dport 31707 -j KUBE-MARK-MASQ -A KUBE-NODEPORTS -p tcp -m comment --comment "default/dev-np:https" -m tcp --dport 31707 -j KUBE-SVC-3UCI24FU4XAF2OPE -A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -m mark --mark 0x4000/0x4000 -j MASQUERADE -A KUBE-SEP-2GFQOP4A6JXPIKZI -s 172.30.31.2/32 -m comment --comment "default/dev-np:https" -j KUBE-MARK-MASQ -A KUBE-SEP-2GFQOP4A6JXPIKZI -p tcp -m comment --comment "default/dev-np:https" -m tcp -j DNAT --to-destination 172.30.31.2:80 -A KUBE-SEP-BDZCPQIUEUTMNSCN -s 10.4.4.4/32 -m comment --comment "default/kubernetes:https" -j KUBE-MARK-MASQ -A KUBE-SEP-BDZCPQIUEUTMNSCN -p tcp -m comment --comment "default/kubernetes:https" -m recent --set --name KUBE-SEP-BDZCPQIUEUTMNSCN --mask 255.255.255.255 --rsource -m tcp -j DNAT --to-destination 10.4.4.4:6443 -A KUBE-SEP-OKBK5A3GPOH3UXPW -s 172.30.91.2/32 -m comment --comment "default/dev-np:https" -j KUBE-MARK-MASQ -A KUBE-SEP-OKBK5A3GPOH3UXPW -p tcp -m comment --comment "default/dev-np:https" -m tcp -j DNAT --to-destination 172.30.91.2:80 -A KUBE-SERVICES -d 10.254.236.167/32 -p tcp -m comment --comment "default/dev-np:https cluster IP" -m tcp --dport 80 -j KUBE-SVC-3UCI24FU4XAF2OPE -A KUBE-SERVICES -d 10.254.0.1/32 -p tcp -m comment --comment "default/kubernetes:https cluster IP" -m tcp --dport 443 -j KUBE-SVC-NPX46M4PTMTKRN6Y -A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS -A KUBE-SVC-3UCI24FU4XAF2OPE -m comment --comment "default/dev-np:https" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-2GFQOP4A6JXPIKZI -A KUBE-SVC-3UCI24FU4XAF2OPE -m comment --comment "default/dev-np:https" -j KUBE-SEP-OKBK5A3GPOH3UXPW -A KUBE-SVC-NPX46M4PTMTKRN6Y -m comment --comment "default/kubernetes:https" -m recent --rcheck --seconds 10800 --reap --name KUBE-SEP-BDZCPQIUEUTMNSCN --mask 255.255.255.255 --rsource -j KUBE-SEP-BDZCPQIUEUTMNSCN -A KUBE-SVC-NPX46M4PTMTKRN6Y -m comment --comment "default/kubernetes:https" -j KUBE-SEP-BDZCPQIUEUTMNSCN
node02のネットワーク関連の情報
node02のRouting Table、iptablesのチェイン、ネットワークインターフェースは以下のとおりです。
ネットワークインターフェース
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 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 pfifo_fast state UP qlen 1000 link/ether 00:0d:3a:50:f5:1c brd ff:ff:ff:ff:ff:ff inet 10.4.4.7/24 brd 10.4.4.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::20d:3aff:fe50:f51c/64 scope link valid_lft forever preferred_lft forever 3: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN link/ether 8e:21:ec:ac:11:c9 brd ff:ff:ff:ff:ff:ff inet 172.30.91.0/32 scope global flannel.1 valid_lft forever preferred_lft forever inet6 fe80::8c21:ecff:feac:11c9/64 scope link valid_lft forever preferred_lft forever 4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP link/ether 02:42:f8:19:58:56 brd ff:ff:ff:ff:ff:ff inet 172.30.91.1/24 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:f8ff:fe19:5856/64 scope link valid_lft forever preferred_lft forever 6: veth8fe2e09@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master docker0 state UP link/ether d6:20:44:72:47:39 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::d420:44ff:fe72:4739/64 scope link valid_lft forever preferred_lft forever
Routing Table
Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default gateway 0.0.0.0 UG 0 0 0 eth0 10.4.4.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 168.63.129.16 gateway 255.255.255.255 UGH 0 0 0 eth0 link-local 0.0.0.0 255.255.0.0 U 1002 0 0 eth0 169.254.169.254 gateway 255.255.255.255 UGH 0 0 0 eth0 172.30.0.0 0.0.0.0 255.255.0.0 U 0 0 0 flannel.1 172.30.91.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0
iptablesのチェイン
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER -A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER -A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING -A POSTROUTING -s 172.30.91.0/24 ! -o docker0 -j MASQUERADE -A DOCKER -i docker0 -j RETURN -A KUBE-MARK-DROP -j MARK --set-xmark 0x8000/0x8000 -A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000 -A KUBE-NODEPORTS -p tcp -m comment --comment "default/dev-np:https" -m tcp --dport 31707 -j KUBE-MARK-MASQ -A KUBE-NODEPORTS -p tcp -m comment --comment "default/dev-np:https" -m tcp --dport 31707 -j KUBE-SVC-3UCI24FU4XAF2OPE -A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -m mark --mark 0x4000/0x4000 -j MASQUERADE -A KUBE-SEP-2GFQOP4A6JXPIKZI -s 172.30.31.2/32 -m comment --comment "default/dev-np:https" -j KUBE-MARK-MASQ -A KUBE-SEP-2GFQOP4A6JXPIKZI -p tcp -m comment --comment "default/dev-np:https" -m tcp -j DNAT --to-destination 172.30.31.2:80 -A KUBE-SEP-BDZCPQIUEUTMNSCN -s 10.4.4.4/32 -m comment --comment "default/kubernetes:https" -j KUBE-MARK-MASQ -A KUBE-SEP-BDZCPQIUEUTMNSCN -p tcp -m comment --comment "default/kubernetes:https" -m recent --set --name KUBE-SEP-BDZCPQIUEUTMNSCN --mask 255.255.255.255 --rsource -m tcp -j DNAT --to-destination 10.4.4.4:6443 -A KUBE-SEP-OKBK5A3GPOH3UXPW -s 172.30.91.2/32 -m comment --comment "default/dev-np:https" -j KUBE-MARK-MASQ -A KUBE-SEP-OKBK5A3GPOH3UXPW -p tcp -m comment --comment "default/dev-np:https" -m tcp -j DNAT --to-destination 172.30.91.2:80 -A KUBE-SERVICES -d 10.254.236.167/32 -p tcp -m comment --comment "default/dev-np:https cluster IP" -m tcp --dport 80 -j KUBE-SVC-3UCI24FU4XAF2OPE -A KUBE-SERVICES -d 10.254.0.1/32 -p tcp -m comment --comment "default/kubernetes:https cluster IP" -m tcp --dport 443 -j KUBE-SVC-NPX46M4PTMTKRN6Y -A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS -A KUBE-SVC-3UCI24FU4XAF2OPE -m comment --comment "default/dev-np:https" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-2GFQOP4A6JXPIKZI -A KUBE-SVC-3UCI24FU4XAF2OPE -m comment --comment "default/dev-np:https" -j KUBE-SEP-OKBK5A3GPOH3UXPW -A KUBE-SVC-NPX46M4PTMTKRN6Y -m comment --comment "default/kubernetes:https" -m recent --rcheck --seconds 10800 --reap --name KUBE-SEP-BDZCPQIUEUTMNSCN --mask 255.255.255.255 --rsource -j KUBE-SEP-BDZCPQIUEUTMNSCN -A KUBE-SVC-NPX46M4PTMTKRN6Y -m comment --comment "default/kubernetes:https" -j KUBE-SEP-BDZCPQIUEUTMNSCN
ClusterIP宛の通信がPodに届くまで
ClusterIP宛の通信がPodに届くまでを調べた結果を記載します。node1のPod内のコンテナからClusterIPあて80/tcpにアクセスしたとします。
■ その1
Podのコンテナ内のRouting Tableにより、デフォルトゲートウェイであるdocker0のインターフェースに向かいます。
■ その2
Podのコンテナ内のRouting Tableにより、デフォルトゲートウェイであるdocker0のインターフェースに向かいます。
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
上記のルールによりルーティングされる前にKUBE-SERVICESチェインに飛びます。
-A KUBE-SERVICES -d 10.254.236.167/32 -p tcp -m comment --comment "default/dev-np:https cluster IP" -m tcp --dport 80 -j KUBE-SVC-3UCI24FU4XAF2OPE
KUBE-SERVICESチェインにより、宛先IPアドレスがClusterIP、宛先ポートが80のパケットは、 KUBE-SVC-3UCI24FU4XAF2OPEチェインに飛びます。
-A KUBE-SVC-3UCI24FU4XAF2OPE -m comment --comment "default/dev-np:https" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-2GFQOP4A6JXPIKZI -A KUBE-SVC-3UCI24FU4XAF2OPE -m comment --comment "default/dev-np:https" -j KUBE-SEP-OKBK5A3GPOH3UXPW
KUBE-SVC-3UCI24FU4XAF2OPEチェインにより、1/2の確率でパケットはKUBE-SEP-2GFQOP4A6JXPIKZIチェインか、KUBE-SEP-OKBK5A3GPOH3UXPWチェインに飛びます。ここでは、 KUBE-SEP-OKBK5A3GPOH3UXPWチェインに飛んだとします。
-A KUBE-SEP-OKBK5A3GPOH3UXPW -p tcp -m comment --comment "default/dev-np:https" -m tcp -j DNAT --to-destination 172.30.91.2:80
KUBE-SEP-OKBK5A3GPOH3UXPWチェインにより、パケットの宛先IPアドレスは172.30.91.2、宛先ポートは80になります。
172.30.0.0 0.0.0.0 255.255.0.0 U 0 0 0 flannel.
上記のRouting Tableにより、パケットはflannel.1にルーティングされます。
■ その5
flannel.1はVXLANデバイスなので、172.30.91.0/24宛のパケットは、宛先node02のeth0(10.4.4.7)、プロトコルUDP、ポート番号8472にカプセル化されて転送されます。この転送ルールは、Masterの中にあるetcdに記載されており、flannelがその情報をもとに、このルールを決定します。VXLANの仕組みについては別記事にて記載する予定ですので、ここでは詳細は割愛させて頂きます。
そして、
10.4.4.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
のRouting Tableにより、パケットはnode01のeth0にRoutingされます。
-A POSTROUTING -s 172.30.31.0/24 ! -o docker0 -j MASQUERADE
のiptablesのルールにより、送信元は、node01のeth0(10.4.4.5)にSNATされます。
■ その7
node02は、受け取ったパケットがVXLANのパケットであると判断し、先程カプセル化されたL2パケットを取り出して、flannel.1に転送します。
172.30.91.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0
このRouting Tableによって、パケットはdocker0インタフェースに転送されます。
■ その9
最終的にパケットは、node02のPodに届きます。
nodeのインターフェース宛の通信がPodに届くまで
nodeのインターフェース宛の通信がPodに届くまでを調べました。
■ その1
node01のeth0のインターフェースの31707宛にパケットが飛んできます。
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
PREROUTINGチェインは、KUBE-SERVICESチェインに飛びます。
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS
KUBE-SERVICESでは、ローカルのインターフェース(この場合だとnodeのeth0)宛のパケットは、KUBE-NODEPORTSチェインに飛びます。
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/dev-np:https" -m tcp --dport 31707 -j KUBE-SVC-3UCI24FU4XAF2OPE
KUBE-NODEPORTSチェインでは、31707/tcp宛のパケットは、KUBE-SVC-3UCI24FU4XAF2OPEチェインに飛びます。
-A KUBE-SVC-3UCI24FU4XAF2OPE -m comment --comment "default/dev-np:https" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-2GFQOP4A6JXPIKZI -A KUBE-SVC-3UCI24FU4XAF2OPE -m comment --comment "default/dev-np:https" -j KUBE-SEP-OKBK5A3GPOH3UXPW
KUBE-SVC-3UCI24FU4XAF2OPEチェインでは、statisticモジュールにより、50%の確率でKUBE-SEP-2GFQOP4A6JXPIKZIチェインかKUBE-SEP-OKBK5A3GPOH3UXPWチェインに飛びます。ここではKUBE-SEP-2GFQOP4A6JXPIKZIに飛んだとします。
-A KUBE-SEP-2GFQOP4A6JXPIKZI -p tcp -m comment --comment "default/dev-np:https" -m tcp -j DNAT --to-destination 172.30.31.2:80
宛先が、Pod宛のIPアドレス、ポートにNATされ、Podに転送されることがわかります。
172.30.31.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0
このRouting Tableにより、パケットdocker0インターフェースに届きます。
最後に
いかがでしたでしょうか?iptablesのルールがたくさんあって、目が回りそうになりました。誰かのお役に立てれば幸いです。
大変参考になる記事ありがとうございます。
過去記事へのリンクのURLが誤っているようなので
修正いただいた方がよろしいかと思います。
例)
https://tech-lab.sios.jparchives/7745
ご指摘頂きまして大変ありがとうございました。早速修正させていただきました。
今後とも当社技術ブログを何卒よろしくお願い申し上げます。