1. 程式人生 > 其它 >一次“不負責任”的 K8s 網路故障排查經驗分享

一次“不負責任”的 K8s 網路故障排查經驗分享

作者 | 駱冰利
來源 | Erda 公眾號

某天晚上,客戶碰到了這樣的問題:K8s 叢集一直擴容失敗,所有節點都無法正常加入叢集。在經過多番折騰無解後,客戶將問題反饋到我們這裡,希望得到技術支援。該問題的整個排查過程比較有意思,本文對其中的排查思路及所用的方法進行了歸納整理並分享給大家,希望能夠對大家在排查此類問題時有些幫助和參考。

問題現象

運維同學在對客戶的 K8s 叢集進行節點擴容時,發現新增的節點一直新增失敗。初步排查結果如下:

  • 在新增節點上,訪問 K8s master service vip 網路不通。
  • 在新增節點上,直接訪問 K8s master hostIP + 6443 網路正常。
  • 在新增節點上,訪問其他節點的容器 IP 可以正常 ping 通。
  • 在新增節點上,訪問 coredns service vip 網路正常。

該客戶使用的 Kubernetes 版本是 1.13.10,宿主機的核心版本是 4.18(centos 8.2)。

問題排查過程

收到該一線同事的反饋,我們已經初步懷疑是 ipvs 的問題。根據以往網路問題排查的經驗,我們先對現場做了些常規排查:

  • 確認核心模組 ip_tables 是否載入(正常)
  • 確認 iptable forward 是否預設 accpet (正常)
  • 確認宿主機網路是否正常(正常)
  • 確認容器網路是否正常(正常)
  • ...

排除了常規問題後,基本可以縮小範圍,下面我們再繼續基於 ipvs 相關層面進行排查。

1. 通過 ipvsadm 命令排查

10.96.0.1 是客戶叢集 K8s master service vip。

如上圖所示,我們可以發現存在異常連線,處於 SYN_RECV 的狀態,並且可以觀察到,啟動時 kubelet + kube-proxy 是有正常建連的,說明是在啟動之後,K8s service 網路出現了異常。

2. tcpdump 抓包分析

兩端進行抓包,並通過 telnet 10.96.0.1 443 命令進行確認。

結論:發現 SYN 包在本機沒有傳送出去。

3. 初步總結

通過上面的排查,我們可以再次縮小範圍:問題基本就在 kube-proxy 身上。我們採用了 ipvs 模式,也依賴了 iptables 配置實現一些網路的轉發、snat、drop 等。

根據上面的排查過程,我們又一次縮小了範圍,開始分析懷疑物件 kube-proxy。

4. 檢視 kube-proxy 日誌


如上圖所示:發現異常日誌,iptables-restore 命令執行異常。通過 Google、社群檢視,確認問題。

相關 issue 連結可參考:

5. 繼續深入

通過程式碼檢視(1.13.10 版本 pkg/proxy/ipvs/proxier.go:1427),可以發現該版本確實沒有判斷 KUBE-MARK-DROP 是否存在並建立的邏輯。當出現該鏈不存在時,會出現邏輯缺陷,導致 iptable 命令執行失敗。

K8s master service vip 不通,實際容器相關的 ip 是通的,這種情況出現的原因,與下面的 iptable 規則有關:

iptable -t nat -A KUBE-SERVICES ! -s 9.0.0.0/8 -m comment --comment "Kubernetes service cluster ip + port for masquerade purpose" -m set --match-set KUBE-CLUSTER-IP dst,dst -j KUBE-MARK-MASQ

6. 根因探究

前面我們已經知道了 kube-proxy 1.13.10 版本存在缺陷,在沒有建立 KUBE-MARK-DROP 鏈的情況下,執行 iptables-restore 命令配置規則。但是為什麼 K8s 1.13.10 版本跑在 centos8.2 4.18 核心的作業系統上會報錯,跑在 centos7.6 3.10 核心的作業系統上卻正常呢?

我們檢視下 kube-proxy 的原始碼,可以發現 kube-proxy 其實也就是執行 iptables 命令進行規則配置。那既然 kube-proxy 報錯 iptables-restore 命令失敗,我們就找一臺 4.18 核心的機器,進入 kube-proxy 容器看下情況。

到容器內執行下 iptables-save 命令,可以發現 kube-proxy 容器內確實沒有建立 KUBE-MARK-DROP 鏈(符合程式碼預期)。繼續在宿主機上執行下 iptables-save 命令,卻發現存在 KUBE-MARK-DROP 鏈。

這裡有兩個疑問:

  • 為什麼 4.18 核心宿主機的 iptables 有 KUBE-MARK-DROP 鏈?
  • 為什麼 4.18 核心宿主機的 iptables 規則和 kube-proxy 容器內的規則不一致?

第一個疑惑,憑感覺懷疑除了 kube-proxy,還會有別的程式在操作 iptables,繼續擼下 K8s 程式碼。
結論:發現確實除了 kube-proxy,還有 kubelet 也會修改 iptables 規則。具體程式碼可以檢視:pkg/kubelet/kubelet_network_linux.go

第二個疑惑,繼續憑感覺······Google 一發撈一下為何 kube-proxy 容器掛載了宿主機 /run/xtables.lock 檔案的情況下,宿主機和容器 iptables 檢視的規則不一致。
​結論:CentOS 8 在網路方面摒棄 iptables,採用 nftables 框架作為預設的網路包過濾工具。

至此,所有的謎團都解開了。

團隊完成過大量的客戶專案交付,這裡有些問題可以再解答下:​

  • 問題一:為什麼這麼多客戶環境第一次碰到該情況?

因為需要 K8s 1.13.10 + centos 8.2 的作業系統,這個組合罕見,且問題必現。升級 K8s 1.16.0+ 就不出現該問題。

  • 問題二:為什麼使用 K8s 1.13.10 + 5.5 核心卻沒有該問題?

因為與 centos 8 作業系統有關,我們手動升級 5.5 版本後,預設還是使用的 iptables 框架。

可以通過 iptables -v 命令,來確認是否使用 nftables。

題外話:nftables 是何方神聖?比 iptables 好麼?這是另一個值得進一步學習的點,這裡就不再深入了。

總結與感悟

針對以上的排查問題,我們總結下解決方法:

  • 調整核心版本到 3.10(centos 7.6+),或者手動升級核心版本到 5.0 +;
  • 升級 Kubernetes 版本,當前確認 1.16.10+ 版本沒有該問題。

以上是我們在進行 Kubernetes 網路故障排查中的一點經驗,希望能夠對大家高效排查,定位原因有所幫助。

如果對於 Erda 專案你有其它想要了解的內容,歡迎新增小助手微信(Erda202106)加入交流群!

歡迎參與開源

Erda 作為開源的一站式雲原生 PaaS 平臺,具備 DevOps、微服務觀測治理、多雲管理以及快資料治理等平臺級能力。點選下方連結即可參與開源,和眾多開發者一起探討、交流,共建開源社群。歡迎大家關注、貢獻程式碼和 Star!