K8s 服務(wù)訪問(wèn)不通?從 Pod、Service 到 Ingress 一步步查
問(wèn)題背景
Kubernetes 里服務(wù)訪問(wèn)不通是比節(jié)點(diǎn) NotReady 更常見(jiàn)的故障場(chǎng)景。相比節(jié)點(diǎn) NotReady 這種基礎(chǔ)設(shè)施層的問(wèn)題,服務(wù)訪問(wèn)不通直接表現(xiàn)為業(yè)務(wù)不可用——用戶能打開(kāi)頁(yè)面但數(shù)據(jù)加載不出來(lái),API 請(qǐng)求返回 502 或 504,Pod 之間內(nèi)部調(diào)用超時(shí)。這類問(wèn)題的根因分散在多個(gè)層次:Pod 本身沒(méi)啟動(dòng)、Service 配置錯(cuò)誤、Endpoint 沒(méi)有關(guān)聯(lián)到 Pod、網(wǎng)絡(luò)策略阻止了流量、Ingress 配置不對(duì)、DNS 解析失敗等等。
更重要的是,K8s 的網(wǎng)絡(luò)是虛擬網(wǎng)絡(luò),流量路徑不直觀。外部請(qǐng)求經(jīng)過(guò) NodePort 或 LoadBalancer 到達(dá)某個(gè)節(jié)點(diǎn),再由 kube-proxy 轉(zhuǎn)發(fā)到后端 Pod;Pod 之間的調(diào)用直接通過(guò) Service IP 或 DNS 名稱,但實(shí)際連接的是 Endpoint IP。路徑中間任何一環(huán)出問(wèn)題,都會(huì)導(dǎo)致訪問(wèn)不通。
這篇文章面向初中級(jí) Kubernetes 運(yùn)維和 DevOps 工程師,以一個(gè)典型的"服務(wù)訪問(wèn)返回 502"故障為線索,從 Pod、Service、Endpoint、kube-proxy、CNI、Ingress 六個(gè)層次系統(tǒng)講解排查方法。每個(gè)層次都有對(duì)應(yīng)的檢查命令、判斷邏輯和修復(fù)步驟。涉及的所有命令在 Kubernetes 1.24 及以上版本驗(yàn)證,部分命令更早版本表現(xiàn)可能略有差異,會(huì)在文中說(shuō)明。
適用場(chǎng)景
服務(wù)對(duì)外提供訪問(wèn),但返回 502 Bad Gateway 或 504 Gateway Timeout
Pod 之間通過(guò) Service 名稱調(diào)用,但請(qǐng)求超時(shí)或連接拒絕
通過(guò) Ingress 訪問(wèn)服務(wù),但 URL 無(wú)法解析或請(qǐng)求路由到錯(cuò)誤的后端
NodePort / LoadBalancer 類型的 Service 無(wú)法從外部訪問(wèn)
Headless Service 下的 Pod 之間無(wú)法通過(guò) DNS 互相發(fā)現(xiàn)
某個(gè)命名空間內(nèi)的 Pod 無(wú)法訪問(wèn)其他命名空間的服務(wù)
特定 IP 段或端口的流量被網(wǎng)絡(luò)策略阻斷
K8s 服務(wù)訪問(wèn)路徑全解析
在動(dòng)手排查之前,必須清楚流量從客戶端到后端 Pod 的完整路徑。不同類型的服務(wù),路徑不同。
場(chǎng)景一:集群內(nèi)部 Pod 訪問(wèn) Service(最常見(jiàn))
PodA -> ClusterIP:ServicePort -> kube-proxy (iptables/ipvs) -> EndpointIP:ContainerPort -> PodB
Pod 內(nèi)運(yùn)行的進(jìn)程訪問(wèn)http://service-name.namespace.svc.cluster.local:port,這個(gè) DNS 名稱被 CoreDNS 解析為 Service IP,然后流量被 kube-proxy 攔截并 DNAT 轉(zhuǎn)發(fā)到后端某個(gè) Endpoint IP。
場(chǎng)景二:集群外部通過(guò) NodePort 訪問(wèn)
外部客戶端 -> NodeIP:NodePort -> kube-proxy -> EndpointIP:ContainerPort -> PodB
外部請(qǐng)求到達(dá)任意節(jié)點(diǎn)的 NodePort,kube-proxy 將流量轉(zhuǎn)發(fā)到后端 Pod。不一定必須是目標(biāo) Pod 所在的節(jié)點(diǎn)。
場(chǎng)景三:通過(guò) Ingress 訪問(wèn)
外部客戶端 -> Ingress Controller Pod -> Ingress 規(guī)則匹配 -> Service:NodePort -> kube-proxy -> PodB
流量先打到 Ingress Controller(通常是一個(gè) NodePort 或 LoadBalancer 類型的 Service),Ingress Controller 根據(jù) Host 和 Path 規(guī)則找到對(duì)應(yīng)的 Backend Service,然后通過(guò) Service 訪問(wèn)到后端 Pod。
理解了這三條路徑,就知道每個(gè)環(huán)節(jié)該查什么。
第一步:確認(rèn)問(wèn)題范圍和現(xiàn)象
1.1 確認(rèn)問(wèn)題是否真實(shí)存在
不要假設(shè)問(wèn)題存在,先復(fù)現(xiàn)并確認(rèn)現(xiàn)象:
# 通過(guò) kubectl 確認(rèn)目標(biāo) Service 存在 kubectl get svc -n# 確認(rèn) Pod 是否存在且 Running kubectl get pods -n -l app= # 如果 Pod 不在 Running 狀態(tài),先解決 Pod 問(wèn)題 # (Pod 都沒(méi)起來(lái),Service 肯定訪問(wèn)不了)
1.2 從業(yè)務(wù)角度確認(rèn)問(wèn)題現(xiàn)象
# 測(cè)試外部能否訪問(wèn)(用 NodePort 或 LoadBalancer IP) curl -v http://: / # 典型 502 錯(cuò)誤: # HTTP/1.1 502 Bad Gateway # Server: nginx/1.24.0 # 502 Bad Gateway
# 典型超時(shí)錯(cuò)誤: # curl: (7) Failed to connect toport : Connection timed out
1.3 確認(rèn)問(wèn)題影響范圍
# 查看該 Service 有多少個(gè)健康的 Endpoint kubectl get endpoints -n# 如果 endpoints 列表為空或數(shù)量少于預(yù)期,說(shuō)明 Service 沒(méi)有關(guān)聯(lián)到健康的 Pod # 如果 endpoints 數(shù)量正常,問(wèn)題可能在 Ingress 或更上層 # 查看 Pod 分布 kubectl get pods -n -o wide | grep
第二步:從 Pod 層開(kāi)始排查
Pod 是流量的最終目的地。如果 Pod 本身不正常,前面查什么都不重要。
2.1 確認(rèn) Pod 是否存在且 Running
kubectl get pods -n-o wide # 確認(rèn)所有副本都在 Running # 如果有非 Running 狀態(tài)的 Pod: kubectl get pods -n --field-selector=status.phase!=Running
2.2 查看 Pod 事件
Pod 創(chuàng)建失敗或重啟時(shí),Events 是最直接的線索:
kubectl describe pod-n # 重點(diǎn)看最后 20 行 Events kubectl describe pod -n | tail -20 # 常見(jiàn)的問(wèn)題線索: # - ImagePullBackOff: 鏡像拉取失敗,檢查鏡像名稱、tag、私有倉(cāng)庫(kù)認(rèn)證 # - CrashLoopBackOff: 容器啟動(dòng)后立即退出,檢查應(yīng)用啟動(dòng)命令和健康檢查 # - CreateContainerConfigError: 配置錯(cuò)誤,檢查 ConfigMap/Secret 掛載 # - OOMKilled: 內(nèi)存不足被殺,檢查資源限制和實(shí)際使用 # - Evicted: 被驅(qū)逐,檢查節(jié)點(diǎn)資源壓力
2.3 查看容器內(nèi)部日志
# 查看當(dāng)前日志(stdout) kubectl logs-n # 如果容器重啟過(guò),查看上次運(yùn)行的日志(用于排查啟動(dòng)后立即崩潰的問(wèn)題) kubectl logs -n --previous # 如果是多容器 Pod,指定容器名 kubectl logs -n -c # 實(shí)時(shí)查看日志 kubectl logs -f -n --tail=100
2.4 登錄 Pod 內(nèi)部排查
如果日志沒(méi)有明顯線索,可以進(jìn) Pod 內(nèi)部測(cè)試網(wǎng)絡(luò)連通性:
# 進(jìn)容器(需要容器內(nèi)有 bash) kubectlexec-it-n -- /bin/bash # 如果容器沒(méi)有 bash,用 sh kubectlexec-it -n -- /bin/sh # 確認(rèn)容器進(jìn)程是否監(jiān)聽(tīng)在預(yù)期端口 # Java 應(yīng)用:ps aux | grep java # Nginx / Python / Node.js:netstat -tlnp 或 ss -tlnp # 注意:很多精簡(jiǎn)鏡像沒(méi)有這些命令,可以臨時(shí)安裝或用其他方式驗(yàn)證 # 測(cè)試 localhost 端口是否監(jiān)聽(tīng) wget -qO- http://127.0.0.1: /healthz # 或 curl -s http://127.0.0.1: /healthz # 如果 localhost 訪問(wèn)正常,但通過(guò) Service 訪問(wèn)有問(wèn)題,說(shuō)明問(wèn)題在 Service 層
2.5 檢查 Pod 資源限制
資源限制設(shè)置不當(dāng)會(huì)導(dǎo)致 Pod 被 OOMKill 或被 throttle,導(dǎo)致請(qǐng)求超時(shí):
# 查看 Pod 的資源請(qǐng)求和限制 kubectl get pod-n -o jsonpath='{.spec.containers[*].resources}' # 查看 Pod 實(shí)際資源使用(需要 metrics-server) kubectl top pod -n # 如果資源使用接近限制,說(shuō)明需要調(diào)高限制或優(yōu)化應(yīng)用內(nèi)存使用
第三步:排查 Service 層
確認(rèn) Pod 正常后,轉(zhuǎn)到 Service 層。Service 是 Pod 的訪問(wèn)入口,也是最容易配置出錯(cuò)的地方。
3.1 確認(rèn) Service 配置正確
# 查看 Service 完整配置 kubectl get svc-n -o yaml # 重點(diǎn)檢查以下字段: # spec.selector: 是否正確關(guān)聯(lián)到目標(biāo) Pod 的 label # spec.ports: 端口號(hào)、目標(biāo)端口、協(xié)議是否正確 # spec.type: ClusterIP / NodePort / LoadBalancer
常見(jiàn)配置錯(cuò)誤一:selector 寫錯(cuò)
# 錯(cuò)誤示例:selector 寫成了 app: web-frontend,但 Pod 的 label 是 app: web spec: selector: app:web-frontend# 這個(gè) selector 找不到任何 Pod
正確做法:
# 確認(rèn) Pod 實(shí)際的 label kubectl get pods -n--show-labels # 查看 Service 的 selector 是否能匹配到 Pod kubectl get pods -n -l app=web-frontend # 如果沒(méi)有輸出,說(shuō)明 selector 寫錯(cuò)了
常見(jiàn)配置錯(cuò)誤二:端口配置錯(cuò)誤
# 錯(cuò)誤示例:targetPort 寫成了字符串,但容器實(shí)際監(jiān)聽(tīng)的端口是數(shù)字 spec: ports: -port:80 targetPort:"http" # 容器沒(méi)有 named port "http",應(yīng)該寫 8080 protocol:TCP
3.2 確認(rèn) Endpoints 存在且健康
Service 通過(guò) Endpoints 關(guān)聯(lián)到 Pod。如果 Endpoints 為空,說(shuō)明 Service 找不到健康的后端:
# 查看 Endpoints kubectl get endpoints-n # 正常輸出: # NAME ENDPOINTS # my-service 10.244.1.15:8080,10.244.2.23:8080 # 如果為空: # 查看 Endpoints 詳細(xì)信息(關(guān)聯(lián)了哪些 Pod IP 和端口) kubectl describe endpoints -n
Endpoints 為空的排查方法:
Endpoints 為空通常有兩個(gè)原因:selector 匹配不到 Pod,或者匹配的 Pod 沒(méi)有 ready(readinessProbe 失敗)。
# 1. 確認(rèn) selector 是否匹配到 Pod kubectl get pods -n--show-labels # 手動(dòng)驗(yàn)證:Service selector 的每個(gè) key-value 是否都能在 Pod label 中找到 # 2. 確認(rèn) Pod 的 Ready 狀態(tài) kubectl get pods -n -o wide # 關(guān)注 Ready 列:應(yīng)該是 1/1、2/2 等,如果不是,說(shuō)明 readinessProbe 有問(wèn)題 # 3. 查看 Pod 的 readinessProbe 配置 kubectl get pod -n -o jsonpath='{.spec.containers[*].readinessProbe}'
readinessProbe 配置錯(cuò)誤的常見(jiàn)場(chǎng)景:
# 錯(cuò)誤示例:readinessProbe HTTP 路徑返回 404,但應(yīng)用實(shí)際路徑是 /api/health readinessProbe: httpGet: path:/healthz# 應(yīng)用沒(méi)有 /healthz 端點(diǎn) port:8080 initialDelaySeconds:5 periodSeconds:5
3.3 測(cè)試從集群內(nèi)部訪問(wèn) Service
從 Pod 內(nèi)部訪問(wèn) Service 是最基礎(chǔ)的測(cè)試。如果這一步失敗,說(shuō)明 kube-proxy 或 CoreDNS 有問(wèn)題:
# 創(chuàng)建一個(gè)臨時(shí)測(cè)試 Pod kubectl run -ntestpod --image=busybox:1.36 --restart=Never -it --rm -- sh # 在 Pod 內(nèi)部執(zhí)行以下測(cè)試: # 1. DNS 解析測(cè)試 nslookup # 正常輸出: # Name: # Address: # 如果 nslookup 失?。―NS 問(wèn)題) nslookup kubernetes.default # 如果連 kubernetes 都解析不了,說(shuō)明 CoreDNS 有問(wèn)題 # 2. 通過(guò) Service IP 訪問(wèn) wget -qO- http:// : /healthz # 3. 通過(guò) DNS 名稱訪問(wèn) wget -qO- http:// . .svc.cluster.local: /healthz # 4. 測(cè)試不同端口(有些服務(wù)可能有多個(gè)端口) wget -qO- http:// . .svc.cluster.local: / # 如果 DNS 名稱能解析但連接超時(shí): # 說(shuō)明 kube-proxy 沒(méi)有正確轉(zhuǎn)發(fā)流量,檢查 kube-proxy # 如果 Connection refused: # 說(shuō)明容器內(nèi)端口沒(méi)監(jiān)聽(tīng)或 Service targetPort 配置錯(cuò)誤
3.4 測(cè)試 NodePort 類型 Service
如果 Service 是 NodePort 類型,在所有節(jié)點(diǎn)的 NodePort 上都應(yīng)該能訪問(wèn):
# 查看 NodePort kubectl get svc-n | grep NodePort # 輸出示例: # NodePort: http 30080/TCP # NodePort 是 30080,在所有節(jié)點(diǎn)上都能通過(guò) <任意節(jié)點(diǎn)IP>:30080 訪問(wèn) # 從集群外部測(cè)試(如果有 firewall 規(guī)則,需要先確認(rèn) NodePort 已放行) curl -v http://<任意節(jié)點(diǎn)IP>:30080/ # 如果某個(gè)節(jié)點(diǎn)能訪問(wèn)、另一個(gè)不能: # 檢查不能訪問(wèn)的節(jié)點(diǎn)上 kube-proxy 是否正常 ssh <目標(biāo)節(jié)點(diǎn)>"systemctl status kube-proxy"
第四步:排查 kube-proxy 層
kube-proxy 是 Service 流量的轉(zhuǎn)發(fā)引擎。它運(yùn)行在每個(gè)節(jié)點(diǎn)上,監(jiān)聽(tīng) Service 和 Endpoints 的變化,動(dòng)態(tài)更新本地的 iptables 或 ipvs 規(guī)則。如果 kube-proxy 異常,Service 流量轉(zhuǎn)發(fā)就會(huì)失敗。
4.1 檢查 kube-proxy 是否運(yùn)行
# kube-proxy 以 DaemonSet 形式運(yùn)行 kubectl get pods -n kube-system -l k8s-app=kube-proxy # 查看 kube-proxy 日志 kubectl logs -n kube-system -l k8s-app=kube-proxy --tail=50 # 如果 kube-proxy Pod 不在 Running,檢查詳情 kubectl describe pod -n kube-system -l k8s-app=kube-proxy
4.2 檢查 kube-proxy 的轉(zhuǎn)發(fā)模式
kube-proxy 支持 iptables 和 ipvs 兩種模式,默認(rèn)是 iptables。兩種模式的排查方法不同:
# 查看 kube-proxy 的啟動(dòng)參數(shù),確認(rèn)使用的是 iptables 還是 ipvs kubectl get configmap -n kube-system kube-proxy -o yaml | grep mode # 或者在節(jié)點(diǎn)上查看 ssh"grep mode /var/lib/kubelet/config.yaml" # iptables 模式:默認(rèn),性能稍差,但穩(wěn)定 # ipvs 模式:性能更好,但配置復(fù)雜,某些內(nèi)核版本支持不好
4.3 iptables 模式排查
# SSH 到某個(gè)節(jié)點(diǎn)上查看 iptables 規(guī)則 # 1. 查看 NAT 表中關(guān)于某個(gè) Service 的規(guī)則 # 注意:規(guī)則很多,直接 grep ssh"sudo iptables -t nat -L -n | grep " # 輸出示例: # KUBE-SVC-XXXXXXXX tcp -- anywhere 10.109.12.34 tcp dpt:80 # KUBE-SEP-XXXXXXXX tcp -- anywhere anywhere statistic mode: probability mode: random 0.500000000 # KUBE-SEP-XXXXXXXX tcp -- anywhere 10.244.1.15 tcp dpt:8080 # KUBE-SVC-XXXXXXXX 是 Service 對(duì)應(yīng)的鏈 # KUBE-SEP-XXXXXXXX 是具體 Endpoint 的鏈 # 如果沒(méi)有這些規(guī)則,說(shuō)明 kube-proxy 沒(méi)有為這個(gè) Service 生成規(guī)則 # 2. 查看 KUBE-SERVICES 鏈中是否有該 Service ssh "sudo iptables -t nat -L KUBE-SERVICES -n | grep " # 3. 查看 FILTER 表的 FORWARD 鏈 ssh "sudo iptables -t filter -L FORWARD -n | grep KUBE" # 如果 FORWARD 鏈沒(méi)有 ACCEPT KUBE 相關(guān)的規(guī)則,可能 kube-proxy 沒(méi)有正確處理流量
4.4 ipvs 模式排查
# SSH 到節(jié)點(diǎn)上查看 ipvs 規(guī)則 ssh"sudo ipvsadm -L -n" # 正常輸出: # IP Virtual Server version 1.2.1 (size=4096) # TCP :80 rr # -> :8081 Masq 1 0 0 # -> :8082 Masq 1 0 0 # 如果 ipvsadm 沒(méi)有安裝或 ipvs 模塊未加載,kube-proxy 會(huì)回退到 iptables # 檢查 ipvs 模塊是否加載 ssh "lsmod | grep ip_vs"
4.5 kube-proxy 日志分析
# 查看 kube-proxy 是否有報(bào)錯(cuò) kubectl logs -n kube-system -l k8s-app=kube-proxy | grep -i error # 常見(jiàn)錯(cuò)誤: # "Failed to delete service" - 刪除 Service 時(shí)清理規(guī)則失敗 # "Failed to sync iptables" - 同步規(guī)則失敗,可能是權(quán)限問(wèn)題 # "ipvs struct not found" - ipvs 模式下某些連接狀態(tài)異常
第五步:排查網(wǎng)絡(luò)插件層(CNI)
如果 Service 層和 kube-proxy 都正常,但 Pod 之間還是不通,問(wèn)題可能在 CNI(容器網(wǎng)絡(luò)接口)層。CNI 負(fù)責(zé) Pod IP 分配和 Pod 之間的網(wǎng)絡(luò)連通性。
5.1 確認(rèn) Pod 網(wǎng)絡(luò)是否正常
# 在有問(wèn)題的 Pod 內(nèi)測(cè)試 kubectlexec-it-n -- /bin/sh # 測(cè)試到另一個(gè) Pod 的連通性(通過(guò) Pod IP,不是 Service IP) ping -c 3 <另一個(gè)Pod的IP> # 測(cè)試到網(wǎng)關(guān)的連通性 ip route # 默認(rèn)網(wǎng)關(guān)應(yīng)該是 CNI 網(wǎng)橋或 flannel.1 / calico* 等接口 # 如果 ping 不通: # 1. 確認(rèn)兩邊的 Pod 是否在同一個(gè)網(wǎng)段 # 2. 確認(rèn) CNI 插件創(chuàng)建的接口是否存在 ip addr | grep -E"flannel|calico|cni|docker"
5.2 常見(jiàn) CNI 插件排查
Flannel 排查:
# 查看 flannel 是否為所有節(jié)點(diǎn)創(chuàng)建了網(wǎng)絡(luò) kubectl get pods -n kube-system -l app=flannel # 在節(jié)點(diǎn)上查看 flannel.1 接口 ip addr show flannel.1 # 查看 flannel 的網(wǎng)絡(luò)范圍(Pod CIDR) kubectl get cm -n kube-system kube-flannel-cfg -o yaml | grep -A 3"net-conf.json" # 典型問(wèn)題:節(jié)點(diǎn) Pod CIDR 沒(méi)有被 flannel 分配 # 在節(jié)點(diǎn)上查看: ip addr show flannel.1 # 如果 flannel.1 沒(méi)有 UP,說(shuō)明該節(jié)點(diǎn)的 CNI 網(wǎng)絡(luò)沒(méi)有正常初始化
Calico 排查:
# 查看 calico-node 是否 Running kubectl get pods -n kube-system -l k8s-app=calico-node # 查看 calico-node 日志 kubectl logs -n kube-system -l k8s-app=calico-node --tail=100 # 查看 BGP 對(duì)等連接(需要 calicoctl) calicoctl node status # 查看 IP 池 calicoctl get ippool -o wide # 典型問(wèn)題:節(jié)點(diǎn)沒(méi)有分配到 IP 池地址 # 查看節(jié)點(diǎn) IP 分配情況 calicoctl get workloadendpoints -o wide
5.3 跨節(jié)點(diǎn) Pod 通信測(cè)試
# 獲取兩個(gè)不同節(jié)點(diǎn)上 Pod 的 IP kubectl get pods -o wide --all-namespaces | grep Running # 在源 Pod 內(nèi)測(cè)試到目標(biāo) Pod IP 的連通性 kubectlexec-it <源Pod> -n-- ping -c 5 <目標(biāo)PodIP> # 如果同節(jié)點(diǎn) Pod 通信正常,跨節(jié)點(diǎn)不通: # 1. 檢查源節(jié)點(diǎn)到目標(biāo)節(jié)點(diǎn)的路由(VPC 或物理網(wǎng)絡(luò)層) # 2. 檢查 CNI 的跨節(jié)點(diǎn)隧道是否正常(flannel 用 VXLAN,calico 可以用 BGP 或 VXLAN) # 3. 檢查是否有防火墻規(guī)則阻止了 CNI 隧道端口(flannel UDP 8472,Calico IP-in-IP 4) # 測(cè)試特定端口(如果 ping 通了但服務(wù)訪問(wèn)不通,可能是防火墻或端口問(wèn)題) kubectlexec-it <源Pod> -n -- nc -zv <目標(biāo)PodIP> <端口>
第六步:排查 Ingress 層
如果 Service 本身正常,但通過(guò) Ingress 訪問(wèn)不通,問(wèn)題就在 Ingress 層。Ingress 是外部流量的入口,也是配置最復(fù)雜的一層。
6.1 確認(rèn) Ingress Controller 正常運(yùn)行
# Ingress Controller 通常部署在 kube-system 命名空間 kubectl get pods -n kube-system | grep -E"ingress|nginx" # 常見(jiàn) Ingress Controller: # nginx-ingress-controller(社區(qū)版或 Nginx Inc 版) # ingress-nginx(Kubernetes 官方維護(hù)的 nginx-ingress) # traefik # 如果 Ingress Controller 沒(méi)有 Running: kubectl describe pod -n kube-system# 如果 Ingress Controller Pod 重啟頻繁: kubectl logs -n kube-system --previous | tail -50
6.2 查看 Ingress 規(guī)則配置
# 查看所有 Ingress 資源 kubectl get ingress -A # 查看具體 Ingress 的配置 kubectl get ingress-n -o yaml # 重點(diǎn)檢查: # spec.rules: Host 和 Path 配置 # spec.tls: HTTPS 證書(shū)配置 # spec.backend: 默認(rèn)后端(如果沒(méi)有匹配規(guī)則時(shí)使用)
6.3 確認(rèn) Ingress 和 Service 的關(guān)聯(lián)
# Ingress 的 backend.service.name 必須和目標(biāo) Service 名稱一致 kubectl get ingress-n -o jsonpath='{.spec.rules[*].http.paths[*].backend.service}' # 確認(rèn) Ingress backend 的 port 端口和 Service port 一致 kubectl get ingress -n -o jsonpath='{.spec.rules[*].http.paths[*].backend.service.port}'
6.4 Ingress 日志分析
# 查看 Ingress Controller 日志 kubectl logs -n kube-system-f --tail=100 # 搜索訪問(wèn)日志 # nginx-ingress-controller 日志格式: # - [ ] " " " " " " # 搜索 502 / 503 / 504 錯(cuò)誤 kubectl logs -n kube-system | grep" 502 | 503 | 504 " # 搜索特定 Host 的訪問(wèn) kubectl logs -n kube-system | grep"Host: " # 如果沒(méi)有日志輸出,說(shuō)明請(qǐng)求根本沒(méi)到 Ingress Controller # 檢查 DNS 解析、LB 配置、NodePort 是否正確
6.5 逐層排查 Ingress 流量路徑
如果確認(rèn) Ingress 也有問(wèn)題,按以下路徑逐層排查:
1. 外部 -> DNS 解析 -> Ingress Controller 的外部 IP/NodePort 2. Ingress Controller -> Ingress 規(guī)則匹配 -> Backend Service 3. Backend Service -> kube-proxy -> Endpoint -> Pod
層級(jí)一:確認(rèn)外部能訪問(wèn)到 Ingress Controller
# 獲取 Ingress Controller 的 NodePort 或 LoadBalancer IP kubectl get svc -n kube-system | grep -E"ingress|nginx" # 測(cè)試從外部直接訪問(wèn) NodePort(繞過(guò) DNS) curl -v http://<任意節(jié)點(diǎn)IP>:/ # 如果 NodePort 能訪問(wèn),但域名訪問(wèn)不行: # 說(shuō)明 DNS 解析有問(wèn)題,域名沒(méi)有解析到正確的 IP
層級(jí)二:確認(rèn) Ingress 規(guī)則被正確加載
# 查看 Ingress Controller 進(jìn)程的配置(nginx.conf) kubectlexec-n kube-system-- cat /etc/nginx/nginx.conf # 搜索對(duì)應(yīng)的 server {} 塊 kubectlexec-n kube-system -- grep -A 20"server_name " # 確認(rèn) upstream {} 塊中的 server 列表是否包含正確的 Endpoint IP kubectlexec-n kube-system -- grep -A 10"upstream "
6.6 TLS/HTTPS 相關(guān)問(wèn)題
# 查看 Ingress 的 TLS 配置 kubectl get ingress-n -o yaml | grep -A 10 tls # 常見(jiàn) TLS 問(wèn)題: # 1. Secret 不存在 kubectl get secret -n # 2. Secret 類型不是 kubernetes.io/tls # 3. 證書(shū)過(guò)期(Let's Encrypt 證書(shū)有效期 90 天) # 4. 證書(shū)和域名不匹配 # 測(cè)試 HTTPS 訪問(wèn) curl -v https:// / --insecure # --insecure 參數(shù)跳過(guò)證書(shū)驗(yàn)證,用于排查是證書(shū)問(wèn)題還是連接問(wèn)題 # 如果加 --insecure 能通,說(shuō)明是證書(shū)配置問(wèn)題
第七步:排查 DNS 解析問(wèn)題
DNS 問(wèn)題是導(dǎo)致"服務(wù)訪問(wèn)不通"的重災(zāi)區(qū)。Pod 訪問(wèn) Service 通過(guò) DNS 名稱,如果 DNS 解析失敗或緩慢,所有依賴 Service 名稱的調(diào)用都會(huì)失敗。
7.1 測(cè)試 DNS 解析
# 在 Pod 內(nèi)測(cè)試 DNS kubectlexec-it-n -- nslookup kubernetes.default kubectlexec-it -n -- nslookup . .svc.cluster.local # 如果 nslookup 不存在,用 dig kubectlexec-it -n -- apt-get update && apt-get install -y dnsutils kubectlexec-it -n -- dig +short kubernetes.default.svc.cluster.local # 測(cè)試完整 DNS 名稱 kubectlexec-it -n -- getent hosts . .svc.cluster.local
7.2 查看 CoreDNS 狀態(tài)
# CoreDNS 通常以 Deployment 形式運(yùn)行在 kube-system kubectl get pods -n kube-system -l k8s-app=kube-dns # 查看 CoreDNS 日志 kubectl logs -n kube-system -l k8s-app=kube-dns --tail=100 # 查看 CoreDNS 配置 kubectl get configmap -n kube-system coredns -o yaml
7.3 DNS 解析慢的常見(jiàn)原因
DNS 解析本身不應(yīng)該很慢,但如果 Pod 數(shù)量很大或 DNS 查詢量很高,可能出現(xiàn)延遲。常見(jiàn)原因:
DNS 查詢并發(fā)限制:Linux 內(nèi)核對(duì)每個(gè)進(jìn)程/容器的 DNS 并發(fā)查詢有限制
ndots 配置過(guò)高:K8s 默認(rèn) ndots=5,意味著任何帶 5 個(gè)以下點(diǎn)的名稱都會(huì)先查集群 DNS 再查外部 DNS,導(dǎo)致所有外部 DNS 查詢都走集群 DNS
優(yōu)化 ndots 配置(在 Pod 的 dnsPolicy 不為 Default 時(shí)生效):
# 在 Pod spec 中添加 dnsConfig spec: dnsConfig: options: -name:ndots value:"2"# 默認(rèn) 5,改成 2 減少集群 DNS 查詢 -name:timeout value:"2" -name:attempts value:"2"
第八步:排查網(wǎng)絡(luò)策略(NetworkPolicy)
如果以上所有層都正常,但流量就是不通,可能是 NetworkPolicy 阻止了流量。
8.1 查看命名空間的 NetworkPolicy
# 查看該命名空間是否應(yīng)用了 NetworkPolicy kubectl get networkpolicy -n# 查看具體的 NetworkPolicy 規(guī)則 kubectl get networkpolicy -n -o yaml
8.2 常見(jiàn) NetworkPolicy 配置錯(cuò)誤
# 錯(cuò)誤示例:只允許特定標(biāo)簽的 Pod 訪問(wèn) spec: podSelector: matchLabels: role:frontend ingress: -from: -podSelector: matchLabels: role:nginx# 只有帶 nginx 標(biāo)簽的 Pod 能訪問(wèn) # 但實(shí)際請(qǐng)求來(lái)自其他標(biāo)簽的 Pod,所以被阻止
臨時(shí)測(cè)試:放行所有流量排查是否是 NetworkPolicy 問(wèn)題
# 如果確認(rèn)是 NetworkPolicy 問(wèn)題,需要臨時(shí)放行,可以先刪除 Policy kubectl delete networkpolicy-n # 或者臨時(shí)添加一條允許所有流量的 Policy kubectl apply -f - <<'EOF' apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: ? name: allow-all ? namespace: spec: podSelector: {} ingress: - {} EOF
第九步:綜合故障案例
案例一:Service 有 Endpoint 但返回 502
現(xiàn)象:Service 的 Endpoints 列表正常(有 IP 有端口),但通過(guò) Ingress 訪問(wèn)返回 502。
排查過(guò)程:
# 1. 確認(rèn) Service 和 Endpoint 配置 kubectl get svc my-service -n default kubectl get endpoints my-service -n default # 輸出正常,Endpoint 數(shù)量和 Pod 數(shù)量一致 # 2. 從 Pod 內(nèi)直接訪問(wèn) Service(繞過(guò) Ingress) kubectl run -n default curl-test --image=curlimages/curl --restart=Never -it --rm -- sh # curl http://my-service.default.svc.cluster.local:8080/api/health # 能正常返回 # 3. 通過(guò) Ingress 訪問(wèn)返回 502 curl http:///api/health # 返回 502 # 4. 查看 Ingress Controller 日志 kubectl logs -n kube-system nginx-ingress-controller-xxx | grep"/api/health" # 日志顯示:"upstream timed out (110: Connection timed out)" # 5. 確認(rèn)問(wèn)題:Ingress Controller 無(wú)法連接到后端 Service # 檢查 Ingress Controller 和后端是否在同一個(gè)網(wǎng)絡(luò) # 發(fā)現(xiàn):Ingress Controller 在 kube-system 命名空間,應(yīng)用在 default 命名空間 # 但 NetworkPolicy 默認(rèn)允許同集群內(nèi)通信,所以不是這個(gè)原因 # 6. 實(shí)際根因:應(yīng)用的重試機(jī)制導(dǎo)致請(qǐng)求攜帶無(wú)效的 "Host" 頭 # Ingress Controller 的 upstream 配置中 server_name 和請(qǐng)求頭中的 Host 不匹配 # 修復(fù):調(diào)整 Ingress 的 host 字段或者應(yīng)用的 Host 頭
修復(fù)方案:
# 在 Ingress 中明確指定 backend 的 serviceName 和 servicePort
spec:
rules:
-host:api.example.com
http:
paths:
-path:/api
pathType:Prefix
backend:
service:
name:my-service
port:
number:8080
案例二:Pod 之間通過(guò) Headless Service 無(wú)法互相發(fā)現(xiàn)
現(xiàn)象:部署了有狀態(tài)應(yīng)用(MySQL 主從),使用 Headless Service,但 Pod 之間無(wú)法通過(guò) DNS 互相發(fā)現(xiàn)。
排查過(guò)程:
# 1. 確認(rèn) Headless Service 配置
kubectl get svc mysql-headless -n database -o yaml
# 關(guān)鍵字段:
# clusterIP: None <-- 這個(gè) None 是 Headless Service 的標(biāo)志
# spec:
# ? selector:
# ? ? app: mysql
# 2. 確認(rèn) Pod 的 DNS 記錄
kubectl?exec?-it mysql-0 -n database -- nslookup mysql-headless.database.svc.cluster.local
# 正常情況下應(yīng)該返回所有匹配的 Pod IP
# 3. 如果 DNS 記錄為空,檢查:
# - selector 是否能匹配到 Pod label
kubectl get pods -n database -l app=mysql --show-labels
# - StatefulSet 的 serviceName 是否和 Headless Service 名稱一致
kubectl get statefulset mysql -n database -o jsonpath='{.spec.serviceName}'
根因:StatefulSet 的serviceName必須和 Headless Service 名稱完全一致,否則 Pod 的 DNS 記錄不會(huì)生成。
第十步:預(yù)防措施
10.1 建立服務(wù)可達(dá)性巡檢
#!/bin/bash
# save as: k8s_service_health_check.sh
# 建議配合 Prometheus 告警使用,這里提供命令行巡檢方式
NAMESPACES=$(kubectl get ns -o jsonpath='{.items[*].metadata.name}'| tr' ''
'| grep -v kube-system)
forNSin$NAMSPACES;do
# 找出所有非 Headless 的 ClusterIP Service
SERVICES=$(kubectl get svc -n$NS-o json | jq -r'.items[] | select(.spec.clusterIP != "None") | select(.spec.clusterIP != "") | .metadata.name')
forSVCin$SERVICES;do
ENDPOINTS=$(kubectl get endpoints$SVC-n$NS-o json | jq'.subsets | length')
if["$ENDPOINTS"=="0"] || ["$ENDPOINTS"=="null"];then
echo"[ALERT] Service$SVCin namespace$NShas NO endpoints"
fi
done
done
echo"巡檢完成"
10.2 Ingress 和 Service 關(guān)聯(lián)性校驗(yàn)
每次部署新服務(wù)時(shí),用以下命令校驗(yàn) Ingress 和 Service 的關(guān)聯(lián)是否正確:
#!/bin/bash
# 檢查所有 Ingress 資源對(duì)應(yīng)的 Service 是否存在且有 Endpoints
forINGRESSin$(kubectl get ingress -A -o jsonpath='{.items[*].metadata.name}');do
NS=$(kubectl get ingress$INGRESS-A -o jsonpath='{.items[0].metadata.namespace}')
SVC=$(kubectl get ingress$INGRESS-n$NS-o jsonpath='{.spec.rules[0].http.paths[0].backend.service.name}')
PORT=$(kubectl get ingress$INGRESS-n$NS-o jsonpath='{.spec.rules[0].http.paths[0].backend.service.port.number}')
echo"Ingress:$INGRESS, Service:$SVC, Port:$PORT"
EP_COUNT=$(kubectl get endpoints$SVC-n$NS-o json 2>/dev/null | jq'.subsets | map(.addresses) | flatten | length')
if["$EP_COUNT"=="0"] || ["$EP_COUNT"=="null"];then
echo" [WARNING] Service$SVChas no endpoints!"
fi
done
10.3 Pod readinessProbe 配置規(guī)范
# 標(biāo)準(zhǔn) Spring Boot 應(yīng)用的 readinessProbe 配置 readinessProbe: httpGet: path:/actuator/health/readiness port:8080 initialDelaySeconds:30 # 等待應(yīng)用啟動(dòng) periodSeconds:10 # 每 10 秒檢查一次 timeoutSeconds:5 # 超時(shí) 5 秒算失敗 successThreshold:1 # 成功 1 次即 Ready failureThreshold:3 # 連續(xù)失敗 3 次才標(biāo)記 NotReady # 標(biāo)準(zhǔn) Golang 應(yīng)用的 readinessProbe 配置 readinessProbe: tcpSocket: port:8080 initialDelaySeconds:5 periodSeconds:10 failureThreshold:3
總結(jié)
K8s 服務(wù)訪問(wèn)不通的問(wèn)題,排查核心在于分層定位、逐層排除。推薦按以下優(yōu)先級(jí)排查:
第一優(yōu)先級(jí)(5 分鐘內(nèi)定位):
Pod 是否 Running、Ready、日志是否正常
Service 的 Endpoints 是否有關(guān)聯(lián)的 Pod IP
從 Pod 內(nèi)直接訪問(wèn) Service IP(繞過(guò) Ingress 和 NodePort)
第二優(yōu)先級(jí)(5-15 分鐘):
kube-proxy 的 iptables/ipvs 規(guī)則是否正確生成
CNI 插件是否正常(flannel.1 / calico* 接口是否存在)
Ingress Controller 是否 Running、日志是否有 502/503 錯(cuò)誤
第三優(yōu)先級(jí)(15 分鐘以上):
DNS 解析是否正常(CoreDNS 日志、ndots 配置)
NetworkPolicy 是否阻止了合法流量
跨節(jié)點(diǎn)網(wǎng)絡(luò)路由是否正確(VPC/物理網(wǎng)絡(luò)層)
關(guān)鍵原則:
始終從 Pod 層開(kāi)始,往上排查。Pod 正常是 Service 正常的前提
用kubectl get endpoints判斷 Service 是否有關(guān)聯(lián)的 Pod,是最快速的診斷方法
Ingress 訪問(wèn)問(wèn)題,80% 是 Ingress 和 Service 關(guān)聯(lián)配置錯(cuò)誤,20% 是 Ingress Controller 自身問(wèn)題
NetworkPolicy 問(wèn)題隱蔽性最強(qiáng),排查到最后才考慮
記住一個(gè)排查順序口訣:先 Pod 再 Svc,EP 不為空則網(wǎng)絡(luò)通;Ingress 有問(wèn)題先看日志,DNS 不通查 CoreDNS。
-
Service
+關(guān)注
關(guān)注
0文章
33瀏覽量
14385 -
kubernetes
+關(guān)注
關(guān)注
0文章
279瀏覽量
9539
原文標(biāo)題:K8s 服務(wù)訪問(wèn)不通?從 Pod、Service 到 Ingress 一步步查
文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
什么是 K8S,如何使用 K8S
如何利用K8S全面擁抱微服務(wù)架構(gòu)?
OpenStack與K8s結(jié)合的兩種方案的詳細(xì)介紹和比較
Docker不香嗎為什么還要用K8s
簡(jiǎn)單說(shuō)明k8s和Docker之間的關(guān)系
K8S集群服務(wù)訪問(wèn)失敗怎么辦 K8S故障處理集錦
k8s是什么意思?kubeadm部署k8s集群(k8s部署)|PetaExpres
什么是K3s和K8s?K3s和K8s有什么區(qū)別?
k8s生態(tài)鏈包含哪些技術(shù)
K8s常見(jiàn)的10個(gè)問(wèn)題排查
K8s服務(wù)訪問(wèn)不通的排查方法
評(píng)論