K8S 爲什麼在 Pod 中無法通過 Service 訪問自己?

問題現象

創建一個 nginx pod,並配置了 service 訪問,service 後端指向 pod。

進入 pod 中使用 service ip 或者 service 域名,無法訪問。

一開始以爲是環境配置或者 k8s 版本(1.9)的問題,在其他 1.13 的 kubernetes 環境下也試了,還是同樣的問題。

環境配置

使用的 cni 插件是 flannel,但不是容器化安裝,也不是標準化的通過 kubelet 指定 cni plugin(--cni-bin-dir,--cni-conf-dir 參數),而是通過 dockerd 提供的-bip參數指定容器的子網,而這個值是從/run/flannel/subnet.env(flannel 會將獲取到的子網寫入到該文件)

排查過程

「1、首先嚐試通過 pod ip 嘗試是否可訪問,已驗證是可通的。」

「2、嘗試對 docker0 網橋進行抓包」

tcpdump -i docker0

神奇的在這裏,再次嘗試通過 service 訪問是居然可以通,發現只要 tcpdump 斷開就不行了。

到這裏的時候有點覺得詭異了

在 pod 內通過 service 訪問的時候網絡的流向應該是

pod內部訪問service->docker0網橋->宿主機的iptables規則->docker0網橋->pod內部

查閱了相關資料後,看到 kubelet 有個--hairpin-mod參數:

文檔說明:

如果網絡沒有爲 “髮夾模式” 流量生成正確配置,通常當 kube-proxy 以 iptables 模式運行,並且 Pod 與橋接網絡連接時,就會發生這種情況。Kubelet 公開了一個 hairpin-mode 標誌,如果 pod 試圖訪問它們自己的 Service VIP,就可以讓 Service 的端點重新負載到他們自己身上。hairpin-mode 標誌必須設置爲 hairpin-veth 或者 promiscuous-bridge。

可是我設置之後還是沒有還是不行,翻了一下 kubelet 裏面的代碼,發現 cni 組件並沒有取這個值做任何才做(在 kubnet 中有)

查看這個討論:https://github.com/kubernetes/kubernetes/issues/45790

「大致結論是,應該由 cni 插件來根據這個值來做對應的操作。」

還是沒解決我的問題?

不過我看到 hairpin 開啓的標誌位是通過/sys/devices/virtual/net/docker0/brif/veth-xxx/hairpin_mod 內容設置爲 1 開啓的。

所以我將所有 veth 該文件內容設置1

for intf in /sys/devices/virtual/net/docker0/brif/*; do echo 1> $intf/hairpin_mod; done

可以訪問了。😺

解疑:promiscuous-bridge 與 hairpin-veth

「爲什麼我無法訪問」

bridge 不允許包從收到包的端口發出,比如這種情況,在 pod 內通過 docker0 訪問 service,後續又通過 docker0 網橋進來,所以需要開啓hairpin_mod

「爲什麼使用 tcpdump 可以讓訪問可通?」

因爲 tcpdump 要抓取所有經過該網卡,所以需要開啓混雜模式。可以在 / var/log/message 看到device docker0 entered promiscuous mode的 log。

混雜模式(英語:promiscuous mode)是電腦網絡中的術語。是指一臺機器的網卡能夠接收所有經過它的數據流,而不論其目的地址是否是它。一般計算機網卡都工作在非混雜模式下,此時網卡只接受來自網絡端口的目的地址指向自己的數據。當網卡工作在混雜模式下時,網卡將來自接口的所有數據都捕獲並交給相應的驅動程序。

手動開關命令:ifconfig docker0 promisc on/off

總結

其實我們集羣通過這種比較另類的方式來分配 POD IP 也用了了很久了,之所以沒出問題,應該是業務基本沒遇到這種 pod 內通過 service 訪問自己的情況。

所以還是要跟着標準的 k8s 方式來安裝 cni,避免入坑,比如 flannel 就已經提供給了hairpinMode參數來進行配置開啓。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/IdEMPK87yUPclbhdT_1eTQ