摘要:串联 DNS、外部负载均衡、Ingress、Service、CNI 与后端 Pod 的请求链路,通过分层检查定位解析、端点、跨节点和网络策略故障。
1. 先建立完整的网络拓扑
假设有以下环境:
flowchart TB
subgraph G0["DNS + 外部负载均衡器"]
direction TB
N1["shop.example.com"]
N2["198.51.100.20"]
end
N0["浏览器客户端<br/>公网客户端 IP:203.0.113.50"]
N3["Ingress Controller<br/>Node A / Pod 网络"]
N4["前端 Service"]
N5["前端 Pod<br/>处理页面请求"]
N6["后端 API Service"]
N7["API Pod<br/>处理业务请求"]
N8["数据库 Service / 数据库 Pod<br/>持久化数据"]
N0 --> N1
N1 -->|"DNS 解析"| N2
N2 --> N3
N3 --> N4
N4 --> N5
N5 --> N6
N6 --> N7
N7 --> N8
注意,这是一种示例拓扑,并非所有集群都经过完全相同的组件。
例如:
- 有的集群使用云厂商的外部负载均衡器。
- 有的集群通过 NodePort 暴露 Ingress。
- 有的集群使用 Gateway API。
- 有的集群使用 Cilium eBPF 实现 Service 转发,而不使用传统 kube-proxy iptables 规则。
因此,排障的第一步永远是确认当前集群实际采用的网络架构。
2. 第一跳:浏览器如何找到 Kubernetes?
用户输入:
https://shop.example.com浏览器首先需要获得域名对应的 IP 地址。
典型情况下,DNS 返回:
flowchart LR
N0["shop.example.com"]
N1["198.51.100.20"]
N0 -->|"DNS 解析"| N1
然后浏览器与目标地址建立 TCP 连接,并通过 TLS 完成 HTTPS 握手,之后才发送加密的 HTTP 请求。
这里需要区分三个阶段:
- DNS:将域名解析为 IP 地址。
- TCP:建立可靠的传输连接。
- TLS/HTTP:建立加密会话并发送应用请求。
如果 DNS 解析失败,HTTP 请求可能还没有发出;如果 TCP 握手失败,问题发生在连接建立阶段;如果 TLS 握手失败,TCP 可能已经正常建立,但 HTTPS 仍无法使用。
可以在客户端检查:
nslookup shop.example.com检查 TCP 连接:
nc -vz -w 3 198.51.100.20 443检查 HTTPS:
curl -vk --connect-timeout 3 https://shop.example.com/这些命令分别帮助你判断 DNS、TCP 端口连通性和 TLS/HTTP 层面的情况。
其中 -k 会跳过证书验证,仅适合临时诊断,不能作为生产访问方式。
上面的 198.51.100.20 是文档示例地址,实际排障时应替换为真实负载均衡器地址。
3. 第二跳:外部负载均衡器如何把请求送入集群?
假设外部负载均衡器监听:
198.51.100.20:443它将请求转发给 Kubernetes 中的 Ingress Controller。
常见方式有三种:
暴露方式与请求路径:
flowchart LR
N0["云负载均衡器 + NodePort<br/>LB"]
N1["NodeIP:NodePort"]
N2["Ingress Pod"]
N3["云负载均衡器 + LoadBalancer Service<br/>LB"]
N4["Service 对应的后端"]
N5["裸机环境 + MetalLB<br/>外部网络"]
N6["对外公布的 Service IP"]
N0 --> N1
N1 --> N2
N3 --> N4
N5 --> N6
这里最重要的问题是:外部负载均衡器转发后,后端看到的源 IP 还是不是客户端 IP?
答案取决于负载均衡器和 Kubernetes 的网络配置。
例如,某些四层负载均衡器会保留客户端源 IP;某些模式会进行 SNAT,使后端看到负载均衡器的地址;也有方案通过 PROXY protocol 传递原始客户端地址。
这会直接影响:
- 日志中的客户端 IP。
- 基于源 IP 的访问控制。
- 网络策略。
- 故障定位时的抓包结果。
如果你发现后端 Pod 看到的源 IP 是节点地址,不要立即认定这是 CNI 故障,应该先确认外部负载均衡器、NodePort 和 Service 的实际转发模式。
4. 第三跳:Ingress 如何转发到 Service?
假设 Ingress Controller 收到请求:
Host: shop.example.comPath: /它根据路由规则选择前端 Service:
flowchart TB
N0["shop.example.com/"]
N1["frontend Service"]
N2["frontend Pod"]
N0 --> N1
N1 --> N2
先检查 Ingress 配置:
kubectl get ingress -Akubectl describe ingress <ingress-name>再查看 Service:
kubectl get svc frontend -o yaml查看后端端点:
kubectl get endpointslices \ -l kubernetes.io/service-name=frontend这几个对象的职责不同:
- Ingress:定义 HTTP/HTTPS 路由规则。
- Ingress Controller:真正执行这些规则。
- Service:提供稳定的服务入口和后端选择机制。
- EndpointSlice:记录服务关联的后端端点。
Ingress 不是数据包转发规则本身,而是由控制器读取并执行的配置对象。
此外,Ingress Controller 可能通过自己的代理逻辑直接连接后端 Pod,也可能经过 Service ClusterIP。不能假定所有 Ingress Controller 都会采用相同的转发路径。
5. 第四跳:Service 如何选择后端 Pod?
假设前端 Service 为:
ClusterIP:10.96.10.20端口:80后端有两个 Pod:
Pod A:10.244.1.10:8080Pod B:10.244.2.20:8080当请求访问 Service 时,具体的数据面会选择一个后端。
1. 传统 kube-proxy iptables 模式
典型逻辑如下:
flowchart TB
N0["客户端"]
N1["Service ClusterIP:80"]
N2["iptables / Netfilter"]
N3["DNAT 到某个后端 Pod"]
N4["Pod:8080"]
N0 --> N1
N1 --> N2
N2 --> N3
N3 --> N4
假设选择 Pod A,数据包的目标地址可能从:
10.96.10.20:80转换为:
10.244.1.10:8080这就是 DNAT。
2. Cilium eBPF 模式
在适用的 Cilium 配置中,Service 选择也可以通过 eBPF 完成。有些流量会在 Socket 层选择后端,有些流量则在后续数据包处理路径中执行负载均衡。
所以不能只通过检查 iptables-save 就断定所有 Service 流量的处理情况。
可以先检查集群组件:
kubectl get pods -A -o widekubectl get svc -Akubectl get endpointslices -A然后根据实际数据面选择相应的诊断工具。
6. 第五跳:跨节点时,CNI 如何传递数据包?
假设前端 Pod 位于 Node A,后端 Pod 位于 Node B:
flowchart TB
N0["Node A<br/>前端 Pod:10.244.1.10<br/>节点 IP:192.168.1.10"]
N1["CNI 数据面<br/>路由 / VXLAN / eBPF 等"]
N2["Node B<br/>后端 Pod:10.244.2.20<br/>节点 IP:192.168.1.20"]
N0 <--> N1
N1 <--> N2
如果采用 VXLAN,数据包可能被封装:
| 数据包层次 | 源地址 | 目的地址 |
|---|---|---|
| 内部 Pod 数据包 | 10.244.1.10 | 10.244.2.20 |
| 外层节点数据包 | 192.168.1.10 | 192.168.1.20 |
如果采用直接路由,路径则可能不需要 VXLAN 封装。
如果使用 Cilium eBPF,部分转发和策略处理也可能由内核中的 eBPF 程序执行。
因此,排查跨节点网络时,要区分:
- Pod 内部数据包。
- 节点之间传输的数据包。
- Service 地址转换。
- CNI 的封装与解封装。
- 目标 Pod 所在节点的最终转发。
7. 按故障现象确定排查入口
排障的关键不是每次都从头检查所有组件,而是先确认请求在哪一步失败。
例如:
- 域名无法解析:先检查 DNS。
- Service 没有端点:先检查选择器、Pod 标签和就绪状态。
- TCP 连接超时:检查数据面、路由、防火墙和策略。
- TCP 已连接但返回 502:检查代理与后端应用。
- 只有跨节点 Pod 不通:重点检查 CNI 和节点之间的网络。
8. 确认 Pod 和节点是否正常
遇到网络问题,不要立即检查 CNI。先确认 Pod 是否正常运行、被分配到了哪台节点,以及是否已经获得 IP。
kubectl get pods -A -o widekubectl get nodes -o widekubectl get events -A --sort-by=.lastTimestamp重点观察:
| 状态或现象 | 优先排查方向 |
|---|---|
Pending | 调度、资源、PVC、节点约束 |
ContainerCreating 长时间不结束 | 镜像、存储、CNI 配置等 |
CrashLoopBackOff | 应用启动失败、配置错误、探针 |
Running 但 0/1 Ready | Readiness Probe、依赖服务、应用状态 |
| Pod 没有 IP | CNI、IPAM、Pod Sandbox 创建过程 |
注意:Running 只代表 Pod 已经进入运行阶段,不代表应用可以正常处理请求。
如果 Pod 尚未 Ready,先执行:
kubectl describe pod <pod-name>kubectl logs <pod-name>kubectl logs <pod-name> --previous--previous 可以查看上一次已经终止的容器实例日志,适合排查反复重启的问题。
9. Service 无法访问,怎么排查?
假设你访问:
curl http://backend:8080但请求失败。
按以下顺序检查。
1. Service 是否存在?
kubectl get svc backendkubectl describe svc backend确认 Service 的 ClusterIP、端口和选择器。
2. EndpointSlice 是否有可用端点?
kubectl get endpointslices \ -l kubernetes.io/service-name=backend -o yaml如果没有可用端点,优先检查:
- Service 的
selector是否正确。 - Pod 标签是否匹配。
- Pod 是否 Ready。
- Service 的
targetPort是否与应用监听端口一致。
例如,Service 配置为:
ports: - port: 8080 targetPort: 80客户端访问的是 Service 的 8080,而后端进程应监听容器网络中的 80 端口。
3. 直接访问 Pod IP
从同一个集群中的客户端 Pod 测试:
curl -v --connect-timeout 3 http://<pod-ip>:80结果可以帮助缩小范围:
| 测试结果 | 下一步 |
|---|---|
| Pod IP 可访问,Service 不可访问 | 检查 Service 数据面、端口映射、Service 规则 |
| 同节点 Pod IP 可访问,跨节点失败 | 检查 CNI、跨节点路由和防火墙 |
| Pod IP 也不可访问 | 检查应用监听、NetworkPolicy、路由 |
| TCP 连接成功但返回 HTTP 错误 | 检查应用日志和 HTTP 请求 |
这些结果只是定位线索,并非绝对结论。例如,直接访问 Pod IP 和访问 ClusterIP 可能经过不同的网络规则。
10. DNS 解析失败
假设下面的请求失败:
curl http://backend.default.svc.cluster.local:8080先单独测试 DNS:
nslookup backendnslookup backend.default.svc.cluster.local如果容器没有 nslookup,可以使用具备 DNS 工具的临时调试容器。
接着检查 Pod DNS 配置:
cat /etc/resolv.conf查看 CoreDNS:
kubectl get pods -n kube-systemkubectl get svc -n kube-systemkubectl logs -n kube-system \ -l k8s-app=kube-dns建议按这个顺序判断:
- 完整域名是否正确。
- Pod 的
nameserver是否正确。 - CoreDNS Pod 是否正常运行。
- DNS Service 是否存在。
- DNS 请求是否被 NetworkPolicy 或网络故障阻断。
- CoreDNS 是否能访问 Kubernetes API 并正确解析服务记录。
特别要区分两种情况:
NXDOMAIN:通常意味着查询名称不存在,先检查域名和对应资源。- 查询超时:更应该检查 DNS 服务是否可达、CoreDNS 状态及 UDP/TCP 53 流量。
不要将所有 DNS 问题都归因于 CoreDNS 崩溃。
11. 跨节点 Pod 无法通信
假设:
Pod A:10.244.1.10,Node APod B:10.244.2.20,Node B同节点通信正常,但 Pod A 无法访问 Pod B。
此时应该检查底层网络。
在 Node A 上执行:
ip addrip routeip neigh检查:
- Node A 是否存在到目标 Pod 网段的路由。
- 路由是否指向正确的节点或隧道。
- CNI 虚拟接口是否正常。
- 节点间的网络路径是否可达。
如果使用 Flannel VXLAN,还可以检查:
ip -d link show观察是否存在相关 VXLAN 接口。
如果使用 Calico,则需要根据其具体运行模式检查路由、BGP 或隧道状态;如果使用 Cilium,则应检查对应的 Cilium 状态和数据面。
注意:并不是所有 CNI 都必须创建相同名称的虚拟接口,也不是所有集群都必须使用 VXLAN。
1. 重点检查 MTU
如果小数据包正常,而较大的请求超时,可能存在 MTU 问题。
Overlay 封装会增加额外的网络头部。如果 Pod 网络 MTU 与底层网络不匹配,就可能导致分片、丢包或路径 MTU 发现异常。
可以检查:
ip link show再结合实际网络路径、接口 MTU 和抓包结果分析。不要未经验证就统一修改所有节点的 MTU。
12. NetworkPolicy 导致通信失败
如果 Pod、Service 和 DNS 都正常,但某些 Pod 之间无法通信,应检查 NetworkPolicy。
kubectl get networkpolicy -Akubectl describe networkpolicy <policy-name> -n <namespace>检查:
- 目标 Pod 是否被
podSelector选中。 - 来源 Pod 是否符合
from条件。 - 目标端口是否正确。
- 是否启用了入站或出站隔离。
- 是否有其他策略同时允许该流量。
- CNI 是否真正支持并执行这些策略。
例如,后端只允许前端访问 TCP 8080,但你的应用实际监听 9090,就可能出现连接失败。
还要记住,NetworkPolicy 主要描述 IP 层和传输层的流量规则,不负责校验 HTTP 用户身份,也不等价于应用层鉴权。
13. Ingress 返回 502、503 或超时
如果外部访问:
https://shop.example.com/api出现错误,可以按照错误类型进行初步定位。
| 现象 | 常见排查方向 |
|---|---|
| 404 | Host、Path、路由规则、应用路径 |
| 502 Bad Gateway | 上游连接失败、协议不匹配、后端异常 |
| 503 Service Unavailable | 没有可用端点、上游健康检查失败 |
| 连接超时 | 外部负载均衡器、网络路径、策略、防火墙 |
| TLS 证书错误 | 证书内容、域名、有效期、TLS 配置 |
检查 Ingress:
kubectl get ingress -Akubectl describe ingress <ingress-name>检查后端:
kubectl get svc <service-name>kubectl get endpointslices \ -l kubernetes.io/service-name=<service-name>最后查看 Controller 和后端日志:
kubectl logs -n <controller-namespace> <controller-pod>kubectl logs <backend-pod>502 并不必然意味着 CNI 故障。它也可能是后端没有监听目标端口、网关使用了错误的上游协议,或者应用主动关闭连接。
14. 定位间歇性的 504 超时
假设用户反馈:
网站偶尔返回 504 Gateway Timeout。
不要直接重启 Ingress,也不要立即修改 CNI。按照下面的顺序排查。
1. 第一步:确认外部访问是否正常
curl -vk --connect-timeout 3 \ https://shop.example.com/观察 DNS、TCP、TLS 和 HTTP 各阶段是否正常。
2. 第二步:检查 Ingress 和 Service
kubectl get ingress -Akubectl get svc -Akubectl get endpointslices -A如果后端没有可用端点,先检查 Pod 的 Ready 状态、Service 选择器和端口配置。
3. 第三步:从集群内部访问后端
使用已有的调试 Pod,或者创建一个经过授权的临时调试 Pod:
curl -v --connect-timeout 3 \ http://<backend-service>.<namespace>.svc.cluster.local:<port>/然后直接访问后端 Pod IP:
curl -v --connect-timeout 3 \ http://<pod-ip>:<target-port>/两种测试的结果可以帮助缩小故障范围:
| Service 访问 | Pod IP 访问 | 排查重点 |
|---|---|---|
| 成功 | 成功 | 继续检查 Ingress、外部负载均衡器及间歇性问题 |
| 失败 | 成功 | 检查 Service 数据面、端口映射和后端选择 |
| 失败 | 失败 | 检查 Pod 监听、网络策略、CNI 和路由 |
| 间歇性失败 | 成功 | 检查部分后端、负载均衡与连接状态 |
4. 第四步:检查 Ingress Controller 日志
kubectl logs -n <controller-namespace> \ <controller-pod> --since=10m重点关注:
- 上游连接超时。
- 后端连接被拒绝。
- 没有可用上游端点。
- TLS 或 HTTP 协议配置错误。
- 请求延迟异常。
5. 第五步:双端抓包
如果仍然无法定位,登录相关节点,在请求端和接收端同时抓包:
sudo tcpdump -i any -nn -tttt \ 'host <peer-ip> and tcp port <port>'如果前端发出 SYN,而后端没有收到,检查中间转发路径。
如果后端收到 SYN,却没有发出 SYN-ACK,检查监听端口和节点上的网络过滤规则。
如果 TCP 握手成功,但应用迟迟没有响应,则进一步检查应用日志、数据库连接池和下游依赖。
6. 第六步:检查 CNI 与策略
kubectl get networkpolicy -Akubectl get pods -A -o wide然后根据实际使用的 CNI 检查对应的状态、路由、策略和流量事件。
如果使用 Cilium,可以结合:
cilium statushubble observe --follow如果不是 Cilium,就使用对应网络插件的诊断工具,不要直接照搬这些命令。
15. 完整链路与故障边界
flowchart TB
N0["浏览器<br/>DNS / TCP / TLS"]
N1["外部负载均衡器<br/>公网入口、源地址保留或转换"]
N2["Ingress / Gateway<br/>Host、Path、TLS、上游代理"]
N3["Service 数据面<br/>iptables / IPVS / eBPF 等"]
N4["CNI 与 Linux 网络<br/>路由、策略、封装、跨节点转发"]
N5["后端 Pod<br/>应用监听、业务处理、下游依赖"]
N0 --> N1
N1 --> N2
N2 --> N3
N3 --> N4
N4 --> N5
每一层都可以独立产生问题,而上层看到的错误不一定能直接指出根因。
例如,504 可能来自 Ingress 的上游超时,也可能是后端应用处理缓慢,或者请求路径中的网络问题。要通过分层测试、日志和抓包逐步定位。
16. 排障检查清单
排障检查清单
- 确认客户端、服务端 Pod 和 Node 状态
- 检查 Service、端口映射和 EndpointSlice
- 区分 DNS 解析失败与网络连接失败
- 测试 Pod IP,比较同节点和跨节点通信
- 检查 CNI、路由、MTU 和节点防火墙
- 检查 NetworkPolicy 和 DNS 出站规则
- 检查 Ingress/Gateway 配置与应用日志