作者: 郑明泉、余凯

为啥争吵,吵什么?

"你到底在说什么啊,我K8s的ecs节点要拜访clb的地址不通和本地网卡有什么关系..." 愤慨语气都从电话那头传了过去,这时电话两端都缄默了。过了好一会传来地铁小姐姐甘甜的播报声打断了刚刚的寂静「乘坐地铁必须全程佩戴口罩,下一站西湖文化广场...」。

pod须要拜访clb的443的监听, 然而如果是集群内(集群内前面都指的K8s的节点或者POD)拜访就会呈现如下报错Connection refused:

所以就捋了一下客户链路如下:

具体景象是什么

无论是节点node还是pod里拜访192.168.1.200:443都是不通的,然而拜访192.168.1.200:80却是失常的。同时集群外的ECS192.168.3.100拜访192.168.1.200:443和192.168.1.200:80都是失常的。

进一步剖析看看

CLB1的IP192.168.1.200被绑定到了K8s的node节点的kube-ipvs0网卡上,这个是一张dummy 网卡,参考dummy interface。因为 SVC1 是LoadBalancer类型的,同时复用了这个CLB1,关联endpoint是POD1192.168.1.101:80,那么就能够解释为何拜访192.168.1.200:80是失常,是因为kube-proxy依据SVC1的配置创立ipvs规定同时挂载了可被拜访的后端服务。而集群里拜访192.168.1.200:443都是不通的,因为IP被绑定到dummy网卡后,就不会再出节点去拜访到CLB1,同时没有443对应ipvs规定,所以间接是回绝的。

这个时候如果节点里没有ipvs规定(ipvs优先于监听)然而又能拜访通的话, 能够检查一下是否本地有监听0.0.0.0:443的服务,那么这个时候所有网卡IP+443都能通,然而拜访的是本地服务,而不是真正的CLB后端的服务。

是否有方法解决呢

最倡议的形式

最好的形式拆分, 集群内和集群外的服务离开两个CLB应用。

阿里云svc注解的形式

SVC1应用这个注解service.beta.kubernetes.io/alibaba-cloud-loadbalancer-hostname,进行占位,这样就不会绑定CLB的IP到kube-ipvs0的网卡上,集群内拜访CLB的IP就会出集群拜访CLB,然而须要留神如果监听协定为TCP或UDP,集群内拜访CLB IP时将会存在回环拜访问题。详细信息,请参见客户端无法访问负载平衡CLB [ 1]

须要CCM版本在 v2.3.0及以上版本才反对这个注解, 具体参考:通过Annotation配置传统型负载平衡CLB [ 2]

demo:

apiVersion: v1kind: Servicemetadata:  annotations:    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-hostname: "${your_service_hostname}"  name: nginx-svc  namespace: defaultspec:  ports:  - name: http    port: 80    protocol: TCP    targetPort: 80  selector:    app: nginx  type: LoadBalancer

集群内拜访 ExternalTrafficPolicy 策略有影响吗?

咱们都晓得K8s的nodeport和loadbalancer模式是能够调整内部流量策略的,那么图中的「内部策略为Local/Cluster,所有集群节点创立IPVS规定是有区别的」该如何解释呢, 以及集群内拜访nodePort/CLBIP的时候会产生什么。

以下都是针对svc的internalTrafficPolicy都是Cluster或者缺省的状况,这个ServiceInternalTrafficPolicy个性在1.22的K8s中默认开启,具体参考service-traffic-policy [ 3]

具体到阿里云容器在不同网络CNI状况下的数据链路,能够参考上面的文章:

  • 全景分析阿里云容器网络数据链路(一)—— Flannel
  • 全景分析阿里云容器网络数据链路(二)—— Terway ENI
  • 全景分析阿里云容器网络数据链路(三)—— Terway ENIIP
  • 全景分析阿里云容器网络数据链路(四)—— Terway IPVLAN+EBPF
  • 全景分析阿里云容器网络数据链路(五)—— Terway ENI-Trunking
  • 全景分析阿里云容器网络数据链路(六)—— ASM Istio

此处咱们只探讨ipvs TrafficPolicy Local在Kubernetes 从1.22降级到1.24的行为变动。

Kubernetes 1.24 IPVS的变动

以下均以kube-proxy的IPVS模式为例:

  • 当externalTrafficPolicy为Cluster模式或缺省的时候,ipvs规定里的nodePort/CLBIP后端会挂载所有的Endpoint的IP,这时候集群内拜访会失落源IP,因为节点会做一层SNAT。
  • 当externalTrafficPolicy是Local的时候

<!---->

    • 当节点上有对应service的Endpoint的时候,ipvs规定里的nodePort/CLBIP后端只挂载本人节点的Endpoint的IP,集群内拜访会保留源IP。
    • 当节点上没有对应service的Endpoint的时候
    • 在1.24之前的版本是会挂空的后端的,集群内拜访会回绝。
    • 在1.24之后的K8s集群里,当节点上没有对应service的Endpoint的时候,ipvs规定里的nodePort/CLB IP后端会挂载所有的Endpoint的IP,这时候集群内拜访会失落源IP,因为节点会做一层SNAT。社区调整了Local策略后端服务的规定挂载策略,具体参考社区PR [ 4]

https://github.com/kubernetes/kubernetes/pull/97081/commits/61085a75899a820b5eebfa71801e17423c1ca4da

集群外拜访SLB

集群外拜访SLB的话,CCM只会挂载Local类型的节点,状况跟1.24 kubernetes前一样,这里不做过多论述,请见下面连贯。

集群外拜访NodePort

1.24 Kubernetes之前版本

  • 拜访有Endpoint的节点的NodePort,能够通,能够保留源IP

Nginx散布在cn-hongkong.10.0.4.174和cn-hongkong.10.0.2.84节点。

从内部10.0.3.72节点拜访有后端pod所在节点的cn-hongkong.10.0.2.84的30479端口,能够拜访。

cn-hongkong.10.0.0.140节点上是有相干的IPVS的规定的,然而只有该节点上后端Pod IP。

通过conntrack表能够到,这是因为在cn-hongkong.10.0.0.140节点上,相干的链路被dnat,最初是由pod cn-hongkong.10.0.2.84节点上的 的nginx-7d6877d777-tzbf7 10.0.2.87返回源,所有的相干转化都在该节点上,所以TCP四层建连能够胜利。

  • 拜访没有Endpoint的节点的NodePort,不能通,因为节点上没有相干的ipvs转发规定

从内部10.0.3.72节点拜访无后端pod所在节点的cn-hongkong.10.0.0.140的30479端口,不能够拜访。

查看该cn-hongkong.10.0.0.140节点,并没有相干的ipvs转发规定,所以无奈进行dnat,拜访会失败。

1.24 Kubernetes版本之后(含)

拜访有Endpoint节点的NodePort,能够通,能够保留源IP

拜访没有Endpoint节点的NodePort:

  • terway ENIIP or host网络:不通

Nginx散布在cn-hongkong.10.0.2.77和cn-hongkong.10.0.0.171 节点。

从内部10.0.3.72节点拜访无后端pod所在节点的cn-hongkong.10.0.5.168的30745端口,能够看到,拜访失败。

cn-hongkong.10.0.5.168节点上是有相干的IPVS的规定的,并且会把所有的后端Pod IP加到IPVS规定中。

通过conntrack表能够到,这是因为在cn-hongkong.10.0.5.168节点上,相干的链路被dnat,最初是由pod cn-hongkong.10.0.2.77节点上的nginx-79fc6bc6d-8vctc 10.0.2.78返回源,源在承受这个链路后,会发现和本人的五元组不匹配,间接抛弃,三次握手必然失败,所以建连失败。

  • flannel网络:能够通,然而保留不了源IP

Nginx散布在cn-hongkong.10.0.2.86。

从内部拜访cn-hongkong.10.0.4.176的31218端口,能够拜访胜利。

cn-hongkong.10.0.4.176记录了src是10.0.3.72,并做了dnat为172.16.160.135,冀望它返回给10.0.4.176的58825端口。

后端ep所在节点cn-hongkong.10.0.2.86,conntrack表记录了src是10.0.4.176,sport是58825。所以能够看到利用pod是记录的源IP是10.0.4.176,失落了源IP。

集群内拜访SLB或者NodePort

1.24 Kubernetes之前版本

  • 有Endpoint的节点上拜访,能够通,能够保留源IP

Nginx散布在ap-southeast-1.192.168.100.209和ap-southeast-1.192.168.100.208节点,ap-southeast-1.192.168.100.210节点没有Nginx pod。

从集群任意节点(本例就在209节点)拜访有后端pod所在节点的ap-southeast-1.192.168.100.209的NodePort  31565端口,能够拜访。

从有后端pod所在节点ap-southeast-1.192.168.100.209拜访SLB 8.222.252.252 的80端口,能够拜访。

ap-southeast-1.192.168.100.209节点上是有NodePort 和SLB 的IPVS的规定的,然而只有该节点上后端Pod IP。

通过conntrack表能够到,这是因为在ap-southeast-1.192.168.100.209 节点上,相干的链路被dnat,最初是由pod 在ap-southeast-1.192.168.100.209 节点上的 的nginx-7d6877d777-2wh4s 192.168.100.222返回源,所有的相干转化都在该节点上,所以TCP四层建连能够胜利。

  • 没有Endpoint的节点上拜访,不能通,因为节点上没有相干的ipvs转发规定

从集群任意节点(本例就在210节点)拜访没有后端pod所在节点的ap-southeast-1.192.168.100.210 的NodePort 31565端口或者SLB,不能够拜访。

也进一步证实,集群内拜访关联svc的SLB不出节点,即便SLB有其余监听端口,拜访SLB其余端口也会回绝。

查看该ap-southeast-1.192.168.100.210 节点,并没有相干的ipvs转发规定,所以无奈进行dnat,拜访会失败。

1.24 Kubernetes版本之后(含)

  • 有Endpoint节点上拜访,能够通,能够保留源IP

与上文的1.24 Kubernetes之前版本集群内拜访统一,能够参考上文形容。

  • 没有Endpoint节点上拜访:

Nginx散布在cn-hongkong.10.0.2.77和cn-hongkong.10.0.0.171节点,所以在没有Nginx的cn-hongkong.10.0.4.141节点上测试。

别离有以下几种状况:

  • terway或后端为hostNetwork

<!---->

    • 节点拜访的通 NodePort(源 IP 是 ECS IP,不须要做 SNAT),无奈保留源IP

能够看到没有Endpoint的节点的NodePort 110.0.4.141:30745 的IPVS 的规定增加的Nginx的所有后端POD nginx-79fc6bc6d-8vctc 10.0.2.78  和 nginx-79fc6bc6d-j587w 10.0.0.172。

集群内节点本身拜访没有后端pod所在节点的cn-hongkong.10.0.4.141 的NodePort 30745/TCP端口,能够拜访。

通过conntrack表能够到,在cn-hongkong.10.0.4.141节点上,相干的链路被dnat,最初是由后盾Nginx pod nginx-79fc6bc6d-8vctc 10.0.2.78返回源。

而在nginx-79fc6bc6d-8vctc 10.0.2.78 所在的节点cn-hongkong.10.0.2.77上的conntrack表记录的是10.04.141拜访10.0.2.78,并冀望10.0.2.78间接返回10.0.4.141的的39530端口。

集群内有endpoint 节点拜访没有后端pod所在节点的ap-southeast-1.192.168.100.131 的NodePort 32292端口,不能够拜访,与上文1.24 Kubernetes版本之后(含) 集群外拜访统一,能够参考上文形容。
    • 节点拜访不通 SLB IP(源 IP 是 SLB IP,没有人做 SNAT)

能够看到没有Endpoint的节点的SLB IP 的IPVS 的规定增加的Nginx的所有后端POD nginx-79fc6bc6d-8vctc 10.0.2.78  和 nginx-79fc6bc6d-j587w 10.0.0.172。

没有Endpoint的节点上拜访 SLB 47.243.247.219,拜访确是超时。

通过conntrack表能够到,在没有ep的节点拜访SLB的IP,能够看到冀望的是后端pod返回给SLB IP。而SLB IP 在节点上曾经被kube-ipvs虚构占位了,所以没有做snat,造成无法访问。

  • flannel并且后端为一般pod,能够拜访通,然而保留不了源IP

Nginx散布在cn-hongkong.10.0.2.86。

在cn-hongkong.10.0.4.176拜访SLB 47.242.86.39 是能够拜访胜利的。

cn-hongkong.10.0.4.176节点的conntrack表能够看到是src和dst都是47.242.86.39,然而冀望的是 nginx pod172.16.160.135 返回给 10.0.4.176 的54988端口,47.242.86.39 snat成10.0.4.176。

后端ep所在节点cn-hongkong.10.0.2.86,conntrack表记录了src是10.0.4.176,sport是54988。所以能够看到利用pod是记录的源IP是10.0.4.176,失落了源IP。

相干链接:

[1] 客户端无法访问负载平衡CLB

https://help.aliyun.com/document_detail/55206.htm

[2] 通过Annotation配置传统型负载平衡CLB

https://www.yuque.com/r/goto?url=https%3A%2F%2Fhelp.aliyun.com%2Fzh%2Fack%2Fack-managed-and-ack-dedicated%2Fuser-guide%2Fadd-annotations-to-the-yaml-file-of-a-service-to-configure-clb-instances

[3] service-traffic-policy

https://kubernetes.io/zh-cn/docs/concepts/services-networking/service-traffic-policy/

[4] 社区PR

https://github.com/kubernetes/kubernetes/pull/97081/commits/61085a75899a820b5eebfa71801e17423c1ca4da