LoginSignup
53
41

More than 5 years have passed since last update.

Kubernetes Network Deep Dive (NodePort, ClusterIP, Flannel)

Last updated at Posted at 2018-06-10

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

IPtableの良い解説

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

53
41
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
53
41