跳到正文
复制成功
请遵守本站许可
报告
章节_文章 // 现场报告

文章编号: RL-KUBERNET

2026.08.15

Kubernetes 请求链路与网络故障排查

Chavy
Chavy
#Kubernetes#网络#故障排查#请求链路
档案

摘要:串联 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?#

用户输入:

PRTCL // TEXT
https://shop.example.com

浏览器首先需要获得域名对应的 IP 地址。

典型情况下,DNS 返回:

flowchart LR
    N0["shop.example.com"]
    N1["198.51.100.20"]
    N0 -->|"DNS 解析"| N1

然后浏览器与目标地址建立 TCP 连接,并通过 TLS 完成 HTTPS 握手,之后才发送加密的 HTTP 请求。

这里需要区分三个阶段:

  1. DNS:将域名解析为 IP 地址。
  2. TCP:建立可靠的传输连接。
  3. TLS/HTTP:建立加密会话并发送应用请求。

如果 DNS 解析失败,HTTP 请求可能还没有发出;如果 TCP 握手失败,问题发生在连接建立阶段;如果 TLS 握手失败,TCP 可能已经正常建立,但 HTTPS 仍无法使用。

可以在客户端检查:

PRTCL // BASH
Terminal window
nslookup shop.example.com

检查 TCP 连接:

PRTCL // BASH
Terminal window
nc -vz -w 3 198.51.100.20 443

检查 HTTPS:

PRTCL // BASH
Terminal window
curl -vk --connect-timeout 3 https://shop.example.com/

这些命令分别帮助你判断 DNS、TCP 端口连通性和 TLS/HTTP 层面的情况。

其中 -k 会跳过证书验证,仅适合临时诊断,不能作为生产访问方式。

上面的 198.51.100.20 是文档示例地址,实际排障时应替换为真实负载均衡器地址。

3. 第二跳:外部负载均衡器如何把请求送入集群?#

假设外部负载均衡器监听:

PRTCL // TEXT
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 收到请求:

PRTCL // TEXT
Host: shop.example.com
Path: /

它根据路由规则选择前端 Service:

flowchart TB
    N0["shop.example.com/"]
    N1["frontend Service"]
    N2["frontend Pod"]
    N0 --> N1
    N1 --> N2

先检查 Ingress 配置:

PRTCL // BASH
Terminal window
kubectl get ingress -A
kubectl describe ingress <ingress-name>

再查看 Service:

PRTCL // BASH
Terminal window
kubectl get svc frontend -o yaml

查看后端端点:

PRTCL // BASH
Terminal window
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 为:

PRTCL // TEXT
ClusterIP:10.96.10.20
端口:80

后端有两个 Pod:

PRTCL // TEXT
Pod A:10.244.1.10:8080
Pod 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,数据包的目标地址可能从:

PRTCL // TEXT
10.96.10.20:80

转换为:

PRTCL // TEXT
10.244.1.10:8080

这就是 DNAT。

2. Cilium eBPF 模式#

在适用的 Cilium 配置中,Service 选择也可以通过 eBPF 完成。有些流量会在 Socket 层选择后端,有些流量则在后续数据包处理路径中执行负载均衡。

所以不能只通过检查 iptables-save 就断定所有 Service 流量的处理情况。

可以先检查集群组件:

PRTCL // BASH
Terminal window
kubectl get pods -A -o wide
kubectl get svc -A
kubectl 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.1010.244.2.20
外层节点数据包192.168.1.10192.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。

PRTCL // BASH
Terminal window
kubectl get pods -A -o wide
kubectl get nodes -o wide
kubectl get events -A --sort-by=.lastTimestamp

重点观察:

状态或现象优先排查方向
Pending调度、资源、PVC、节点约束
ContainerCreating 长时间不结束镜像、存储、CNI 配置等
CrashLoopBackOff应用启动失败、配置错误、探针
Running 但 0/1 ReadyReadiness Probe、依赖服务、应用状态
Pod 没有 IPCNI、IPAM、Pod Sandbox 创建过程

注意:Running 只代表 Pod 已经进入运行阶段,不代表应用可以正常处理请求。

如果 Pod 尚未 Ready,先执行:

PRTCL // BASH
Terminal window
kubectl describe pod <pod-name>
kubectl logs <pod-name>
kubectl logs <pod-name> --previous

--previous 可以查看上一次已经终止的容器实例日志,适合排查反复重启的问题。

9. Service 无法访问,怎么排查?#

假设你访问:

PRTCL // BASH
Terminal window
curl http://backend:8080

但请求失败。

按以下顺序检查。

1. Service 是否存在?#

PRTCL // BASH
Terminal window
kubectl get svc backend
kubectl describe svc backend

确认 Service 的 ClusterIP、端口和选择器。

2. EndpointSlice 是否有可用端点?#

PRTCL // BASH
Terminal window
kubectl get endpointslices \
-l kubernetes.io/service-name=backend -o yaml

如果没有可用端点,优先检查:

  • Service 的 selector 是否正确。
  • Pod 标签是否匹配。
  • Pod 是否 Ready。
  • Service 的 targetPort 是否与应用监听端口一致。

例如,Service 配置为:

PRTCL // YAML
ports:
- port: 8080
targetPort: 80

客户端访问的是 Service 的 8080,而后端进程应监听容器网络中的 80 端口。

3. 直接访问 Pod IP#

从同一个集群中的客户端 Pod 测试:

PRTCL // BASH
Terminal window
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 解析失败#

假设下面的请求失败:

PRTCL // BASH
Terminal window
curl http://backend.default.svc.cluster.local:8080

先单独测试 DNS:

PRTCL // BASH
Terminal window
nslookup backend
nslookup backend.default.svc.cluster.local

如果容器没有 nslookup,可以使用具备 DNS 工具的临时调试容器。

接着检查 Pod DNS 配置:

PRTCL // BASH
Terminal window
cat /etc/resolv.conf

查看 CoreDNS:

PRTCL // BASH
Terminal window
kubectl get pods -n kube-system
kubectl get svc -n kube-system
kubectl logs -n kube-system \
-l k8s-app=kube-dns

建议按这个顺序判断:

  1. 完整域名是否正确。
  2. Pod 的 nameserver 是否正确。
  3. CoreDNS Pod 是否正常运行。
  4. DNS Service 是否存在。
  5. DNS 请求是否被 NetworkPolicy 或网络故障阻断。
  6. CoreDNS 是否能访问 Kubernetes API 并正确解析服务记录。

特别要区分两种情况:

  • NXDOMAIN:通常意味着查询名称不存在,先检查域名和对应资源。
  • 查询超时:更应该检查 DNS 服务是否可达、CoreDNS 状态及 UDP/TCP 53 流量。

不要将所有 DNS 问题都归因于 CoreDNS 崩溃。

11. 跨节点 Pod 无法通信#

假设:

PRTCL // TEXT
Pod A:10.244.1.10,Node A
Pod B:10.244.2.20,Node B

同节点通信正常,但 Pod A 无法访问 Pod B。

此时应该检查底层网络。

在 Node A 上执行:

PRTCL // BASH
Terminal window
ip addr
ip route
ip neigh

检查:

  • Node A 是否存在到目标 Pod 网段的路由。
  • 路由是否指向正确的节点或隧道。
  • CNI 虚拟接口是否正常。
  • 节点间的网络路径是否可达。

如果使用 Flannel VXLAN,还可以检查:

PRTCL // BASH
Terminal window
ip -d link show

观察是否存在相关 VXLAN 接口。

如果使用 Calico,则需要根据其具体运行模式检查路由、BGP 或隧道状态;如果使用 Cilium,则应检查对应的 Cilium 状态和数据面。

注意:并不是所有 CNI 都必须创建相同名称的虚拟接口,也不是所有集群都必须使用 VXLAN。

1. 重点检查 MTU#

如果小数据包正常,而较大的请求超时,可能存在 MTU 问题。

Overlay 封装会增加额外的网络头部。如果 Pod 网络 MTU 与底层网络不匹配,就可能导致分片、丢包或路径 MTU 发现异常。

可以检查:

PRTCL // BASH
Terminal window
ip link show

再结合实际网络路径、接口 MTU 和抓包结果分析。不要未经验证就统一修改所有节点的 MTU。

12. NetworkPolicy 导致通信失败#

如果 Pod、Service 和 DNS 都正常,但某些 Pod 之间无法通信,应检查 NetworkPolicy。

PRTCL // BASH
Terminal window
kubectl get networkpolicy -A
kubectl describe networkpolicy <policy-name> -n <namespace>

检查:

  • 目标 Pod 是否被 podSelector 选中。
  • 来源 Pod 是否符合 from 条件。
  • 目标端口是否正确。
  • 是否启用了入站或出站隔离。
  • 是否有其他策略同时允许该流量。
  • CNI 是否真正支持并执行这些策略。

例如,后端只允许前端访问 TCP 8080,但你的应用实际监听 9090,就可能出现连接失败。

还要记住,NetworkPolicy 主要描述 IP 层和传输层的流量规则,不负责校验 HTTP 用户身份,也不等价于应用层鉴权。

13. Ingress 返回 502、503 或超时#

如果外部访问:

PRTCL // TEXT
https://shop.example.com/api

出现错误,可以按照错误类型进行初步定位。

现象常见排查方向
404Host、Path、路由规则、应用路径
502 Bad Gateway上游连接失败、协议不匹配、后端异常
503 Service Unavailable没有可用端点、上游健康检查失败
连接超时外部负载均衡器、网络路径、策略、防火墙
TLS 证书错误证书内容、域名、有效期、TLS 配置

检查 Ingress:

PRTCL // BASH
Terminal window
kubectl get ingress -A
kubectl describe ingress <ingress-name>

检查后端:

PRTCL // BASH
Terminal window
kubectl get svc <service-name>
kubectl get endpointslices \
-l kubernetes.io/service-name=<service-name>

最后查看 Controller 和后端日志:

PRTCL // BASH
Terminal window
kubectl logs -n <controller-namespace> <controller-pod>
kubectl logs <backend-pod>

502 并不必然意味着 CNI 故障。它也可能是后端没有监听目标端口、网关使用了错误的上游协议,或者应用主动关闭连接。

14. 定位间歇性的 504 超时#

假设用户反馈:

网站偶尔返回 504 Gateway Timeout。

不要直接重启 Ingress,也不要立即修改 CNI。按照下面的顺序排查。

1. 第一步:确认外部访问是否正常#

PRTCL // BASH
Terminal window
curl -vk --connect-timeout 3 \
https://shop.example.com/

观察 DNS、TCP、TLS 和 HTTP 各阶段是否正常。

2. 第二步:检查 Ingress 和 Service#

PRTCL // BASH
Terminal window
kubectl get ingress -A
kubectl get svc -A
kubectl get endpointslices -A

如果后端没有可用端点,先检查 Pod 的 Ready 状态、Service 选择器和端口配置。

3. 第三步:从集群内部访问后端#

使用已有的调试 Pod,或者创建一个经过授权的临时调试 Pod:

PRTCL // BASH
Terminal window
curl -v --connect-timeout 3 \
http://<backend-service>.<namespace>.svc.cluster.local:<port>/

然后直接访问后端 Pod IP:

PRTCL // BASH
Terminal window
curl -v --connect-timeout 3 \
http://<pod-ip>:<target-port>/

两种测试的结果可以帮助缩小故障范围:

Service 访问Pod IP 访问排查重点
成功成功继续检查 Ingress、外部负载均衡器及间歇性问题
失败成功检查 Service 数据面、端口映射和后端选择
失败失败检查 Pod 监听、网络策略、CNI 和路由
间歇性失败成功检查部分后端、负载均衡与连接状态

4. 第四步:检查 Ingress Controller 日志#

PRTCL // BASH
Terminal window
kubectl logs -n <controller-namespace> \
<controller-pod> --since=10m

重点关注:

  • 上游连接超时。
  • 后端连接被拒绝。
  • 没有可用上游端点。
  • TLS 或 HTTP 协议配置错误。
  • 请求延迟异常。

5. 第五步:双端抓包#

如果仍然无法定位,登录相关节点,在请求端和接收端同时抓包:

PRTCL // BASH
Terminal window
sudo tcpdump -i any -nn -tttt \
'host <peer-ip> and tcp port <port>'

如果前端发出 SYN,而后端没有收到,检查中间转发路径。

如果后端收到 SYN,却没有发出 SYN-ACK,检查监听端口和节点上的网络过滤规则。

如果 TCP 握手成功,但应用迟迟没有响应,则进一步检查应用日志、数据库连接池和下游依赖。

6. 第六步:检查 CNI 与策略#

PRTCL // BASH
Terminal window
kubectl get networkpolicy -A
kubectl get pods -A -o wide

然后根据实际使用的 CNI 检查对应的状态、路由、策略和流量事件。

如果使用 Cilium,可以结合:

PRTCL // BASH
Terminal window
cilium status
hubble 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 配置与应用日志
归档于 2026.08.15
// 档案结束
Classified
章节_06
协议编号: CC-BY-NC-SA-4.0

Kubernetes 请求链路与网络故障排查

作者:Chavy发布日期:2026.08.15

基于 CC BY-NC-SA 4.0

© 2026 Chavy
Powered by Astro