共计 6717 个字符,预计需要花费 17 分钟才能阅读完成。
作者:郑明泉、余凯
为啥争吵,吵什么?
“ 你到底在说什么啊,我 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: v1
kind: Service
metadata:
annotations:
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-hostname: "${your_service_hostname}"
name: nginx-svc
namespace: default
spec:
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