Kubernetes Network Deep Dive (NodePort, ClusterIP, Flannel)

# 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台


![001.png](https://qiita-image-store.s3.amazonaws.com/0/104247/2e3fd924-b584-efcc-a18c-bc5a1510ed22.png)





# 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と通信する場合もあります**




![002.png](https://qiita-image-store.s3.amazonaws.com/0/104247/0c943bdb-0538-776a-0f60-78b9bd194c55.png)






# Pod同士の通信(異なるNode)

node01で稼働しているPodとnode02で稼働しているPodの通信を確認します
Flannelはvxlanでトンネリングを実施しているため、そこに注目して通信を見てみましょう



![003.png](https://qiita-image-store.s3.amazonaws.com/0/104247/8af36ed9-6bfc-f806-42fa-6091b9a680d6.png)








- 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内のルーティングテーブルを表示しています



![004.png](https://qiita-image-store.s3.amazonaws.com/0/104247/43292391-c661-0c24-8e2b-9a18f86f5a8d.png)







- DefaultGW (10.1.5.1)は、Nodeサーバ上の LinuxGridge cni0 インターフェースとなっています
   cni0はnode02の Pod (10.1.6.5)へパケット転送を行うため、Node01のルーティングテーブルに従って、flannel.1 へパケットを転送します
   このルーティングテーブルはどこから来ているかというと、後述のflannelのサービスが追加をしています



![005.png](https://qiita-image-store.s3.amazonaws.com/0/104247/d5b048f2-99f3-5126-1ca7-63a7127b1986.png)






- 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



![006.png](https://qiita-image-store.s3.amazonaws.com/0/104247/42c9c86e-ad14-a50e-bbd2-15576476d76c.png)


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のカプセル化が解除されます


![007.png](https://qiita-image-store.s3.amazonaws.com/0/104247/da4c5a27-4068-f27b-c07a-51f8c7823849.png)






- ルーティングテーブルに従って、cni0へパケットを転送します

![008.png](https://qiita-image-store.s3.amazonaws.com/0/104247/8a63c63c-7908-eb94-b276-4ebc85e78e01.png)






- cni0 が 対象のPodのMacAddressを学習していない場合は、ARP Request を投げます

- 対象のPodがArp Reply を返し、NWの疎通が出来る

![009.png](https://qiita-image-store.s3.amazonaws.com/0/104247/60e1b138-168a-bbf4-9b70-7709b4635b09.png)







# 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 を実行します

![010.png](https://qiita-image-store.s3.amazonaws.com/0/104247/5f700baf-4a09-02d6-60cc-2eb8e5f6d57b.png)





- ClusterIP は別セグメントなので、DefaultGWのcni0にパケットを転送します


![011.png](https://qiita-image-store.s3.amazonaws.com/0/104247/6ae7010f-df3e-02c0-6810-5a2803921d5c.png)





- cni0のインターフェースを持つ Node がパケットを受け取る際に、iptablesのnatテーブルが作用し、DNAT が使用されパケットのDestinationIPが変換されます
   IPtableのチェインは、評価される順番があります。パケット受信の際には、natテーブルのPREROUTINGチェインが最初に評価されます

![memo.png](https://qiita-image-store.s3.amazonaws.com/0/104247/d8de2ee5-2efa-b64b-b06d-217214d5d5d1.png)

詳細は、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
```



![012.png](https://qiita-image-store.s3.amazonaws.com/0/104247/f890be2b-badf-980e-801b-2431c87107c1.png)




これ以降の通信は、上の方で説明した「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.226６: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へアクセスします

![013.png](https://qiita-image-store.s3.amazonaws.com/0/104247/e91a6466-1787-a117-52e7-09d94ed83145.png)






- 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」です

![014.png](https://qiita-image-store.s3.amazonaws.com/0/104247/1336f4db-7187-777e-ab68-adb46cf344af.png)










- 下記のパケット受信の表か順を確認すると、「PREROUTING」の次は、ルーティングが実行されます。

![memo.png](https://qiita-image-store.s3.amazonaws.com/0/104247/80289e2b-efc9-2f95-3dee-3a20866cddc5.png)


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 
```

![015.png](https://qiita-image-store.s3.amazonaws.com/0/104247/6da8ad09-17a8-9cf7-20c5-d7a310ab0313.png)









- ルーティング後は、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 されます

![016.png](https://qiita-image-store.s3.amazonaws.com/0/104247/f34986fc-037e-dba3-2d99-b9a356210ed3.png)








- 「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されることとなります。

![017.png](https://qiita-image-store.s3.amazonaws.com/0/104247/79998cc5-c0f3-1e5e-cc30-74c4179040f2.png)







- Podのアプリケーションで処理されたあと、Podはcni0へパケットを返します

![018.png](https://qiita-image-store.s3.amazonaws.com/0/104247/0fea80dc-751d-5e99-0923-c1d76dac2424.png)





- cni0インターフェースでsnatを実施したため、パケットのDestinationIPを基に戻します。
    その後、ルーティングテーブルに従って、ens192へパケットを転送します

![019.png](https://qiita-image-store.s3.amazonaws.com/0/104247/59fac3e2-b9b8-b429-2148-eec5a0779904.png)







- ens192インターフェースでdnatを実施したため、パケットのSourceIPを基に戻します。その後、NodePortへrequestをしたclientにパケットが返ります

![020.png](https://qiita-image-store.s3.amazonaws.com/0/104247/2fff93b9-3b0c-e1b4-e689-72502b3546e6.png)









## NodePort通信の確認(異なるNode)

NodePortでDNATされた結果、異なるNodeへ転送する場合の経路を確認しましょう。
同一のNodeへ転送する場合と異なり、SNATするインターフェースが別になっています。





- Kubernetes外部のマシンから、Node01のNodePortへアクセスします

![013.png](https://qiita-image-store.s3.amazonaws.com/0/104247/7e82bac9-a89b-5121-caed-5d8f67b999ef.png)







- 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」と変換したパケットを追っていきます。

![021.png](https://qiita-image-store.s3.amazonaws.com/0/104247/d46324c8-2c1c-dd74-4d3b-e3dd432a62b8.png)





- 「PREROUTING」チェインが終わった後は、ルーティングが実行する流れとなります。
   DestIP 「10.1.6.5」のパケットは、ルーティングテーブルに従い、flannel.1 へ転送されます

![022.png](https://qiita-image-store.s3.amazonaws.com/0/104247/d859f4cf-6fde-4f56-0290-37b82335c8ca.png)



- ルーティング後は、flannel.1 はさらに Podへパケットを転送するので、iptablesのFilterテーブルにある「FORWARD」チェインが評価されます。
   パケットに Mark 0x4000 が付与されているため、ACCEPTされます。

![023.png](https://qiita-image-store.s3.amazonaws.com/0/104247/4f097145-40d9-9e3c-5e1a-6eed43ddb27b.png)





- 次は、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 )となります

![024.png](https://qiita-image-store.s3.amazonaws.com/0/104247/fbbb56ef-51df-3091-5865-cd1c74a4d0e1.png)



- 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
```

![025.png](https://qiita-image-store.s3.amazonaws.com/0/104247/1f5d60fc-e336-45a4-88b2-3f432da8e929.png)







- Node02のflannel.1でVXLANのカプセル化を解除します

![026.png](https://qiita-image-store.s3.amazonaws.com/0/104247/f2878259-db41-1cc2-3b6d-5a06aa5bebc4.png)





- ルーティングテーブルに従ってパケットをcni0へ転送します・

![027.png](https://qiita-image-store.s3.amazonaws.com/0/104247/9e538ae9-dacc-74b9-9c1f-ed09841e3be0.png)







- cni0 が PodのMacAddressを学習していない場合は、ARP Request を投げます。その後、Podへパケットを転送します

![028.png](https://qiita-image-store.s3.amazonaws.com/0/104247/53c712a3-890e-beb8-c951-22fa85359dd4.png)





- Podは、別セグメントへパケットをReplyするために、DefaultGW(10.1.6.1) を転送します

![029.png](https://qiita-image-store.s3.amazonaws.com/0/104247/e122ede2-fc23-572c-a9dc-afc23224e293.png)







- cni0 インターフェースはルーティングテーブルに従って、flannel.1 へ転送します

![030.png](https://qiita-image-store.s3.amazonaws.com/0/104247/0a5ff41e-8300-4f58-66b1-5f0a9e67e46d.png)





- RoutingTable, ARP, FDB を参照して、flannel.1は VXLANでカプセル化の後、node01へ転送します

![031.png](https://qiita-image-store.s3.amazonaws.com/0/104247/b9c5fa3a-8935-fb0b-49c8-d07bc10f6362.png)





- VXLANのパケットを、カプセル化を解除します

![032.png](https://qiita-image-store.s3.amazonaws.com/0/104247/a1bb3bfd-bb3e-1bc7-4add-9e28342b4e81.png)







- パケット受信時にSNATを行ったため、DestIPを基に戻します

![033.png](https://qiita-image-store.s3.amazonaws.com/0/104247/0c17fb88-787e-2b9b-33ba-4a4879ef1835.png)



- ルーティングテーブルに従って、ens192へ転送します

![034.png](https://qiita-image-store.s3.amazonaws.com/0/104247/ea909806-cf83-7dc0-87c4-240f7a74f4e5.png)





- パケット受信時にDNATを実施したため、SourceIPをもとにもどします。その後、Request元へパケットを返します



![035.png](https://qiita-image-store.s3.amazonaws.com/0/104247/c777cd8f-0da7-a517-205a-5a2545e66da9.png)









# 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

http://enakai00.hatenablog.com/entry/2015/04/02/173739

https://www.slideshare.net/hichihara/container-networking-deep-dive-94307233

https://speakerdeck.com/hhiroshell/kubernetes-network-deep-dive/?slide=13

https://tech-lab.sios.jp/archives/7857

https://github.com/coreos/flannel/blob/master/backend/vxlan/vxlan.go#L39



IPtableの良い解説

http://www.atmarkit.co.jp/ait/articles/1002/09/news119.html



IPtablesのランダム DNAT について解説

https://www.sssg.org/blogs/naoya/archives/1695