一次網絡不通“爭吵”引發的思考

爲啥爭吵,吵什麼?

"你到底在說什麼啊,我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使用這個註解http://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]

此處我們只討論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

作者: 鄭明泉、餘凱

點擊立即免費試用雲產品 開啓雲上實踐之旅!

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章