網絡排錯
本章主要介紹各種常見的網絡問題以及排錯方法,包括 Pod 訪問異常、Service 訪問異常以及網絡安全策略異常等。
說到 Kubernetes 的網絡,其實無非就是以下三種情況之一
Pod 訪問容器外部網絡
從容器外部訪問 Pod 網絡
Pod 之間相互訪問
當然,以上每種情況還都分別包括本地訪問和跨主機訪問兩種場景,並且一般情況下都是通過 Service 間接訪問 Pod。
排查網絡問題基本上也是從這幾種情況出發,定位出具體的網絡異常點,再進而尋找解決方法。網絡異常可能的原因比較多,常見的有
CNI 網絡插件配置錯誤,導致多主機網絡不通,比如
IP 網段與現有網絡衝突
插件使用了底層網絡不支持的協議
忘記開啓 IP 轉發等
sysctl net.ipv4.ip_forwardsysctl net.bridge.bridge-nf-call-iptables
Pod 網絡路由丟失,比如
kubenet 要求網絡中有 podCIDR 到主機 IP 地址的路由,這些路由如果沒有正確配置會導致 Pod 網絡通信等問題
在公有云平臺上,kube-controller-manager 會自動爲所有 Node 配置路由,但如果配置不當(如認證授權失敗、超出配額等),也有可能導致無法配置路由
Service NodePort 和 health probe 端口衝突
在 1.10.4 版本之前的集群中,多個不同的 Service 之間的 NodePort 和 health probe 端口有可能會有重合 (已經在 kubernetes#64468 修復)
主機內或者雲平臺的安全組、防火牆或者安全策略等阻止了 Pod 網絡,比如
非 Kubernetes 管理的 iptables 規則禁止了 Pod 網絡
公有云平臺的安全組禁止了 Pod 網絡(注意 Pod 網絡有可能與 Node 網絡不在同一個網段)
交換機或者路由器的 ACL 禁止了 Pod 網絡
Flannel Pods 一直處於 Init:CrashLoopBackOff 狀態
Flannel 網絡插件非常容易部署,只要一條命令即可
然而,部署完成後,Flannel Pod 有可能會碰到初始化失敗的錯誤
查看日誌會發現
這一般是由於 SELinux 開啓導致的,關閉 SELinux 既可解決。有兩種方法:
修改
/etc/selinux/config文件方法:SELINUX=disabled通過命令臨時修改(重啓會丟失):
setenforce 0
Pod 無法分配 IP
Pod 一直處於 ContainerCreating 狀態,查看事件發現網絡插件無法爲其分配 IP:
查看網絡插件的 IP 分配情況,進一步發現 IP 地址確實已經全部分配完,但真正處於 Running 狀態的 Pod 數卻很少:
這有兩種可能的原因
網絡插件本身的問題,Pod 停止後其 IP 未釋放
Pod 重新創建的速度比 Kubelet 調用 CNI 插件回收網絡(垃圾回收時刪除已停止 Pod 前會先調用 CNI 清理網絡)的速度快
對第一個問題,最好聯繫插件開發者詢問修復方法或者臨時性的解決方法。當然,如果對網絡插件的工作原理很熟悉的話,也可以考慮手動釋放未使用的 IP 地址,比如:
停止 Kubelet
找到 IPAM 插件保存已分配 IP 地址的文件,比如
/var/lib/cni/networks/cbr0(flannel)或者/var/run/azure-vnet-ipam.json(Azure CNI)等查詢容器已用的 IP 地址,比如
kubectl get pod -o wide --all-namespaces | grep <node-name>對比兩個列表,從 IPAM 文件中刪除未使用的 IP 地址,並手動刪除相關的虛擬網卡和網絡命名空間(如果有的話)
重新啓動 Kubelet
而第二個問題則可以給 Kubelet 配置更快的垃圾回收,如
Pod 無法解析 DNS
如果 Node 上安裝的 Docker 版本大於 1.12,那麼 Docker 會把默認的 iptables FORWARD 策略改爲 DROP。這會引發 Pod 網絡訪問的問題。解決方法則在每個 Node 上面運行 iptables -P FORWARD ACCEPT,比如
如果使用了 flannel/weave 網絡插件,更新爲最新版本也可以解決這個問題。
除此之外,還有很多其他原因導致 DNS 無法解析:
(1)DNS 無法解析也有可能是 kube-dns 服務異常導致的,可以通過下面的命令來檢查 kube-dns 是否處於正常運行狀態
如果 kube-dns 處於 CrashLoopBackOff 狀態,那麼可以參考 Kube-dns/Dashboard CrashLoopBackOff 排錯 來查看具體排錯方法。
(2)如果 kube-dns Pod 處於正常 Running 狀態,則需要進一步檢查是否正確配置了 kube-dns 服務:
如果 kube-dns service 不存在,或者 endpoints 列表爲空,則說明 kube-dns service 配置錯誤,可以重新創建 kube-dns service,比如
(3)如果最近升級了 CoreDNS,並且使用了 CoreDNS 的 proxy 插件,那麼需要注意 1.5.0 以上的版本需要將 proxy 插件替換爲 forward 插件。比如:
(4)如果 kube-dns Pod 和 Service 都正常,那麼就需要檢查 kube-proxy 是否正確爲 kube-dns 配置了負載均衡的 iptables 規則。具體排查方法可以參考下面的 Service 無法訪問部分。
DNS解析緩慢
由於內核的一個 BUG,連接跟蹤模塊會發生競爭,導致 DNS 解析緩慢。社區跟蹤 Issue 爲 https://github.com/kubernetes/kubernetes/issues/56903。
臨時解決方法:爲容器配置 options single-request-reopen,避免相同五元組的併發 DNS 請求:
或者爲 Pod 配置 dnsConfig:
注意:
single-request-reopen選項對 Alpine 無效,請使用 Debian 等其他的基礎鏡像,或者參考下面的修復方法。
修復方法:升級內核並保證包含以下兩個補丁
netfilter: nf_nat: skip nat clash resolution for same-origin entries (included since kernel v5.0)
netfilter: nf_conntrack: resolve clash for matching conntracks (included since kernel v4.19)
對於 Azure 來說,這個問題已經在 v4.15.0-1030.31/v4.18.0-1006.6 中修復了(patch1, patch2)。
其他可能的原因和修復方法還有:
Kube-dns 和 CoreDNS 同時存在時也會有問題,只保留一個即可。
kube-dns 或者 CoreDNS 的資源限制太小時會導致 DNS 解析緩慢,這時候需要增大資源限制。
配置 DNS 選項
use-vc,強制使用 TCP 協議發送 DNS 查詢。在每個節點上運行一個 DNS 緩存服務,然後把所有容器的 DNS nameserver 都指向該緩存。
推薦部署 Nodelocal DNS Cache 擴展來解決這個問題,同時也提升 DNS 解析的性能。 Nodelocal DNS Cache 的部署步驟請參考 https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/dns/nodelocaldns。
更多 DNS 配置的方法可以參考 Customizing DNS Service。
Service 無法訪問
訪問 Service ClusterIP 失敗時,可以首先確認是否有對應的 Endpoints
如果該列表爲空,則有可能是該 Service 的 LabelSelector 配置錯誤,可以用下面的方法確認一下
如果 Endpoints 正常,可以進一步檢查
Pod 的 containerPort 與 Service 的 containerPort 是否對應
直接訪問
podIP:containerPort是否正常
再進一步,即使上述配置都正確無誤,還有其他的原因會導致 Service 無法訪問,比如
Pod 內的容器有可能未正常運行或者沒有監聽在指定的 containerPort 上
CNI 網絡或主機路由異常也會導致類似的問題
kube-proxy 服務有可能未啓動或者未正確配置相應的 iptables 規則,比如正常情況下名爲
hostnames的服務會配置以下 iptables 規則
Pod 無法通過 Service 訪問自己
這通常是 hairpin 配置錯誤導致的,可以通過 Kubelet 的 --hairpin-mode 選項配置,可選參數包括 "promiscuous-bridge"、"hairpin-veth" 和 "none"(默認爲"promiscuous-bridge")。
對於 hairpin-veth 模式,可以通過以下命令來確認是否生效
而對於 promiscuous-bridge 模式,可以通過以下命令來確認是否生效
無法訪問 Kubernetes API
很多擴展服務需要訪問 Kubernetes API 查詢需要的數據(比如 kube-dns、Operator 等)。通常在 Kubernetes API 無法訪問時,可以首先通過下面的命令驗證 Kubernetes API 是正常的:
如果出現超時錯誤,則需要進一步確認名爲 kubernetes 的服務以及 endpoints 列表是正常的:
然後可以直接訪問 endpoints 查看 kube-apiserver 是否可以正常訪問。無法訪問時通常說明 kube-apiserver 未正常啓動,或者有防火牆規則阻止了訪問。
但如果出現了 403 - Forbidden 錯誤,則說明 Kubernetes 集群開啓了訪問授權控制(如 RBAC),此時就需要給 Pod 所用的 ServiceAccount 創建角色和角色綁定授權訪問所需要的資源。比如 CoreDNS 就需要創建以下 ServiceAccount 以及角色綁定:
內核導致的問題
除了以上問題,還有可能碰到因內核問題導致的服務無法訪問或者服務訪問超時的錯誤,比如
未設置
--random-fully導致無法爲 SNAT 分配端口,進而會導致服務訪問超時。注意, Kubernetes 暫時沒有爲 SNAT 設置--random-fully選項,如果碰到這個問題可以參考這裏 配置。
參考文檔
Last updated