解決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保留端口錯開,本文中描述的問題的源頭也是因爲修改了該內核參數,因此非必要不要修改內核參數!