docker
kubernetes
container
flannel

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

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

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

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

003.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

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

005.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

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

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

008.png

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

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

009.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

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

011.png

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

memo.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

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

013.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

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

memo.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

  • ルーティング後は、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

  • 「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

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

018.png

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

019.png

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

020.png

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

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

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

013.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

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

022.png

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

023.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

  • 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

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

026.png

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

027.png

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

028.png

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

029.png

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

030.png

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

031.png

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

032.png

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

033.png

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

034.png

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

035.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