死磕k8s之calico-nodeport

Posted K8S中文社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了死磕k8s之calico-nodeport相关的知识,希望对你有一定的参考价值。

序言

本篇文章将要聚焦于k8s在使用calico作为网络插件的时候,当pod以nodeport的形式暴露出来,我们以集群节点的ip加端口的形式访问的时候,流量如何转发到具体的pod,流量经过了哪些路由和哪些iptables链。如集群还没准备好,请参考[死磕k8s之calico-环境准备]。

我的环境

死磕k8s之calico-nodeport

nginx pod信息
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESnginx-6799fc88d8-wfztd 1/1 Running 0 3h32m 192.168.231.70 shen-k8s-node-1 <none> <none>
nodeport信息
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTORnginx   NodePort   10.101.14.7   <none>        8080:32220/TCP   2d1h   app=nginx


注意

接下来的分析主要基于该表的路径

死磕k8s之calico-nodeport


开始发请求到nodeport

我目前nginx的pod在work节点1上面,为了减少分析过程,我在和k8s同网段的其他机器10.0.0.53上面请求work节点1的ip加端口
             
               
               
             
curl 10.0.0.51:32220


到达work节点1

01

首先会到达raw的PREROUTING

包的流向如下



死磕k8s之calico-nodeport



raw的PRESOUTING相对比较简单,在这里没有匹配到任何的规则

02

然后到达mangle的PREROUTING

包的流向如下



死磕k8s之calico-nodeport



对新来的包来说也没有匹配到任何规则,但是第二条规则会对以后的包做一个放行,效率更高

03

然后到了重要表nat的PREROUTING

在PREROUTING一般对包做DNAT操作

包的流量如下:


死磕k8s之calico-nodeport



此表的规则路径稍微复杂一些了,第三条规则匹配到了我们的请求的目标端口32220,注意下第4条规则,会给包打上0x4000/0x4000标签。最后到了第7条做了一个比较重要的dnat操作,此时请求的包的源地址还是10.0.0.53,但是包的目的地址会变成192.168.231.70,并且目标端口也会换成我们暴露的容器端口80。 192.168.231.70 正是pod nginx-6799fc88d8-wfztd 的地址。因为我们这里只有一个pod,所以只会匹配到这一条dnat规则,如果我们有多个pod的话,会按照该规则上的概率随机匹配到一条dnat规则。大家由此就会联想到service的负载均衡策略也可以这么做。

04

然后就开始第一次的路由选择了



死磕k8s之calico-nodeport

上图是work节点1 的路由信息,据上面分析得到包的目的地址是192.168.231.70,此时正好匹配到红色框的路由。而虚拟网卡cali583c2cee4e2是和192.168.231.70这个pod的veth pair,下图可以求证

死磕k8s之calico-nodeport


死磕k8s之calico-nodeport


第一张图是在pod内看到虚拟网络信息,第二张图示pod所在节点的网络接口信息,pod内if41正好对应节点上网卡cali583c2cee4e2@if4,该网卡和上面匹配到的路由一样,所以该包可以顺利到达pod内。

但是此时的源地址还是最初发送请求的地址10.13.0.53,如果以这个包转发到pod内,那么pod的回包就找不到正确发送回来的目的地址了,故接下来肯定会对该包多SNAT,也就是修改源地址。注意此时包还没有发出去,还会继续走其他的表。

05

匹配到了路由信息,此时会走的链是mangle的FORWARD

但该表没有对它作任何操作


06

接下来看看filter表的FORWARD


死磕k8s之calico-nodeport



该链看着挺复杂,其实没有对包作重要的操作。大概说几个比较关键的地方,第2条规则是能匹配到的,因为此时的包经过路由决策之后是需要去cali+开头的网卡。第3条恰恰精确匹配到了包要转发的虚拟网卡。第4条虽然给包打上了0x10000/0x10000,但是走到第8条的时候注意红色框出,这里重新打包为0x4000/0x4000了这里尤其注意了,不然会翻车。

07

然后是mangle的POSTROUTING

然而该表没有任何规则



08

接下来到了nat表的POSTROUTING链

nat表重要的是做了一个重要的操作SNAT


死磕k8s之calico-nodeport



该表重点就是第5条,依照前面分析有个0x400/0x400标记,不然就完了,下面就不会走到第6条去SNAT操作了。
通过抓包显示此时包的目的地址和源地址都被替换了。

死磕k8s之calico-nodeport



09

此时包的路径分析基本已经结束了

因为包已经走到pod里面去了



10

此时我们的pod正好在请求的ip的节点上

如果pod在work节点2上包的走向又会怎么样呢?



那我们改一下该pod的调度策略:
kubectl edit deployment nginx
             
               
               
             
# Please edit the object below. Lines beginning with a '#' will be ignored, # and an empty file will abort the edit. If an error occurs while saving this file will be # reopened with the relevant failures. # apiVersion: apps/v1 kind: Deployment metadata: annotations: deployment.kubernetes.io/revision: "2" creationTimestamp: "2020-11-09T05:56:04Z" generation: 2 labels: app: nginx name: nginx namespace: default resourceVersion: "1086147" selfLink: /apis/apps/v1/namespaces/default/deployments/nginx uid: ec684051-3725-4f5e-9efd-d96fb3257cca spec: progressDeadlineSeconds: 600 replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: app: nginx strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: creationTimestamp: null labels: app: nginx spec: containers: - image: nginx imagePullPolicy: Always name: nginx resources: {} terminationMessagePath: /dev/termination-log terminationMessagePolicy: File dnsPolicy: ClusterFirst nodeSelector: kubernetes.io/hostname: shen-k8s-node-2 #(节点2) restartPolicy: Always schedulerName: default-scheduler securityContext: {} terminationGracePeriodSeconds: 30 status: availableReplicas: 1 conditions: - lastTransitionTime: "2020-11-09T05:56:18Z" lastUpdateTime: "2020-11-09T05:56:18Z" message: Deployment has minimum availability. reason: MinimumReplicasAvailable status: "True" type: Available - lastTransitionTime: "2020-11-09T05:56:04Z" lastUpdateTime: "2020-11-10T14:13:15Z" message: ReplicaSet "nginx-854f5d4bbd" has successfully progressed. reason: NewReplicaSetAvailable status: "True" type: Progressing observedGeneration: 2 readyReplicas: 1 replicas: 1 updatedReplicas: 1

过段时间查看pod状态,最终会调度到work节点2上去

死磕k8s之calico-nodeport


此时的iptables信息和路由信息我就贴到死磕k8s之calico-环境准备去,如果感兴趣可以去那里查看。

11

现在从上面第3条的nat的PREROUTING

开始继续分析


主要是第7条规则有变化,变成了新的pod的ip:192.168.233.136
             
               
               
             
-A KUBE-SEP-PAOOOQUDSCRRYKFR -p tcp -m comment --comment "default/nginx:8080-80" -m tcp -j DNAT --to-destination 192.168.233.136:80


12

然后会经历一波路由策略



死磕k8s之calico-nodeport



和上面的不一样,这里是网段匹配了,会匹配到最后一条路由。该路由的会将包从网关10.0.0.52用网卡tunl0这个网卡转发出去。此时需要注意2点:
  • 网关正好是work节点2上的ip,而pod也被调度到该节点上



13

mangle的FORWARD链没有任何规则

忽略



14

filter的FORWARD链没有重要的规则

但是有一条需要注意,其他的和上面的流程分析一致


             
               
               
             
-A cali-FORWARD -i cali+ -m comment --comment "cali:8ZoYfO5HKXWbB3pk" -j cali-from-wl-dispatch -A cali-FORWARD -o cali+ -m comment --comment "cali:jdEuaPBe14V2hutn" -j cali-to-wl-dispatch
上面2条规则目前来说都匹配不到了,由于上面的路由改变了。

15

mangle的POSTROUTING没有任何规则

故忽略


16

接下来nat的POSTROUTING会做一个SNAT操作


其他的规则和上面的分析一致,但是注意这一条规则
             
               
               
             
-A cali-POSTROUTING -o tunl0 -m comment --comment "cali:JHlpT-eSqR1TvyYm" -m addrtype ! --src-type LOCAL --limit-iface-out -m addrtype --src-type LOCAL -j MASQUERADE
该规则会被匹配到直接做了以work节点1的tunl0的ip为源地址的SNAT操作。此时包为192.168.231.64 -> 192.168.233.136

17

此时包就从tunl0通道留到work节点2的ens192网卡上了

又要走一遍work节点2上的四表五链。


下面是我的抓包信息,可以佐证:

死磕k8s之calico-nodeport


18

raw表的PREROUTING和上面的分析一致

没有操作


mangle表的PREROUTING此时也没有什么操作。nat表的PRESOUTING上上面分析的有差别了,就会匹配到

             
               
               
             
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/nginx:8080-80" -m tcp --dport 32220 -j KUBE-MARK-MASQ -A KUBE-NODEPORTS -p tcp -m comment --comment "default/nginx:8080-80" -m tcp --dport 32220 -j KUBE-SVC-DR2DYVPMBY3GPZ5L
因为此时的端口是80了。

19

然后会做路由决策


此时的路由为:

死磕k8s之calico-nodeport


该路由能精确匹配到最后一条,注意该网卡和pod里面的网卡一一对应,veth pair.
work节点2网卡:

死磕k8s之calico-nodeport


pod的网卡信息:

死磕k8s之calico-nodeport


所以该包会从calib992f6c0b80转发到pod里面去。

20

接下来会走到mangle的FORWARD表,

但是没有规则可匹配


nat的FORWARD也没有什么好分析的,该包也直接进入mangle的POSTROUTING,但是没有规则。然后到了nat的POSTROUTING,此时需要注意一点的是:

-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
            
              
              
            

和前面的分析的差别就在这里,会直接转发到pod里面去了,到此包的旅程就解析完成了。

简单总结

  • 以nodeport的形式的包会被SNAT和DNAT,而且再SNAT之前会被打上很重要的标签0x400/0x400。

  • nodeport的形式的包会走forward链,不会进到INPUT和OUTPUT链。

  • 当nodeport的service的后面有多个终端的时候,也就是多个pod,会按照概率随机走到哪个pod。当流量真正落到的pod的节点不是请求的IP节点的时候,还会通过tunl0这个calico独有的网卡到另外一个节点继续转发。所以当知道pod所在的节点的时候,直接请求该节点的ip,效率会更高,因为会少一些网络的链路操作,当然这种优化个人绝对没有必要。

  • 后续会有其他的情况分析:集群内访问pod的ip,集群内访问service。。。


文章作者:张其
原文链接:
https://blog.csdn.net/u010927340/article/details/109630096
(文章由作者独家投稿

END

Kubernetes CKA实战培训班推荐:


死磕k8s之calico-nodeport

觉得有价值,点个 “在看” ,和朋友们一起成长

以上是关于死磕k8s之calico-nodeport的主要内容,如果未能解决你的问题,请参考以下文章

云原生之:不翻墙,使用最新版本的k8s搭建测试环境

[死磕 Spring 3/43] --- IOC 之 加载 Bean

死磕以太坊源码分析之blockChain分析

死磕算法之汇总篇

死磕Netty源码之Reactor线程模型

死磕Java并:J.U.C之ConcurrentHashMap红黑树转换分析