絡(luò)模型到故障排查)
1. 先弄清楚Kubernetes網(wǎng)絡(luò)模型的底層邏輯聊Pod間通信之前得先明白Kubernetes定的那條鐵律每個(gè)Pod都擁有獨(dú)立的IP地址Pod內(nèi)的所有容器共享這個(gè)IP。這個(gè)設(shè)計(jì)是整個(gè)Pod通信體系的基石沒有這個(gè)前提后面所有通信方式都無從談起。為什么這個(gè)設(shè)計(jì)如此重要因?yàn)镵ubernetes假設(shè)所有Pod之間都能直接通信不需要NAT轉(zhuǎn)換不需要額外配置端口映射。這個(gè)扁平化網(wǎng)絡(luò)的設(shè)想把Pod當(dāng)成了一臺(tái)臺(tái)獨(dú)立的主機(jī)Pod的IP就像是主機(jī)IP一樣可以直接訪問。實(shí)踐中你會(huì)感受到這個(gè)模型讓網(wǎng)絡(luò)問題變得非常清晰——Pod連不上要么是本機(jī)網(wǎng)絡(luò)棧問題要么是跨節(jié)點(diǎn)網(wǎng)絡(luò)問題歸因路徑非常直接。這就像一棟大樓里的各個(gè)房間每個(gè)房間都有自己獨(dú)立的門牌號(hào)房間之間通過走廊就能互相串門不需要經(jīng)過前臺(tái)轉(zhuǎn)接。Kubernetes要做的就是保證這棟大樓里的走廊永遠(yuǎn)暢通不管房間分布在同一層還是不同樓層。理解了這層邏輯再去看各種Pod間通信方式就會(huì)豁然開朗Kubernetes解決的是走廊怎么修而通信方式本質(zhì)上就是在不同的網(wǎng)絡(luò)層次上使用這條走廊。2. 同一個(gè)Pod內(nèi)的容器通信最不起眼但最容易被忽視2.1 共享網(wǎng)絡(luò)命名空間localHost直接訪問同一個(gè)Pod內(nèi)的多個(gè)容器最核心的特征是共享同一個(gè)網(wǎng)絡(luò)命名空間。這意味著它們的網(wǎng)絡(luò)棧完全一樣——同一個(gè)IP地址、同一個(gè)端口空間、同一套路由規(guī)則。所以容器之間通信直接通過localhost訪問即可。實(shí)際項(xiàng)目中這個(gè)模式最典型的應(yīng)用就是Sidecar架構(gòu)。舉個(gè)真實(shí)的例子一個(gè)Web應(yīng)用容器監(jiān)聽8080端口旁邊掛一個(gè)日志采集容器日志采集容器直接通過localhost:8080去抓取Web應(yīng)用的訪問日志。這種模式的優(yōu)點(diǎn)在于網(wǎng)絡(luò)開銷幾乎為零走的是內(nèi)核回環(huán)接口性能損耗可以忽略不計(jì)。需要注意的坑是端口沖突。因?yàn)楣蚕矶丝诳臻g同一個(gè)Pod內(nèi)的不同容器監(jiān)聽同一個(gè)端口會(huì)直接報(bào)錯(cuò)。我踩過這個(gè)坑一次項(xiàng)目中Web容器監(jiān)聽8080監(jiān)控容器想用8080跑一個(gè)健康檢查接口結(jié)果第二個(gè)容器怎么都起不來查了半天才發(fā)現(xiàn)是端口沖突。所以設(shè)計(jì)Pod內(nèi)多容器時(shí)一定要提前規(guī)劃好端口分配。2.2 進(jìn)程間通信的幾種手段除了網(wǎng)絡(luò)通信同一個(gè)Pod內(nèi)還支持進(jìn)程間通信方式包括共享內(nèi)存和信號(hào)量。因?yàn)槿萜鞴蚕硗粋€(gè)PID命名空間部分配置下進(jìn)程可以通過標(biāo)準(zhǔn)IPC機(jī)制交互。但在生產(chǎn)環(huán)境我見得最多的還是通過網(wǎng)絡(luò)端口通信進(jìn)程間通信在容器化場(chǎng)景下用得比較少主要原因是容器技術(shù)本來就是想做進(jìn)程隔離再去突破隔離反而違背了初衷。一個(gè)容易被忽略的細(xì)節(jié)是Pod內(nèi)容器之間的文件交換。容器共享Volume所以可以通過共享文件目錄來傳遞數(shù)據(jù)。這個(gè)方式在配置同步、證書輪換等場(chǎng)景下非常好用——生成證書的容器把證書寫到共享目錄主容器監(jiān)聽目錄變化后自動(dòng)加載不需要任何網(wǎng)絡(luò)通信。3. Pod到Pod的直連通信同節(jié)點(diǎn)與跨節(jié)點(diǎn)3.1 同節(jié)點(diǎn)通信veth對(duì)和Linux Bridge的配合當(dāng)兩個(gè)Pod落在同一個(gè)節(jié)點(diǎn)上通信路徑相對(duì)簡(jiǎn)單。每個(gè)Pod里有一個(gè)虛擬網(wǎng)卡veth這個(gè)虛擬網(wǎng)卡的一端在Pod的網(wǎng)絡(luò)命名空間里另一端掛在節(jié)點(diǎn)的Linux Bridge如cni0上。數(shù)據(jù)從Pod的eth0發(fā)出去實(shí)際上就是從一個(gè)veth口進(jìn)從另一個(gè)veth口出然后由Bridge轉(zhuǎn)發(fā)到目標(biāo)Pod的veth口。這個(gè)過程對(duì)性能的影響很小因?yàn)閿?shù)據(jù)包只在節(jié)點(diǎn)內(nèi)部走了一遍二層交換不涉及封包解包。跑I/O密集型的分布式存儲(chǔ)應(yīng)用時(shí)把相關(guān)工作負(fù)載盡量調(diào)度到同一節(jié)點(diǎn)網(wǎng)絡(luò)延遲能明顯降下來。但同節(jié)點(diǎn)通信也有個(gè)隱藏問題ARP表項(xiàng)。每個(gè)Pod啟動(dòng)時(shí)都要在Bridge上做ARP學(xué)習(xí)當(dāng)節(jié)點(diǎn)上Pod數(shù)量很多比如超過100個(gè)Bridge的MAC地址表可能會(huì)比較大極端情況下會(huì)影響轉(zhuǎn)發(fā)性能。大規(guī)模集群里顯示節(jié)點(diǎn)Pod密度規(guī)劃要有數(shù)別為省機(jī)器瘋狂壓榨單節(jié)點(diǎn)。3.2 跨節(jié)點(diǎn)通信Overlay網(wǎng)絡(luò)的封包與解包Pod分布在不同節(jié)點(diǎn)時(shí)問題就復(fù)雜了。節(jié)點(diǎn)A上的Pod IP是10.244.1.5節(jié)點(diǎn)B上的Pod IP是10.244.2.8這兩個(gè)IP在節(jié)點(diǎn)外是不可路由的。要讓它們通信必須通過Overlay網(wǎng)絡(luò)把Pod的IP包封裝在宿主機(jī)的網(wǎng)絡(luò)包里傳輸。以Flannel的VXLAN模式為例數(shù)據(jù)包的流轉(zhuǎn)過程是這樣的源Pod發(fā)出IP包通過節(jié)點(diǎn)A的cni0進(jìn)入FlannelFlannel將原始IP包封裝成UDP包VXLAN封裝外層IP是宿主機(jī)IP然后從節(jié)點(diǎn)的物理網(wǎng)卡發(fā)出去經(jīng)過Underlay網(wǎng)絡(luò)到達(dá)節(jié)點(diǎn)B節(jié)點(diǎn)B收到后解封裝還原原始IP包再通過本地的cni0送給目標(biāo)Pod。這套機(jī)制本質(zhì)上是硬生生多包了一層頭帶來的代價(jià)就是跨節(jié)點(diǎn)通信的延遲比同節(jié)點(diǎn)高。實(shí)測(cè)下來在常見的物理機(jī)上同節(jié)點(diǎn)Pod間通信延遲在0.05ms左右跨節(jié)點(diǎn)走VXLAN通常要到0.2~0.5ms網(wǎng)絡(luò)吞吐也有10%~20%的損耗。所以對(duì)延遲敏感的應(yīng)用比如實(shí)時(shí)推薦系統(tǒng)、交易系統(tǒng)最好讓Pod和它的依賴盡量落在同一節(jié)點(diǎn)。3.3 CNI插件怎么選Flannel、Calico還是Cilium跨節(jié)點(diǎn)通信這么重要底層的CNI插件選型就成了一件繞不開的事。當(dāng)前主流選項(xiàng)實(shí)際是Flannel、Calico和Cilium三家。Flannel最簡(jiǎn)單部署快VXLAN模式和host-gw模式兩種選擇。如果節(jié)點(diǎn)在同一個(gè)二層網(wǎng)絡(luò)用host-gw模式可以繞開封裝開銷性能基本接近原生網(wǎng)絡(luò)。但host-gw模式下Pod網(wǎng)段要跟物理網(wǎng)絡(luò)規(guī)劃好避免路由沖突這點(diǎn)很多初學(xué)者會(huì)忽略。Calico功能最全它用的是BGP路由協(xié)議替代Overlay封裝Pod數(shù)據(jù)包直接走Underlay網(wǎng)絡(luò)路由性能更好。它還支持NetworkPolicy可以精細(xì)控制Pod間誰能訪問誰。代價(jià)是需要BGP環(huán)境的配合網(wǎng)絡(luò)拓?fù)鋸?fù)雜時(shí)配置會(huì)比較燒腦。Cilium是后起之秀基于eBPF技術(shù)性能和可觀測(cè)性都做得非常出色還內(nèi)置了L3/L4/L7層的網(wǎng)絡(luò)策略。但它對(duì)內(nèi)核版本有要求至少4.9以上推薦5.8老舊的節(jié)點(diǎn)系統(tǒng)跑不了。我的建議是小規(guī)模測(cè)試環(huán)境用Flannel省事生產(chǎn)環(huán)境沒有強(qiáng)合規(guī)要求選Calico有大規(guī)模微服務(wù)和高性能要求同時(shí)內(nèi)核版本滿足條件的Cilium值得一試。4. 通過Service通信讓Pod訪問不再依賴具體IP4.1 為什么需要Service直連通信雖然可行但有個(gè)致命問題Pod會(huì)重建、會(huì)漂移每次重建IP都會(huì)變。如果A服務(wù)要訪問B服務(wù)而B的Pod從10.244.1.5重建后變成了10.244.3.9A服務(wù)難道要跟著改配置Service就是為解決這個(gè)問題出現(xiàn)的。它為一組Pod通常用Label Selector圈定提供一個(gè)穩(wěn)定的虛擬IP也就是ClusterIP??蛻舳酥恍枰L問這個(gè)固定的ClusterIP剩下的事Service管了。這個(gè)模式跟生活中的總機(jī)臺(tái)很像你不用記住每個(gè)員工的直線電話只需要打總機(jī)總機(jī)會(huì)幫你轉(zhuǎn)接到具體的人。就算這個(gè)人換了工位總機(jī)照樣能幫他接到電話。4.2 ClusterIP背后的轉(zhuǎn)發(fā)機(jī)制ClusterIP本身是Kubernetes集群內(nèi)部的一個(gè)虛擬IP沒有對(duì)應(yīng)的物理網(wǎng)卡。流量到達(dá)ClusterIP后如何分發(fā)到后端的Pod這就要靠kube-proxy了。kube-proxy在節(jié)點(diǎn)上維護(hù)轉(zhuǎn)發(fā)規(guī)則主要有iptables模式和IPVS模式兩種實(shí)現(xiàn)。iptables模式邏輯簡(jiǎn)單Kubernetes為每個(gè)Service創(chuàng)建一組iptables規(guī)則數(shù)據(jù)包進(jìn)入節(jié)點(diǎn)后根據(jù)規(guī)則隨機(jī)選擇一個(gè)后端Pod做DNAT。但iptables規(guī)則是鏈?zhǔn)狡ヅ涞漠?dāng)集群規(guī)模變大Service總數(shù)上千時(shí)規(guī)則匹配的耗時(shí)明顯增加還可能遇到規(guī)則更新不及時(shí)的問題。IPVS模式把規(guī)則從iptables遷移到了內(nèi)核的IPVS表里查找效率是哈希匹配性能遠(yuǎn)超iptables線性匹配。生產(chǎn)集群強(qiáng)烈建議把kube-proxy切到IPVS模式。怎么切直接在kube-proxy的啟動(dòng)參數(shù)里加--proxy-modeipvs或者在部署時(shí)配置ConfigMap。切完之后可以看到IPVS的轉(zhuǎn)發(fā)表ipvsadm -Ln這個(gè)命令會(huì)列出所有Service對(duì)應(yīng)的后端Pod IP列表和權(quán)重信息排查負(fù)載均衡不均勻的時(shí)候很有用。4.3 Service類型怎么選Service一共有四種類型按需選擇即可。ClusterIP是默認(rèn)類型只能集群內(nèi)部訪問適合服務(wù)間調(diào)用。NodePort在每個(gè)節(jié)點(diǎn)上開一個(gè)端口外部流量可以通過任意節(jié)點(diǎn)的IP加端口訪問適合臨時(shí)調(diào)試或小規(guī)模對(duì)外服務(wù)。LoadBalancer依賴于云廠商的負(fù)載均衡器把請(qǐng)求轉(zhuǎn)發(fā)到NodePort適合云上生產(chǎn)環(huán)境。ExternalName不創(chuàng)建轉(zhuǎn)發(fā)規(guī)則只是DNS層面的CNAME別名適合把集群外的老服務(wù)包裝成內(nèi)部Service訪問。實(shí)際項(xiàng)目中Service間互相調(diào)用時(shí)還有個(gè)細(xì)節(jié)要留意跨Service調(diào)用時(shí)數(shù)據(jù)包源IP會(huì)被DNAT干擾。默認(rèn)情況下后端Pod看到的是NodeIP或kube-proxy所在節(jié)點(diǎn)的IP不是發(fā)起請(qǐng)求的Pod IP。如果業(yè)務(wù)需要拿到真實(shí)客戶端IP比如審計(jì)需求需要配置externalTrafficPolicy: Local代價(jià)是流量可能分布不均需要根據(jù)業(yè)務(wù)需求權(quán)衡。5. Headless Service把負(fù)載均衡拋到一邊的特殊通信方式有些場(chǎng)景不需要負(fù)載均衡反而需要直接拿到每個(gè)Pod的真實(shí)IP。最典型的就是有狀態(tài)應(yīng)用——比如Elasticsearch集群、Kafka、Cassandra這些。它們需要知道集群里每個(gè)Peer節(jié)點(diǎn)的真實(shí)地址來組成集群如果通過ClusterIP負(fù)載均衡過去節(jié)點(diǎn)間互相通信時(shí)根本不知道自己在跟哪個(gè)節(jié)點(diǎn)說話。Headless Service就是為此設(shè)計(jì)的。創(chuàng)建Service時(shí)把clusterIP指定為None這個(gè)Service就不分配ClusterIP了DNS會(huì)直接把后端Pod的IP列表返回給調(diào)用方。舉個(gè)例子創(chuàng)建一個(gè)headless服務(wù)apiVersion: v1 kind: Service metadata: name: es-cluster spec: clusterIP: None selector: app: elasticsearch ports: - port: 9300 targetPort: 9300之后通過DNS查詢es-cluster.default.svc.cluster.local會(huì)得到所有匹配Pod的IP列表。配合StatefulSetPod的DNS名格式固定為podname.servicename.namespace.svc.cluster.local也就是穩(wěn)定網(wǎng)絡(luò)標(biāo)識(shí)。比如es-0.es-cluster.default.svc.cluster.local即使Pod重建這個(gè)域名也保持不變因?yàn)镾tatefulSet保證Pod的名字和序號(hào)不變。利用這個(gè)機(jī)制一個(gè)Pod根本不需要知道其他Pod的IP只需要按照約定好的域名規(guī)則去拼接就可以了。這也是StatefulSet應(yīng)用的標(biāo)準(zhǔn)做法Elasticsearch的elasticsearch.yml里配置discovery.seed_hosts為主機(jī)列表就是通過headless service解析出來的。Kafka則利用這個(gè)機(jī)制維護(hù)節(jié)點(diǎn)間的broker列表。這里有個(gè)容易犯的錯(cuò)headless service雖然能拿到Pod IP但這些IP列表是DNS返回的如果Pod數(shù)量很大DNS響應(yīng)包會(huì)非常大可能觸發(fā)UDP截?cái)鄦栴}。遇到這種情況建議開啟DNS的TCP查詢支持或者通過StatefulSet的方式直接用域名而不是IP列表減輕DNS壓力。6. 實(shí)操搭建一套Pod通信驗(yàn)證環(huán)境前面講了很多理論這部分我通過一個(gè)完整的示例把所有通信方式串起來實(shí)際驗(yàn)證一遍。6.1 準(zhǔn)備測(cè)試環(huán)境假設(shè)你已經(jīng)有一個(gè)Kubernetes集群Minikube或kind都能用先創(chuàng)建一個(gè)命名空間kubectl create ns net-test然后部署兩個(gè)Deployment分別跑一個(gè)簡(jiǎn)單的HTTP服務(wù)和一個(gè)客戶端工具。為了方便調(diào)試直接用nginx和busybox的組合apiVersion: apps/v1 kind: Deployment metadata: name: web-server namespace: net-test spec: replicas: 2 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80 --- apiVersion: apps/v1 kind: Deployment metadata: name: debug-client namespace: net-test spec: replicas: 1 selector: matchLabels: app: client template: metadata: labels: app: client spec: containers: - name: busybox image: busybox:1.36 command: [sleep, 3600]部署完成后分別驗(yàn)證幾種通信方式是否正常。6.2 逐一驗(yàn)證不同通信方式先找到客戶端Pod的名字kubectl get pod -n net-test -l appclient拿到名字后進(jìn)入Pod內(nèi)部直接訪問nginx服務(wù)kubectl exec -it client-pod -n net-test -- wget -qO- http://web-server.default.svc.cluster.local這次請(qǐng)求走的是Service的ClusterIP轉(zhuǎn)發(fā)驗(yàn)證了通過Service通信的鏈路正常。如果返回了nginx的默認(rèn)頁面說明ClusterIP、kube-proxy、后端Pod轉(zhuǎn)發(fā)整個(gè)鏈路都是通的。接著驗(yàn)證直連Pod IP的通信方式。先查詢某個(gè)web Pod的IPkubectl get pod -n net-test -l appweb -o wide然后在客戶端Pod里用wget加--no-check-certificate直接訪問這個(gè)IPkubectl exec -it client-pod -n net-test -- wget -qO- http://pod-ip如果兩個(gè)Pod在同一節(jié)點(diǎn)走的是前面說的Bridge路徑如果在不同節(jié)點(diǎn)則走Overlay封裝。不管哪種都返回nginx默認(rèn)頁面就說明Pod直連通信正常。再驗(yàn)證同一個(gè)Pod內(nèi)多容器的通信改一下web-server的Deployment加一個(gè)sidecar容器spec: containers: - name: nginx image: nginx:1.25 - name: sidecar image: busybox:1.36 command: [sleep, 3600]然后進(jìn)入sidecar容器訪問nginxkubectl exec -it web-pod -c sidecar -n net-test -- wget -qO- http://localhostlocalhost訪問成功說明同Pod內(nèi)容器共享網(wǎng)絡(luò)棧的機(jī)制正常。6.3 順手做一些網(wǎng)絡(luò)觀察通信驗(yàn)證通了之后可以進(jìn)一步觀察網(wǎng)絡(luò)細(xì)節(jié)。在web Pod里看一眼路由表kubectl exec -it web-pod -n net-test -- ip route會(huì)看到類似這樣的輸出default via 10.244.0.1 dev eth0 10.244.0.0/24 dev eth0 scope link src 10.244.0.20這個(gè)default網(wǎng)關(guān)就是節(jié)點(diǎn)上的cni0網(wǎng)橋的IP。每個(gè)Pod的數(shù)據(jù)包只要是出Pod的都會(huì)先到這個(gè)網(wǎng)關(guān)由節(jié)點(diǎn)決定是橋接給同節(jié)點(diǎn)Pod還是封裝后發(fā)往其他節(jié)點(diǎn)。把這個(gè)路由表記住排查網(wǎng)絡(luò)異常的時(shí)候非常有用——如果Pod的默認(rèn)路由丟了這個(gè)Pod就徹底失聯(lián)了。7. 排查Pod間通信問題的實(shí)戰(zhàn)經(jīng)驗(yàn)7.1 常見問題速查表Pod間通信的故障教科書上看病難但實(shí)際總結(jié)下來高發(fā)的其實(shí)只有幾類。我直接列一個(gè)速查表照著排查效率會(huì)高很多。現(xiàn)象可能原因排查命令同一節(jié)點(diǎn)Pod互通跨節(jié)點(diǎn)Pod不通Overlay網(wǎng)絡(luò)問題比如VXLAN端口被封、Flannel路由表丟失檢查節(jié)點(diǎn)上ip route和ethtool隧道接口狀態(tài)同節(jié)點(diǎn)Pod也不通CNI網(wǎng)橋異常cni0接口或iptables規(guī)則被手動(dòng)改動(dòng)ip link show cni0查看接口是否存在iptables -t nat -L CNI-*查看規(guī)則Service訪問不通Pod直連正常kube-proxy規(guī)則異?;騍ervice selector沒匹配到Podkubectl get endpoints service確認(rèn)Endpoints存在ipvsadm -Ln檢查轉(zhuǎn)發(fā)規(guī)則集群內(nèi)DNS解析不到Service名CoreDNS故障或Pod的DNS配置不正確kubectl get pod -n kube-system -l k8s-appkube-dnsnslookup service名.namespace.svc.cluster.local跨命名空間訪問不通沒寫完整域名或NetworkPolicy阻擋檢查yaml里Service名格式是否正確kubectl get networkpolicy -n namespace從節(jié)點(diǎn)訪問ClusterIP通但Pod內(nèi)不通節(jié)點(diǎn)iptables對(duì)轉(zhuǎn)發(fā)鏈設(shè)置了DROP或Pod的所屬節(jié)點(diǎn)被排除在外檢查每臺(tái)節(jié)點(diǎn)的iptables -L FORWARD的默認(rèn)策略這個(gè)表看著簡(jiǎn)單是我排查了無數(shù)真實(shí)線上故障后沉淀下來的。每個(gè)問題背后基本都有對(duì)應(yīng)的坑下面挑兩個(gè)高頻的細(xì)說一下。7.2 高發(fā)問題一Service沒匹配到PodService建了Pod也跑了但訪問ClusterIP就是超時(shí)。別急著重啟kube-proxy先查一下這個(gè)命令kubectl get endpoints service-name -n namespace如果Endpoints列表是空的說明Service的selector和Pod的label對(duì)不上或者后端Pod還沒就緒。我遇到過最典型的場(chǎng)景Pod加了版本號(hào)label如appweb-v1但Service的selector還停留在舊label appweb結(jié)果半天查不出原因。這種就是純手誤label和selector一對(duì)就立刻現(xiàn)形。還有就緒探針的問題。Pod雖然Running但沒通過readinessProbe檢查不會(huì)進(jìn)Endpoints列表。這時(shí)候看到的現(xiàn)象是Pod明明是Running狀態(tài)但Service就是選不到它。7.3 高發(fā)問題二開通網(wǎng)絡(luò)策略后服務(wù)全斷大團(tuán)隊(duì)合作時(shí)有人引入NetworkPolicy做安全加固后網(wǎng)絡(luò)反而全斷了。這個(gè)問題的根源通常是對(duì)NetworkPolicy的默認(rèn)行為不夠理解沒有NetworkPolicy時(shí)默認(rèn)允許所有流量但只要有NetworkPolicy匹配到某個(gè)Pod就只有顯式允許的流量能進(jìn)來。要排查網(wǎng)絡(luò)策略問題我是先反推的從客戶端Pod去訪問目標(biāo)Pod看中間的路徑上有沒有哪道策略被攔了。首先看目標(biāo)Pod所在namespace有沒有NetworkPolicy限制了入站流量kubectl get networkpolicy -n namespace然后看具體的策略規(guī)則里allow的來源標(biāo)簽和端口能不能對(duì)上天比如只允許appfrontend的Pod訪問TCP 80端口但客戶端Pod的label寫錯(cuò)了自然就撞墻了。這種時(shí)候把podSelector對(duì)準(zhǔn)或者臨時(shí)加一條允許規(guī)則放通測(cè)試流量等確認(rèn)了再做精細(xì)化收斂。繞來繞去排查Pod通信問題的最終奧義就一個(gè)字分段。把鏈路拆成Pod到節(jié)點(diǎn)網(wǎng)關(guān)、節(jié)點(diǎn)到節(jié)點(diǎn)、節(jié)點(diǎn)到目標(biāo)Pod、Service轉(zhuǎn)發(fā)這幾段每一段單獨(dú)驗(yàn)證問題定位就快了。8. 選型建議和幾個(gè)踩坑教訓(xùn)說到選型網(wǎng)絡(luò)上默認(rèn)就是Flannel但生產(chǎn)環(huán)境我會(huì)優(yōu)先用Calico。核心原因是Calico在性能、NetworkPolicy支持和可觀測(cè)性上都有明顯優(yōu)勢(shì)。Flannel能做的Calico都能做而Calico的策略能力Flannel沒有。如果團(tuán)隊(duì)維護(hù)成本敏感Calico的部署也就多幾條配置而已不值得省這個(gè)事。Cilium適合有明確的可觀測(cè)性和能力擴(kuò)展需求團(tuán)隊(duì)eBPF的魔力邊用邊體會(huì)。但它對(duì)內(nèi)核版本的要求確實(shí)是個(gè)硬門檻老機(jī)器裝不上就別硬上別為趕時(shí)髦給自己埋坑。網(wǎng)絡(luò)插件選好之后還要做好網(wǎng)絡(luò)運(yùn)維的基本功。節(jié)點(diǎn)上常用的網(wǎng)絡(luò)排查命令比如ip、route、iptables、ipvsadm、tcpdump一定要熟練到肌肉記憶。我處理過的一次線上故障就是靠tcpdump定位的一個(gè)跨節(jié)點(diǎn)的PostgreSQL集群連接大量超時(shí)懷疑網(wǎng)絡(luò)問題在源節(jié)點(diǎn)和目標(biāo)節(jié)點(diǎn)同時(shí)抓包很快就發(fā)現(xiàn)VXLAN的UDP 8472端口被安全組規(guī)則過濾了。如果不會(huì)抓包對(duì)比分析這個(gè)故障排查可能要拖好幾個(gè)小時(shí)。另外建議大家給節(jié)點(diǎn)上的關(guān)鍵網(wǎng)絡(luò)組件做監(jiān)控告警重點(diǎn)盯cni0接口的狀態(tài)、Overlay隧道的收發(fā)丟包率、kube-proxy的規(guī)則同步延遲。這幾個(gè)指標(biāo)異常往往比業(yè)務(wù)側(cè)報(bào)警來得更早。最后說一個(gè)很多人都忽略的實(shí)踐升級(jí)或者變更網(wǎng)絡(luò)相關(guān)配置前先備份iptables和路由表。尤其是你手動(dòng)調(diào)整過宿主機(jī)網(wǎng)絡(luò)策略的集群升級(jí)CNI插件時(shí)很容易出現(xiàn)規(guī)則互相覆蓋的情況。有一次我們升級(jí)Calico版本升級(jí)過程中節(jié)點(diǎn)上舊的BGP session沒有正常關(guān)閉新版本啟動(dòng)后又建立了一個(gè)新的路由表瞬間多了很多重復(fù)路由導(dǎo)致部分Pod間通信時(shí)有時(shí)無。當(dāng)時(shí)就是因?yàn)橛袀浞萋酚杀砜焖倩貪L才止損。Pod間通信整體上不是多玄妙的機(jī)制核心就是網(wǎng)絡(luò)模型加幾種轉(zhuǎn)發(fā)鏈路。把原理吃透再把常用的驗(yàn)證手段練熟Kubernetes網(wǎng)絡(luò)這塊基本就穩(wěn)了。