Kubernetes Network Deep Dive (NodePort, ClusterIP, Flannel)
初めに
Kubernetes には、Kubeadmをはじめとした半自動インストーラーがあり、比較的簡単にインストールを行うことが出来ます。
このままでもKubernetesクラスタを利用することはできますが、本番運用を考えると、Networkの疎通経路を正しく理解する必要があります。
私のローカル環境のKubernetesで検証・確認した内容をいかに共有します。
皆様のお役に立てましたら幸いです。
- Pod同士の通信 (同一マシン)
- Pod同士の通信 (異なるマシン)
- Service(ClusterIP) を経由した通信
- Service(NodePort) を経由した通信
Kubernetesなどのバージョン情報を以下に記載します
- Kubernetes 1.10
- Kubeadm 1.10
- CentOS 7.5
Podの準備
nginx Pod を 4個作成します
以下のマニフェストファイルを準備します
cat <<'EOF' > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 4 # tells deployment to run 2 pods matching the template
template: # create pods using pod definition in this template
metadata:
# unlike pod-nginx.yaml, the name is not included in the meta data as a unique name is
# generated from the deployment name
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
EOF
NW構成図
- Master1台
- Node2台
NW構成の確認コマンド一覧
Masterサーバを使用して、各種ネットワーク要素を確認していきます
MasterもNodeも基本的には違いはありません。
Master01
物理 Interface です
[root@sugi-kubeadm-master01 ~(kubernetes kube-system kubernetes-admin)]# ip -d a show ens192
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:50:56:98:f7:02 brd ff:ff:ff:ff:ff:ff promiscuity 0 numtxqueues 2 numrxqueues 2 gso_max_size 65536 gso_max_segs 65535
inet 192.168.120.225/24 brd 192.168.120.255 scope global noprefixroute ens192
valid_lft forever preferred_lft forever
inet6 fe80::72f:48e1:638c:e8f6/64 scope link tentative dadfailed
valid_lft forever preferred_lft forever
物理 Interface に紐づく、vxlanの Interface が flannel.1 です
4行目あたりに 「dev ens192」と表示されており、紐づけされていることがわかります。
[root@sugi-kubeadm-master01 ~(kubernetes kube-system kubernetes-admin)]# ip -d a show flannel.1
4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether f2:4a:3d:b6:4e:95 brd ff:ff:ff:ff:ff:ff promiscuity 0
vxlan id 1 local 192.168.120.225 dev ens192 srcport 0 0 dstport 8472 nolearning ageing 300 noudpcsum noudp6zerocsumtx noudp6zerocsumrx numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 10.1.4.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::f04a:3dff:feb6:4e95/64 scope link
valid_lft forever preferred_lft forever
次に cni0 です。これは Linux Bridgeです
cni0 という名前の Linix Bridge に、同一物理Nodeで稼働する Pod の veth が接続される形となります
[root@sugi-kubeadm-master01 ~(kubernetes kube-system kubernetes-admin)]# ip -d a show cni0
5: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default qlen 1000
link/ether 0a:58:0a:01:04:01 brd ff:ff:ff:ff:ff:ff promiscuity 0
bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time 30000 stp_state 0 priority 32768 vlan_filtering 0 vlan_protocol 802.1Q bridge_id 8000.a:58:a:1:4:1 designated_root 8000.a:58:a:1:4:1 root_port 0 root_path_cost 0 topology_change 0 topology_change_detected 0 hello_timer 0.00 tcn_timer 0.00 topology_change_timer 0.00 gc_timer 135.01 vlan_default_pvid 1 vlan_stats_enabled 0 group_fwd_mask 0 group_address 01:80:c2:00:00:00 mcast_snooping 1 mcast_router 1 mcast_query_use_ifaddr 0 mcast_querier 0 mcast_hash_elasticity 4 mcast_hash_max 512 mcast_last_member_count 2 mcast_startup_query_count 2 mcast_last_member_interval 100 mcast_membership_interval 26000 mcast_querier_interval 25500 mcast_query_interval 12500 mcast_query_response_interval 1000 mcast_startup_query_interval 3125 mcast_stats_enabled 0 mcast_igmp_version 2 mcast_mld_version 1 nf_call_iptables 0 nf_call_ip6tables 0 nf_call_arptables 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 10.1.4.1/24 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::a0b1:82ff:fe32:b41e/64 scope link
valid_lft forever preferred_lft forever
brctlコマンドで、Linux Bridge の接続を確認することが出来ます
[root@sugi-kubeadm-master01 ~(kubernetes kube-system kubernetes-admin)]# brctl show
bridge name bridge id STP enabled interfaces
cni0 8000.0a580a010401 no veth4a0982ff
docker0 8000.02420aebdea5 no
brctlコマンドを実行するためには、以下のパッケージをinstallします
yum install -y bridge-utils
veth4a0982ff も同様に ip コマンドで確認することが出来ます
[root@sugi-kubeadm-master01 ~(kubernetes kube-system kubernetes-admin)]# ip -d a show veth4a0982ff
6: veth4a0982ff@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
link/ether de:56:b4:74:4e:b1 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 1
veth
bridge_slave state forwarding priority 32 cost 2 hairpin on guard off root_block off fastleave off learning on flood on port_id 0x8001 port_no 0x1 designated_port 32769 designated_cost 0 designated_bridge 8000.a:58:a:1:4:1 designated_root 8000.a:58:a:1:4:1 hold_timer 0.00 message_age_timer 0.00 forward_delay_timer 0.00 topology_change_ack 0 config_pending 0 proxy_arp off proxy_arp_wifi off mcast_router 1 mcast_fast_leave off mcast_flood on numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet6 fe80::dc56:b4ff:fe74:4eb1/64 scope link
valid_lft forever preferred_lft forever
上記の表示結果は「veth4a0982ff@if3」と表示されており、Interface 3 の docker0 と ペアの veth なのか? と思いきや、そうではありません
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:0a:eb:de:a5 brd ff:ff:ff:ff:ff:ff promiscuity 0
bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time 30000 stp_state 0 priority 32768 vlan_filtering 0 vlan_protocol 802.1Q bridge_id 8000.2:42:a:eb:de:a5 designated_root 8000.2:42:a:eb:de:a5 root_port 0 root_path_cost 0 topology_change 0 topology_change_detected 0 hello_timer 0.00 tcn_timer 0.00 topology_change_timer 0.00 gc_timer 148.93 vlan_default_pvid 1 vlan_stats_enabled 0 group_fwd_mask 0 group_address 01:80:c2:00:00:00 mcast_snooping 1 mcast_router 1 mcast_query_use_ifaddr 0 mcast_querier 0 mcast_hash_elasticity 4 mcast_hash_max 512 mcast_last_member_count 2 mcast_startup_query_count 2 mcast_last_member_interval 100 mcast_membership_interval 26000 mcast_querier_interval 25500 mcast_query_interval 12500 mcast_query_response_interval 1000 mcast_startup_query_interval 3125 mcast_stats_enabled 0 mcast_igmp_version 2 mcast_mld_version 1 nf_call_iptables 0 nf_call_ip6tables 0 nf_call_arptables 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
上記の id3 の数字は、コンテナ内の interface 番号に紐づいています
kube-dns のコンテナにsh を起動してログインします
[root@sugi-kubeadm-master01 ~(kubernetes kube-system kubernetes-admin)]# kubectl exec -it kube-dns-86f4d74b45-zspb9 sh
Defaulting container name to kubedns.
Use 'kubectl describe pod/kube-dns-86f4d74b45-zspb9 -n kube-system' to see all of the containers in this pod.
コンテナ上で eth0 を表示すると、3番目の interface となっており、 if 6 の 「veth4a0982ff@if3」とペアになっていることがわかります
/ # ip a show eth0
3: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue state UP
link/ether 0a:58:0a:01:04:02 brd ff:ff:ff:ff:ff:ff
inet 10.1.4.2/24 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::4466:97ff:feff:bdfb/64 scope link tentative flags 08
valid_lft forever preferred_lft forever
Pod同士の通信(同一のNode)
node01で稼働しているPod同士の通信を確認します
同一のnodeで稼働しているPod同士は、必ず同一のセグメントとなります。
そのため、LinuxBridgeの cni0 で折り返して通信を行うことが出来ます
注意点として、Podが通信を行う時には、Serviceを介して通信を行うことが一般的です。LBの結果同一のNodeと通信する場合もあれば、異なるNodeと通信する場合もあります
Pod同士の通信(異なるNode)
node01で稼働しているPodとnode02で稼働しているPodの通信を確認します
Flannelはvxlanでトンネリングを実施しているため、そこに注目して通信を見てみましょう
- node01のPod(10.1.5.4) から node02の Pod (10.1.6.5) へ Pingを行います
- node01のPod(10.1.5.4) から node02の Pod (10.1.6.5)へPingを送付する際に、同一セグメントに居ないため、DefaultGW (10.1.5.1) にパケットを転送します
以下は Pod内のルーティングテーブルを表示しています
- DefaultGW (10.1.5.1)は、Nodeサーバ上の LinuxGridge cni0 インターフェースとなっています
cni0はnode02の Pod (10.1.6.5)へパケット転送を行うため、Node01のルーティングテーブルに従って、flannel.1 へパケットを転送します
このルーティングテーブルはどこから来ているかというと、後述のflannelのサービスが追加をしています
- flannel.1 は、VXLANのEndpoint (VTEP) なので、トンネリングして次の宛先へ転送します。
次の宛先を決めるための情報は、ARPテーブルと、カーネル内部の FDB (Forwarding Database) に既に格納されています。
誰がこの情報を登録しているのかというと、flannelのサービスが、RoutingTable, ARP, FDB にエントリを追加しています
https://github.com/coreos/flannel/blob/master/backend/vxlan/vxlan.go#L39
RoutingTableの確認
10.1.6.0/24 宛のパケットは、flannel.1 デバイスから、10.1.6.0 宛に転送すればよい事がわかります
[root@sugi-kubeadm-node01 ~]# ip r
default via 192.168.120.254 dev ens192 proto static metric 100
10.1.4.0/24 via 10.1.4.0 dev flannel.1 onlink
10.1.5.0/24 dev cni0 proto kernel scope link src 10.1.5.1
10.1.6.0/24 via 10.1.6.0 dev flannel.1 onlink
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.120.0/24 dev ens192 proto kernel scope link src 192.168.120.226 metric 100
ARPテーブルの確認
10.1.6.0のMacAddressは「36:dc:08:bc:b6:23」とすれば良いことが分かります
[root@sugi-kubeadm-node01 ~]# ip n
192.168.120.227 dev ens192 lladdr 00:50:56:98:f6:4b STALE
10.1.5.5 dev cni0 lladdr 0a:58:0a:01:05:05 STALE
192.168.120.252 dev ens192 lladdr 00:50:56:98:d2:4e REACHABLE
192.168.120.254 dev ens192 lladdr 00:50:56:aa:36:d5 DELAY
10.1.5.4 dev cni0 lladdr 0a:58:0a:01:05:04 STALE
10.1.6.0 dev flannel.1 lladdr 36:dc:08:bc:b6:23 PERMANENT
10.1.5.10 dev cni0 lladdr 0a:58:0a:01:05:0a STALE
10.1.4.0 dev flannel.1 lladdr f2:4a:3d:b6:4e:95 PERMANENT
192.168.120.225 dev ens192 lladdr 00:50:56:98:f7:02 REACHABLE
Forwarding DataBase の確認
「36:dc:08:bc:b6:23」は、92.168.120.227宛に送付すれば良いことがわかります。
これらを使用してVXLANパケットを作成します
[root@sugi-kubeadm-node01 ~]# bridge fdb show dev flannel.1
36:dc:08:bc:b6:23 dst 192.168.120.227 self permanent
f2:4a:3d:b6:4e:95 dst 192.168.120.225 self permanent
- ARPとFDBに従って、VXLANでカプセル化されたパケットが、node02の flannel.1 へ到着し、VXLANのカプセル化が解除されます
- ルーティングテーブルに従って、cni0へパケットを転送します
-
cni0 が 対象のPodのMacAddressを学習していない場合は、ARP Request を投げます
-
対象のPodがArp Reply を返し、NWの疎通が出来る
Service(ClusterIP)の通信
ClusterIPの準備
ClusterIPのServiceを作成します
cat <<'EOF' > /root/kube_yaml/201_walkthrough/service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
ports:
- port: 8000 # the port that this service should serve on
# the container on each pod to connect to, can be a name
# (e.g. 'www') or a number (e.g. 80)
targetPort: 80
protocol: TCP
# just like the selector in the deployment,
# but this time it identifies the set of pods to load balance
# traffic to.
selector:
app: nginx
EOF
Serviceを作成します
kubectl apply -f /root/kube_yaml/201_walkthrough/service.yaml
以下の nginx-service が作成されます
[root@sugi-kubeadm-master01 201_walkthrough(kubernetes default kubernetes-admin)]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 10h <none>
nginx-service ClusterIP 10.1.2.120 <none> 8000/TCP 4m app=nginx
上記のServiceは、既存のnginxPodの4個と紐づいています
[root@sugi-kubeadm-master01 201_walkthrough(kubernetes default kubernetes-admin)]# kubectl describe endpoints nginx-service
Name: nginx-service
Namespace: default
Labels: <none>
Annotations: <none>
Subsets:
Addresses: 10.1.5.4,10.1.5.5,10.1.6.5,10.1.6.6
NotReadyAddresses: <none>
Ports:
Name Port Protocol
---- ---- --------
<unset> 80 TCP
Events: <none>
上記のPodにアクセスするため、CentOSのPodを1個作成します
cat <<'EOF' > /root/kube_yaml/201_walkthrough/centos_deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: centos-deployment
spec:
selector:
matchLabels:
app: centos
replicas: 1 # tells deployment to run 2 pods matching the template
template: # create pods using pod definition in this template
metadata:
# unlike pod-nginx.yaml, the name is not included in the meta data as a unique name is
# generated from the deployment name
labels:
app: centos
spec:
containers:
- name: centos
image: centos:latest
command: [ "sleep", "3600000" ]
EOF
kubectl apply -f /root/kube_yaml/201_walkthrough/centos_deployment.yaml
CentOSでcurlのテストを行います
[root@centos-deployment-748dc5bfb9-4jpgx /]# curl http://10.1.2.120:8000/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
ClusterIPの通信の確認
PodからClusterIPを通じて、別のPodへ通信する経路を確認します。
ClusterIPの実体はIPtablesなので、そこに注目しつつ、順番に追っていきたいと思います。
- Pod CentOSから、ClusterIPへ curl を実行します
- ClusterIP は別セグメントなので、DefaultGWのcni0にパケットを転送します
- cni0のインターフェースを持つ Node がパケットを受け取る際に、iptablesのnatテーブルが作用し、DNAT が使用されパケットのDestinationIPが変換されます
IPtableのチェインは、評価される順番があります。パケット受信の際には、natテーブルのPREROUTINGチェインが最初に評価されます
詳細は、http://www.atmarkit.co.jp/ait/articles/1002/09/news119.html
を確認するとわかりやすいです
iptablesコマンドを使用して、PREROUTINGチェインを確認します。なお、この時に -v を入れて詳細を表示すると、ルールに適用されたパケットの数を確認できるため、複雑なiptablesを追いやすくなるのでお勧めです。
in, out が アスタリスクなので全てのインターフェース、 source, destination が 0.0.0.0/0 なのですべての宛先パケットがルールに該当します。
初めに記載されているルールに従って、「KUBE-SERVICES」チェインが評価されます
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL PREROUTING
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
1612 252K KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
487 95515 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
- 「KUBE-SERVICES」チェインを確認します
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SERVICES
Chain KUBE-SERVICES (2 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ udp -- * * !10.1.4.0/22 10.1.0.10 /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
0 0 KUBE-SVC-TCOU7JCQXEZGVUNU udp -- * * 0.0.0.0/0 10.1.0.10 /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
0 0 KUBE-MARK-MASQ tcp -- * * !10.1.4.0/22 10.1.0.10 /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
0 0 KUBE-SVC-ERIFXISQEP7F7OF4 tcp -- * * 0.0.0.0/0 10.1.0.10 /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
0 0 KUBE-MARK-MASQ tcp -- * * !10.1.4.0/22 10.1.0.1 /* default/kubernetes:https cluster IP */ tcp dpt:443
0 0 KUBE-SVC-NPX46M4PTMTKRN6Y tcp -- * * 0.0.0.0/0 10.1.0.1 /* default/kubernetes:https cluster IP */ tcp dpt:443
0 0 KUBE-MARK-MASQ tcp -- * * !10.1.4.0/22 10.1.2.120 /* default/nginx-service: cluster IP */ tcp dpt:8000
0 0 KUBE-SVC-GKN7Y2BSGW4NJTYL tcp -- * * 0.0.0.0/0 10.1.2.120 /* default/nginx-service: cluster IP */ tcp dpt:8000
0 0 KUBE-NODEPORTS all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL
source ip が10.1.5.10 と、dest ipが10.1.2.120、 dest port が 8000 なので、下の方にある以下のルールにマッチします
0 0 KUBE-SVC-GKN7Y2BSGW4NJTYL tcp -- * * 0.0.0.0/0 10.1.2.120 /* default/nginx-service: cluster IP */ tcp dpt:8000
ルールにマッチし、KUBE-SVC-GKN7Y2BSGW4NJTYL チェインが評価されます
- 「KUBE-SVC-GKN7Y2BSGW4NJTYL」チェインを確認します
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SVC-GKN7Y2BSGW4NJTYL
Chain KUBE-SVC-GKN7Y2BSGW4NJTYL (1 references)
pkts bytes target prot opt in out source destination
1 60 KUBE-SEP-E5HSR3R2B7URHFJI all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ statistic mode random probability 0.25000000000
0 0 KUBE-SEP-R2MSMBFMMKEJBLAL all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ statistic mode random probability 0.33332999982
0 0 KUBE-SEP-CAKGWV44IP65RES6 all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ statistic mode random probability 0.50000000000
0 0 KUBE-SEP-2XH37R654LGYZT6Q all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */
上記チェインの末尾の方に「statistic mode random probability 0.XXXX」と記載があります
これは、パケットを評価する際に、指定した確率を使用して、ルールを実行するか否かを指定できます。
上記の例では、一番最初に「probability 0.25000000000」とあるので、25%の確率でルールが実行されます。
なお、iptablesコマンドのoptionにvを付けているので、ルールに該当したパケット数が表示されています。
上記実行例では、pkts列が1となっているルールが、使用されていることが分かります (この値は定期的にクリアされ0に戻ります)
下記URLに statistic についての説明がされています
https://www.sssg.org/blogs/naoya/archives/1695
- 一番はじめの「KUBE-SEP-E5HSR3R2B7URHFJI」チェインを確認します
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SEP-E5HSR3R2B7URHFJI
Chain KUBE-SEP-E5HSR3R2B7URHFJI (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.1.5.4 0.0.0.0/0 /* default/nginx-service: */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp to:10.1.5.4:80
一番はじめの「KUBE-MARK-MASQ」のルールは、よくわかりません。sourceの「10.1.5.4」は、nginx Pod のうちの1個ですが、なぜこれをMark付けるのかが理解できていません。
次のルールは、DNATをまさに実行している箇所となります。パケットのDestinationを「10.1.5.4:80」と変更しています。
これは、nginx Podのうちの1個のIPアドレスを指し示しています。
同様に他のルールもDNATを実施しています。
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SEP-R2MSMBFMMKEJBLAL
Chain KUBE-SEP-R2MSMBFMMKEJBLAL (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.1.5.5 0.0.0.0/0 /* default/nginx-service: */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp to:10.1.5.5:80
[root@sugi-kubeadm-node01 iptables]#
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SEP-CAKGWV44IP65RES6
Chain KUBE-SEP-CAKGWV44IP65RES6 (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.1.6.5 0.0.0.0/0 /* default/nginx-service: */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp to:10.1.6.5:80
[root@sugi-kubeadm-node01 iptables]#
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SEP-2XH37R654LGYZT6Q
Chain KUBE-SEP-2XH37R654LGYZT6Q (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.1.6.6 0.0.0.0/0 /* default/nginx-service: */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp to:10.1.6.6:80
これ以降の通信は、上の方で説明した「Pod同士の通信(同一のNode)」や「Pod同士の通信(異なるのNode)」と同じ通信となるため、省略します
Service(NodePort)の通信
NodePortの準備
上の章で作成したClusterIPは一度削除します
[root@sugi-kubeadm-master01 ~(kubernetes default kubernetes-admin)]# kubectl delete svc nginx-service
service "nginx-service" deleted
NodePortを定義するために Manifest ファイルを作成します
cat <<'EOF' > /root/kube_yaml/201_walkthrough/nodeport-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
ports:
- port: 8000
targetPort: 80
nodePort: 30080
protocol: TCP
selector:
app: nginx
EOF
NodePortを作成します
kubectl apply -f /root/kube_yaml/201_walkthrough/nodeport-service.yaml
作成されたことを確認します
[root@sugi-kubeadm-master01 ~(kubernetes default kubernetes-admin)]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 13h <none>
nginx-service NodePort 10.1.0.125 <none> 8000:30080/TCP 7s app=nginx
クラスタ外のサーバからNodePortへ接続出来ることを確認します
[root@sugi-bastion-centos ~]# curl http://192.168.120.2266:30080/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
NodePort通信の確認(同一のNode)
NodePortの通信経路を確認します。IPtablesが複雑なので、特にIPtablesに注目して通信経路を確認していきましょう。
- Kubernetesクラスタ外のマシンから、Node01のNodePortへアクセスします
- Node01のens192がパケットを受信することで、iptablesのNATテーブルが作用します。「PREROUTING」チェインが初めに実行されます
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL PREROUTING
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
1669 262K KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
498 96593 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
- 「KUBE-SERVICES」が実行されるので、確認します
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SERVICES
Chain KUBE-SERVICES (2 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ tcp -- * * !10.1.4.0/22 10.1.0.125 /* default/nginx-service: cluster IP */ tcp dpt:8000
0 0 KUBE-SVC-GKN7Y2BSGW4NJTYL tcp -- * * 0.0.0.0/0 10.1.0.125 /* default/nginx-service: cluster IP */ tcp dpt:8000
0 0 KUBE-MARK-MASQ udp -- * * !10.1.4.0/22 10.1.0.10 /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
0 0 KUBE-SVC-TCOU7JCQXEZGVUNU udp -- * * 0.0.0.0/0 10.1.0.10 /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
0 0 KUBE-MARK-MASQ tcp -- * * !10.1.4.0/22 10.1.0.10 /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
0 0 KUBE-SVC-ERIFXISQEP7F7OF4 tcp -- * * 0.0.0.0/0 10.1.0.10 /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
0 0 KUBE-MARK-MASQ tcp -- * * !10.1.4.0/22 10.1.0.1 /* default/kubernetes:https cluster IP */ tcp dpt:443
0 0 KUBE-SVC-NPX46M4PTMTKRN6Y tcp -- * * 0.0.0.0/0 10.1.0.1 /* default/kubernetes:https cluster IP */ tcp dpt:443
0 0 KUBE-NODEPORTS all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL
ClusterIPとNodePortのSVCが混在しているチェインとなっています。
コメントに記載されている通り、NodePortはこのチェインの一番最後に評価される必要があるようです。
DestIPが「192.168.120.226 」なので、「KUBE-NODEPORTS」のルールのみ実行されます。
- 「KUBE-NODEPORTS」を確認します
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-NODEPORTS
Chain KUBE-NODEPORTS (1 references)
pkts bytes target prot opt in out source destination
1 60 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp dpt:30080
1 60 KUBE-SVC-GKN7Y2BSGW4NJTYL tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp dpt:30080
ClusterIPとは違い、「KUBE-MARK-MASQ」の行が実行されます。
- 「KUBE-MARK-MASQ」を確認します
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-MARK-MASQ
Chain KUBE-MARK-MASQ (12 references)
pkts bytes target prot opt in out source destination
0 0 MARK all -- * * 0.0.0.0/0 0.0.0.0/0 MARK or 0x4000
iptables-save で確認すると、下記行が該当します
# iptables-save | less
snip
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000
snip
該当のパケットにMark「0x4000」を付けています。このMarkは、この後に続くiptableの処理の中で、SNATの条件として使用するマークとなります
- 「KUBE-MARK-MASQ」から戻り、「KUBE-SVC-GKN7Y2BSGW4NJTYL」ルールに該当するため、これを確認します
ClusterIPでもありましたが、probabilityで指定した確率を使用して、4つのルールから1個が選ばれます
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SVC-GKN7Y2BSGW4NJTYL
Chain KUBE-SVC-GKN7Y2BSGW4NJTYL (2 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-SEP-E5HSR3R2B7URHFJI all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ statistic mode random probability 0.25000000000
1 60 KUBE-SEP-R2MSMBFMMKEJBLAL all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ statistic mode random probability 0.33332999982
0 0 KUBE-SEP-CAKGWV44IP65RES6 all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ statistic mode random probability 0.50000000000
0 0 KUBE-SEP-2XH37R654LGYZT6Q all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */
- それぞれ確認すると、DNATのルールを確認出来ます
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SEP-E5HSR3R2B7URHFJI
Chain KUBE-SEP-E5HSR3R2B7URHFJI (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.1.5.4 0.0.0.0/0 /* default/nginx-service: */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp to:10.1.5.4:80
[root@sugi-kubeadm-node01 iptables]#
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SEP-R2MSMBFMMKEJBLAL
Chain KUBE-SEP-R2MSMBFMMKEJBLAL (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.1.5.5 0.0.0.0/0 /* default/nginx-service: */
1 60 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp to:10.1.5.5:80
[root@sugi-kubeadm-node01 iptables]#
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SEP-CAKGWV44IP65RES6
Chain KUBE-SEP-CAKGWV44IP65RES6 (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.1.6.5 0.0.0.0/0 /* default/nginx-service: */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp to:10.1.6.5:80
[root@sugi-kubeadm-node01 iptables]#
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SEP-2XH37R654LGYZT6Q
Chain KUBE-SEP-2XH37R654LGYZT6Q (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.1.6.6 0.0.0.0/0 /* default/nginx-service: */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp to:10.1.6.6:80
sourceが「192.168.120.252」のため、「KUBE-MARK-MASQ」チェインには行かず、DNATチェインが使用されています。
DNATの結果、同一のNodeで稼働するPodへDNATされた場合を確認します。IPアドレスは「10.1.5.4」です
- 下記のパケット受信の表か順を確認すると、「PREROUTING」の次は、ルーティングが実行されます。
Node01のルーティングでテーブルを確認すると、下記の内容となっており、10.1.5.4 宛のパケットは、cni0へ転送されます
[root@sugi-kubeadm-node01 ~]# ip r
default via 192.168.120.254 dev ens192 proto static metric 100
10.1.4.0/24 via 10.1.4.0 dev flannel.1 onlink
10.1.5.0/24 dev cni0 proto kernel scope link src 10.1.5.1
10.1.6.0/24 via 10.1.6.0 dev flannel.1 onlink
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.120.0/24 dev ens192 proto kernel scope link src 192.168.120.226 metric 100
- ルーティング後は、cni0 はさらに Podへパケットを転送するので、iptablesのFilterテーブルにある「FORWARD」チェインが評価されます。
「FORWARD」チェインを確認します
[root@sugi-kubeadm-node01 ~]# iptables -t filter -vnL FORWARD
Chain FORWARD (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
78372 55M KUBE-FORWARD all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes forwarding rules */
793 57969 DOCKER-ISOLATION all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
0 0 ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
793 57969 ACCEPT all -- * * 10.1.4.0/22 0.0.0.0/0
0 0 ACCEPT all -- * * 0.0.0.0/0 10.1.4.0/22
- 「KUBE-FORWARD」チェインが評価されるので、こちらを確認します
[root@sugi-kubeadm-node01 ~]# iptables -t filter -vnL KUBE-FORWARD
Chain KUBE-FORWARD (1 references)
pkts bytes target prot opt in out source destination
1 60 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes forwarding rules */ mark match 0x4000/0x4000
5 1117 ACCEPT all -- * * 10.1.4.0/22 0.0.0.0/0 /* kubernetes forwarding conntrack pod source rule */ ctstate RELATED,ESTABLISHED
6 397 ACCEPT all -- * * 0.0.0.0/0 10.1.4.0/22 /* kubernetes forwarding conntrack pod destination rule */ ctstate RELATED,ESTABLISHED
「PREROUTING」チェインで受信したパケットに mark 0x4000 が付いているため、 ACCEPT されます
- 「KUBE-FORWARD」チェインの次は nat テーブルの「POSTROUTING」チェインが評価されます。確認します
[root@sugi-kubeadm-node01 ~]# iptables -t nat -vnL POSTROUTING
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
34849 2894K KUBE-POSTROUTING all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes postrouting rules */
0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
482 36384 RETURN all -- * * 10.1.4.0/22 10.1.4.0/22
121 7396 MASQUERADE all -- * * 10.1.4.0/22 !224.0.0.0/4
0 0 RETURN all -- * * !10.1.4.0/22 10.1.5.0/24
0 0 MASQUERADE all -- * * !10.1.4.0/22 10.1.4.0/22
- 「KUBE-POSTROUTING」チェインに該当するため、こちらを確認します
[root@sugi-kubeadm-node01 ~]# iptables -t nat -vnL KUBE-POSTROUTING
Chain KUBE-POSTROUTING (1 references)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */ mark match 0x4000/0x4000
パケットに mark 0x4000 が付与されているため、MASQUERADEが実行されます。
MASQUERADEは、該当のインターフェースのIPアドレスでSNATする動作となります。
このパケットは現在、cni0に居るため、cni0のIPアドレス(10.1.5.1)でSNATされることとなります。
- Podのアプリケーションで処理されたあと、Podはcni0へパケットを返します
- cni0インターフェースでsnatを実施したため、パケットのDestinationIPを基に戻します。
その後、ルーティングテーブルに従って、ens192へパケットを転送します
- ens192インターフェースでdnatを実施したため、パケットのSourceIPを基に戻します。その後、NodePortへrequestをしたclientにパケットが返ります
NodePort通信の確認(異なるNode)
NodePortでDNATされた結果、異なるNodeへ転送する場合の経路を確認しましょう。
同一のNodeへ転送する場合と異なり、SNATするインターフェースが別になっています。
- Kubernetes外部のマシンから、Node01のNodePortへアクセスします
- Node01のens192がパケットを受信することで、iptablesのNATテーブルが作用します。「PREROUTING」チェインが初めに実行されます
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL PREROUTING
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
1669 262K KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
498 96593 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
- 「KUBE-SERVICES」が実行されるので、確認します
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SERVICES
Chain KUBE-SERVICES (2 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ tcp -- * * !10.1.4.0/22 10.1.0.125 /* default/nginx-service: cluster IP */ tcp dpt:8000
0 0 KUBE-SVC-GKN7Y2BSGW4NJTYL tcp -- * * 0.0.0.0/0 10.1.0.125 /* default/nginx-service: cluster IP */ tcp dpt:8000
0 0 KUBE-MARK-MASQ udp -- * * !10.1.4.0/22 10.1.0.10 /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
0 0 KUBE-SVC-TCOU7JCQXEZGVUNU udp -- * * 0.0.0.0/0 10.1.0.10 /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
0 0 KUBE-MARK-MASQ tcp -- * * !10.1.4.0/22 10.1.0.10 /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
0 0 KUBE-SVC-ERIFXISQEP7F7OF4 tcp -- * * 0.0.0.0/0 10.1.0.10 /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
0 0 KUBE-MARK-MASQ tcp -- * * !10.1.4.0/22 10.1.0.1 /* default/kubernetes:https cluster IP */ tcp dpt:443
0 0 KUBE-SVC-NPX46M4PTMTKRN6Y tcp -- * * 0.0.0.0/0 10.1.0.1 /* default/kubernetes:https cluster IP */ tcp dpt:443
0 0 KUBE-NODEPORTS all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL
ClusterIPとNodePortのSVCが混在しているチェインとなっています。
コメントに記載されている通り、NodePortはこのチェインの一番最後に評価される必要があるようです。
DestIPが「192.168.120.226 」なので、「KUBE-NODEPORTS」のルールのみ実行されます。
- 「KUBE-NODEPORTS」を確認します
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-NODEPORTS
Chain KUBE-NODEPORTS (1 references)
pkts bytes target prot opt in out source destination
1 60 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp dpt:30080
1 60 KUBE-SVC-GKN7Y2BSGW4NJTYL tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp dpt:30080
ClusterIPとは違い、「KUBE-MARK-MASQ」の行が実行されます。
- 「KUBE-MARK-MASQ」を確認します
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-MARK-MASQ
Chain KUBE-MARK-MASQ (12 references)
pkts bytes target prot opt in out source destination
0 0 MARK all -- * * 0.0.0.0/0 0.0.0.0/0 MARK or 0x4000
iptables-save で確認すると、下記行が該当します
# iptables-save | less
snip
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000
snip
該当のパケットにMark「0x4000」を付けています。これは、後のiptablesのSNATの処理に使用する条件となります。
- 「KUBE-MARK-MASQ」から戻り、「KUBE-SVC-GKN7Y2BSGW4NJTYL」ルールに該当するため、これを確認します
ClusterIPでもありましたが、probabilityで指定した確率を使用して、4つのルールから1個が選ばれます
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SVC-GKN7Y2BSGW4NJTYL
Chain KUBE-SVC-GKN7Y2BSGW4NJTYL (2 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-SEP-E5HSR3R2B7URHFJI all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ statistic mode random probability 0.25000000000
1 60 KUBE-SEP-R2MSMBFMMKEJBLAL all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ statistic mode random probability 0.33332999982
0 0 KUBE-SEP-CAKGWV44IP65RES6 all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ statistic mode random probability 0.50000000000
0 0 KUBE-SEP-2XH37R654LGYZT6Q all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */
- それぞれ確認すると、DNATのルールを確認出来ます
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SEP-E5HSR3R2B7URHFJI
Chain KUBE-SEP-E5HSR3R2B7URHFJI (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.1.5.4 0.0.0.0/0 /* default/nginx-service: */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp to:10.1.5.4:80
[root@sugi-kubeadm-node01 iptables]#
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SEP-R2MSMBFMMKEJBLAL
Chain KUBE-SEP-R2MSMBFMMKEJBLAL (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.1.5.5 0.0.0.0/0 /* default/nginx-service: */
1 60 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp to:10.1.5.5:80
[root@sugi-kubeadm-node01 iptables]#
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SEP-CAKGWV44IP65RES6
Chain KUBE-SEP-CAKGWV44IP65RES6 (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.1.6.5 0.0.0.0/0 /* default/nginx-service: */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp to:10.1.6.5:80
[root@sugi-kubeadm-node01 iptables]#
[root@sugi-kubeadm-node01 iptables]# iptables -t nat -vnL KUBE-SEP-2XH37R654LGYZT6Q
Chain KUBE-SEP-2XH37R654LGYZT6Q (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.1.6.6 0.0.0.0/0 /* default/nginx-service: */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp to:10.1.6.6:80
sourceが「192.168.120.252」のため、「KUBE-MARK-MASQ」チェインには行かず、DNATチェインが使用されています。
DNATの結果、別のNodeで稼働するPodへの通信経路を確認したいため、DestinationIPを「10.1.6.5」と変換したパケットを追っていきます。
- 「PREROUTING」チェインが終わった後は、ルーティングが実行する流れとなります。
DestIP 「10.1.6.5」のパケットは、ルーティングテーブルに従い、flannel.1 へ転送されます
- ルーティング後は、flannel.1 はさらに Podへパケットを転送するので、iptablesのFilterテーブルにある「FORWARD」チェインが評価されます。
パケットに Mark 0x4000 が付与されているため、ACCEPTされます。
- 次は、NATテーブルの「POSTROUTING」が評価されます
[root@sugi-kubeadm-node01 ~]# iptables -t nat -vnL POSTROUTING
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
42559 3533K KUBE-POSTROUTING all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes postrouting rules */
0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
482 36384 RETURN all -- * * 10.1.4.0/22 10.1.4.0/22
121 7396 MASQUERADE all -- * * 10.1.4.0/22 !224.0.0.0/4
0 0 RETURN all -- * * !10.1.4.0/22 10.1.5.0/24
0 0 MASQUERADE all -- * * !10.1.4.0/22 10.1.4.0/22
「KUBE-POSTROUTING」チェインのみが該当します
-「KUBE-POSTROUTING」を確認します
[root@sugi-kubeadm-node01 ~]# iptables -t nat -vnL KUBE-POSTROUTING
Chain KUBE-POSTROUTING (1 references)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */ mark match 0x4000/0x4000
パケットにMark 0x4000 が付与されており、このルールにパケットは当てはまります。
targetが「MASQUERADE」となっているため、該当のインターフェースのIPアドレスを使用してSNATされます。
flannel.1インターフェースにいるため、パケットのSourceIPは、flannel.1インターフェースのIPアドレス(10.1.5.0 )となります
- SNAT後、ARP, RoutingTable, FDB(Forwarding DataBase) を参照し、別のNodeへ 転送することとなります。flannel.1でVXLANとしてカプセル化します
RoutingTableの確認
10.1.6.0/24 宛のパケットは、flannel.1 デバイスから、10.1.6.0 宛に転送すればよい事がわかります
[root@sugi-kubeadm-node01 ~]# ip r
default via 192.168.120.254 dev ens192 proto static metric 100
10.1.4.0/24 via 10.1.4.0 dev flannel.1 onlink
10.1.5.0/24 dev cni0 proto kernel scope link src 10.1.5.1
10.1.6.0/24 via 10.1.6.0 dev flannel.1 onlink
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.120.0/24 dev ens192 proto kernel scope link src 192.168.120.226 metric 100
ARPテーブルの確認
10.1.6.0のMacAddressは「36:dc:08:bc:b6:23」とすれば良いことが分かります
[root@sugi-kubeadm-node01 ~]# ip n
192.168.120.227 dev ens192 lladdr 00:50:56:98:f6:4b STALE
10.1.5.5 dev cni0 lladdr 0a:58:0a:01:05:05 STALE
192.168.120.252 dev ens192 lladdr 00:50:56:98:d2:4e REACHABLE
192.168.120.254 dev ens192 lladdr 00:50:56:aa:36:d5 DELAY
10.1.5.4 dev cni0 lladdr 0a:58:0a:01:05:04 STALE
10.1.6.0 dev flannel.1 lladdr 36:dc:08:bc:b6:23 PERMANENT
10.1.5.10 dev cni0 lladdr 0a:58:0a:01:05:0a STALE
10.1.4.0 dev flannel.1 lladdr f2:4a:3d:b6:4e:95 PERMANENT
192.168.120.225 dev ens192 lladdr 00:50:56:98:f7:02 REACHABLE
FDB の確認
「36:dc:08:bc:b6:23」は、92.168.120.227宛に送付すれば良いことがわかります。
これらを使用してVXLANパケットを作成します
[root@sugi-kubeadm-node01 ~]# bridge fdb show dev flannel.1
36:dc:08:bc:b6:23 dst 192.168.120.227 self permanent
f2:4a:3d:b6:4e:95 dst 192.168.120.225 self permanent
- Node02のflannel.1でVXLANのカプセル化を解除します
- ルーティングテーブルに従ってパケットをcni0へ転送します・
- cni0 が PodのMacAddressを学習していない場合は、ARP Request を投げます。その後、Podへパケットを転送します
- Podは、別セグメントへパケットをReplyするために、DefaultGW(10.1.6.1) を転送します
- cni0 インターフェースはルーティングテーブルに従って、flannel.1 へ転送します
- RoutingTable, ARP, FDB を参照して、flannel.1は VXLANでカプセル化の後、node01へ転送します
- VXLANのパケットを、カプセル化を解除します
- パケット受信時にSNATを行ったため、DestIPを基に戻します
- ルーティングテーブルに従って、ens192へ転送します
- パケット受信時にDNATを実施したため、SourceIPをもとにもどします。その後、Request元へパケットを返します
memo IPtablesの確認方法
ServiceのClusterIPを作成する前に、各サーバの iptables を file として保管しておきます
以下コマンドを全サーバで実行します
mkdir /root/iptables/
iptables -t nat -vnL > /root/iptables/001_init_save.txt
iptables-save > /root/iptables/001_init_cmd.txt
ClusterIPのServiceを作成します
cat <<'EOF' > /root/kube_yaml/201_walkthrough/service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
ports:
- port: 8000 # the port that this service should serve on
# the container on each pod to connect to, can be a name
# (e.g. 'www') or a number (e.g. 80)
targetPort: 80
protocol: TCP
# just like the selector in the deployment,
# but this time it identifies the set of pods to load balance
# traffic to.
selector:
app: nginx
EOF
Serviceを作成します
kubectl apply -f /root/kube_yaml/201_walkthrough/service.yaml
以下の nginx-service が作成されます
[root@sugi-kubeadm-master01 201_walkthrough(kubernetes default kubernetes-admin)]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 10h <none>
nginx-service ClusterIP 10.1.2.120 <none> 8000/TCP 4m app=nginx
iptablesをfile化します
iptables -t nat -vnL > /root/iptables/002_clusterip_save.txt
iptables-save > /root/iptables/002_clusterip_cmd.txt
比較してみます
diff -u /root/iptables/001_init_cmd.txt /root/iptables/002_clusterip_cmd.txt
参考URL
IPtableの良い解説
IPtablesのランダム DNAT について解説