我是 LEE岁经,老李隐锭,一個(gè)在 IT 行業(yè)摸爬滾打 16 年的技術(shù)老兵携悯。
事件背景
我們的幾個(gè)核心 k8s 集群規(guī)則持續(xù)增長(zhǎng)获询,網(wǎng)絡(luò)穩(wěn)定性和效能之間的平衡性成為目前主要需要攻克的問題。目前單個(gè)集群的節(jié)點(diǎn) 270+ 漠酿、Pod 40000+ 冯凹,這樣的規(guī)模 Pod 之間的請(qǐng)求流量規(guī)模十分巨大,而且網(wǎng)絡(luò)流量壓力對(duì)傳統(tǒng)的 k8s 網(wǎng)絡(luò)組件壓力很大炒嘲。為了要解決這個(gè)問題宇姚,我把目標(biāo)轉(zhuǎn)向了基于 ebpf 的 cilium 組件匈庭,決定使用 cilium 來替換 kubelet 中的 kubenet 組件。
我選擇了一個(gè)有點(diǎn)量的測(cè)試 k8s 集群浑劳,安裝了 cilium 1.11.8 后阱持,做了集群內(nèi)部的網(wǎng)絡(luò)測(cè)試,Pod 到節(jié)點(diǎn)以及 Pod 之間都是可以 ping 通呀洲,而且端口訪問是正常紊选。隨后我們對(duì)外發(fā)布測(cè)試服務(wù)的時(shí)候,發(fā)現(xiàn)外部無法連接到測(cè)試 k8s 集群內(nèi)的應(yīng)用道逗。這個(gè)事情讓我百思不得其解,明明網(wǎng)絡(luò)測(cè)試都是 OK 的献烦,為什么對(duì)外發(fā)布的測(cè)試服務(wù)卻無法正常訪問滓窍。
現(xiàn)象獲取
這個(gè)時(shí)候拉來了底層云平臺(tái)供應(yīng)商的小伙伴一起排查,也是毫無進(jìn)展巩那。此時(shí)我只能逐一層次網(wǎng)絡(luò)測(cè)試排查吏夯,通過一個(gè)下午的排查,最后發(fā)現(xiàn)外部直接訪問 Pod 提供的服務(wù)是 OK即横,但是通過 Service 中 Loadbalancer 模式發(fā)布的服務(wù)確實(shí)訪問不通的噪生。
我們登錄到云供應(yīng)商上的 Loadbalancer 服務(wù)上,查看對(duì)應(yīng)服務(wù)實(shí)例的狀態(tài)和日志东囚。通過觀察跺嗽,發(fā)現(xiàn) Loadbalancer upstream 的 RealServer 狀態(tài)不停的翻滾,一會(huì)狀態(tài)“健康”页藻,一會(huì)狀態(tài)“異辰凹蓿”。我想這個(gè)“現(xiàn)象”應(yīng)該是導(dǎo)致外部流量沒有辦法通過 Loadbalancer 到后端服務(wù)的原因份帐。
另外一個(gè)問題:我們直接訪問 Node 和 Pod 的端口提供的服務(wù)都是正常的璃吧,但是 Loadbalancer 健康檢測(cè)這個(gè)服務(wù)端口卻是失敗的?
一看究竟就必須要請(qǐng)出 tcpdump 大法废境,抓包復(fù)原 Loadbalancer 健康檢測(cè)會(huì)話流畜挨。
健康檢測(cè)會(huì)話流分析
正常情況
按照原供應(yīng)商小伙伴提供的信息,同時(shí)結(jié)合抓包結(jié)果噩凹,我們得知正常的一個(gè)健康檢測(cè)是:
- Loadbalancer --> RealServer: sync
- RealServer --> Loadbalancer: sync,ack
- Loadbalancer --> RealServer: rst,ack
公有云的 Loadbalancer 會(huì)與 RealServer 嘗試建立 tcp 連接巴元,1,2 步交換 sync 和 sync,ack栓始,Loadbalancer 再收到了 sync,ack务冕,就會(huì)向 RealServer 發(fā)送 rst,ack 關(guān)閉 tcp 連接,成功完成一次健康檢測(cè)
異常情況
我們看過了正常的健康檢測(cè)的樣子幻赚,那么我們這邊公有云的 Loadbalancer 健康異常檢測(cè)如上圖所示禀忆。
經(jīng)過分析臊旭,總結(jié)出現(xiàn)異常的情況如下:
- Loadbalancer --> RealServer 發(fā)送了 sync
- Loadbalancer --> RealServer RealServer 沒有回應(yīng) sync,ack,然后公有云 Loadbalancer 嘗試重傳 sync
- RealServer(另外 IP) --> Loadbalancer 另外一個(gè) IP 的 RealServer 回應(yīng) sync,ack
感覺到了一絲絲不對(duì)的情況箩退,我想心細(xì)的小伙伴應(yīng)該看到了問題所在离熏。問題出現(xiàn)在 RealServer --> Loadbalancer 這里,為什么回應(yīng) Loadbalancer 的 RealServer IP 地址不對(duì)戴涝,導(dǎo)致 Loadbalancer 認(rèn)為正確的 RealServer 沒有回應(yīng)自己的檢測(cè)滋戳,隨之將此 RealServer 的狀態(tài)標(biāo)記成“異常”啥刻。
原理分析
在沉思片刻后奸鸯,我覺得最復(fù)雜的問題導(dǎo)致的原因可能最簡(jiǎn)單,一定是 cilium 組件哪里設(shè)置有問題可帽,因?yàn)楣性?Loadbalancer 到后端 Pod 的這樣業(yè)務(wù)模型在其他的沒有部署 cilium 的集群內(nèi)都是正常娄涩。
既然知道數(shù)據(jù)流的情況,也知道出問題的組件映跟,問題就好解決了蓄拣。
cilium 網(wǎng)絡(luò)架構(gòu)
cilium 的安裝命令
helm upgrade -i cilium cilium/cilium --version 1.11.8 --namespace kube-system \
--set tunnel=disabled \
--set ipam.mode=kubernetes \
--set nativeRoutingCIDR=10.192.32.0/19 \
--set loadBalancer.mode=hybrid \ # 此參數(shù)是問題所在
--set kubeProxyReplacement=probe \
--set prometheus.enabled=true \
--set operator.prometheus.enabled=true \
--set hubble.enabled=true \
--set hubble.metrics.enabled="{dns,drop,tcp,flow,port-distribution,icmp,http}"
庖丁解牛
結(jié)合 cilium 網(wǎng)絡(luò)架構(gòu)看來看去,最值得懷疑的地方就是 loadBalancer.mode=hybrid 這個(gè)參數(shù)努隙,按照 cilium 官方的參考文檔解釋如下:
Cilium also supports a hybrid DSR and SNAT mode, that is, DSR is performed for TCP and SNAT for UDP connections. This has the advantage that it removes the need for manual MTU changes in the network while still benefiting from the latency improvements through the removed extra hop for replies, in particular, when TCP is the main transport for workloads.
The mode setting loadBalancer.mode allows to control the behavior through the options dsr, snat and hybrid. By default the snat mode is used in the agent.
By default, Cilium’s eBPF NodePort implementation operates in SNAT mode. That is, when node-external traffic arrives and the node determines that the backend for the LoadBalancer, NodePort or services with externalIPs is at a remote node, then the node is redirecting the request to the remote backend on its behalf by performing SNAT. This does not require any additional MTU changes at the cost that replies from the backend need to make the extra hop back that node in order to perform the reverse SNAT translation there before returning the packet directly to the external client.
This setting can be changed through the loadBalancer.mode Helm option to dsr in order to let Cilium’s eBPF NodePort implementation operate in DSR mode. In this mode, the backends reply directly to the external client without taking the extra hop, meaning, backends reply by using the service IP/port as a source. DSR currently requires Cilium to be deployed in Native-Routing, i.e. it will not work in either tunneling mode.
Note that usage of DSR mode might not work in some public cloud provider environments due to the Cilium-specific IP options that could be dropped by an underlying fabric. Therefore, in case of connectivity issues to services where backends are located on a remote node from the node that is processing the given NodePort request, it is advised to first check whether the NodePort request actually arrived on the node containing the backend. If this was not the case, then switching back to the default SNAT mode would be advised as a workaround.
一大堆英文的看完了球恤,大概的意思是同時(shí)使用 NativeRouting 和 loadBalancer.mode=hybrid,cilium 內(nèi)部的 Loadbalancer 工作在了 DSR 的模式下荸镊。 那么公有云的 Loadbalancer 對(duì)后端 RealServer 檢測(cè)異常的問題就能很好解釋了咽斧。整個(gè)異常的過程跟 DSR 工作模式完全一樣。
處理方法
通過一通復(fù)雜的分析與驗(yàn)證后贷洲,我們?cè)?cilium 的安裝命令中去掉了 loadBalancer.mode=hybrid 參數(shù)收厨,重新部署 cilium,然后重啟相關(guān)的 Pod优构。
最終效果
公有云的 Loadbalancer 與 Node 節(jié)點(diǎn)之間健康檢測(cè)恢復(fù)正常诵叁,對(duì)外發(fā)布的服務(wù)也能夠正常的訪問。