解決pod健康檢查問題

charlieroro發表於2023-05-10

解決pod健康檢查問題

引自:Solving the mystery of pods health checks failures in Kubernetes。原文中的某些描述並不清晰,本文作了調整。

很早以前,環境中的pod有時候會遇到健康檢查失敗的問題,但並沒有什麼明顯表徵,且幾乎是立馬就會恢復。由於這種情況很少發生,且不會對業務造成影響,因此起初並沒有人關注該問題。

但後來發生的頻率越來越高,導致開發人員頻繁接收到deployment的健康告警。

第1步:檢視日誌

  • Kubernetes worker的系統日誌 -- 無異常
  • kubelet 日誌 -- 無異常
  • Containerd 日誌 -- 無異常
  • CNI 日誌 -- 無異常
  • 檢查最近失敗的pod日誌 -- 無異常

透過檢查相關日誌,並沒有發現什麼異常

第2步:tcpdump

在抓取的流量中發現,當kubelet給pod傳送TCP SYN之後,pod會回覆SYN-ACK,但kubelet並沒有傳送TCP ACK。在一段時間的重試之後,Kubelet會建立起一條TCP會話,因此該問題是隨機發生的。

為以防萬一,我們檢查了TCP中的seq和ack序列號,並沒有發現問題。

此時懷疑worker可能存在問題:是不是Kubelet沒有處理接收到的報文?

第3步:ss

每秒呼叫一次"ss -natp"來檢視kubelet程式連線,此時發現失敗的連線卡在了SYN-SENT階段,說明kubelet並沒有接收到pod發來的SYN-ACK報文。

第4步:conntrack

使用conntrack檢視TCP網路連線跟蹤,發現有的連線卡在SYN-SENT狀態(kubelet側),有的連線卡在SYN-RECV(pod側),但連線的源埠號看起來都類似。

在我們的環境中,設定了一個較大的源埠可選範圍:

net.ipv4.ip_local_port_range=12000 65001

出現問題的源埠為30XXX或31XXX,非常類似。

第5步:ipvs

透過ipvsadm命令檢視ipvs配置發現,所有卡住的連線都使用了Kubernetes的nodeport 保留埠

根因分析

至此,問題已經明瞭。當Kubelet初始化一條TCP連線時,會隨機選擇一個源埠號,例如31055。當TCP SYN到達pod之後,pod會向31055埠號回覆一個TCP SYN-ACK報文。當該報文到達IPVS之後,由於已經存在一個埠號為31055的nodeport(Kubernetes loadbalance service),此時會將TCP SYN-ACK報文轉發到對應的後端(其他pod),這樣就導致Kubelet無法接收到回覆的報文,無法建立連線。

解決辦法

解決方式也很簡單,設定如下核心引數即可,這樣Kubelet在建立連線時就不會選擇30000–32768的埠作為TCP源埠:

net.ipv4.ip_local_reserved_ports="30000–32768"

Kubernetes的nodeport保留埠為30000-32767,因此設定的net.ipv4.ip_local_reserved_ports為30000–32768

TIPs

  • net.ipv4.ip_local_port_range的預設值為32768 60999,正好和Kubernetes的nodeport保留埠錯開,本文中描述的問題的源頭也是因為修改了該核心引數,因此非必要不要修改核心引數!

相關文章