mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1757 字
5 分钟
深度剖析:一个请求从客户端到 Pod 的完整路径
2021-11-06

当你在浏览器输入 https://api.example.com/users 并按下回车时,这个请求会经过多少层网络转换才能到达 Kubernetes 集群中的某个 Pod?DNS 解析、TLS 终止、Ingress 路由、Service 负载均衡、iptables/IPVS DNAT、容器网络……每一层都有自己的转发逻辑和状态管理。

掌握了这条路径,排查 Kubernetes 网络问题就有了清晰的思路。本文从底层视角,逐层追踪一个请求从客户端到 Pod 的完整旅程。

一、全局视角:请求的七层跳转#

1
:api.example.com → 负载均衡器公网 IP
2
:转发到 Ingress Controller NodePort
3
:TLS 终止 + HTTP 路由
4
:虚拟 IP,提供稳定入口
5
:iptables/IPVS DNAT,ClusterIP → Pod IP
6
:Node 间转发
7
:处理请求

每一层的职责:

层次组件核心动作
1客户端 DNSapi.example.com → 负载均衡器公网 IP
2云负载均衡器L4 转发到 Ingress Controller NodePort
3Ingress ControllerTLS 终止,HTTP 路由匹配,转发到 Service
4ServiceClusterIP 虚拟 IP,提供稳定入口
5kube-proxyiptables/IPVS DNAT,ClusterIP → Pod IP
6节点网络路由/网桥,跨 Node 或本地转发
7Pod容器内进程接收并处理请求

二、第一跳:DNS 解析#

sequenceDiagram participant C as 客户端 participant DNS as DNS 服务器 C->>DNS: 查询 api.example.com A 记录 DNS-->>C: 203.0.113.100(云负载均衡器公网 IP)

如果集群使用 ExternalDNS,Ingress 创建时自动在云厂商 DNS 中创建 A 记录,指向 LoadBalancer Service 的外部 IP。

三、第二跳:云负载均衡器(L4)#

flowchart LR C["客户端"] --> LB["203.0.113.100:443<br/>云负载均衡器"] LB --> N1["Node1:30080<br/>NodePort"] LB --> N2["Node2:30080<br/>NodePort"]

云负载均衡器(如 AWS ALB/NLB)做 L4 层转发:

  1. 接收客户端 TCP 连接
  2. 健康检查后端 Node 的 NodePort
  3. 选择健康的 Node,将流量转发到 NodeIP:30080
  4. 保持连接跟踪(Connection Affinity)
Note

负载均衡器转发到 NodePort 时,即使该 Node 上没有运行 Ingress Controller Pod,kube-proxy 的 iptables 规则也会将流量二次转发到有 Pod 的 Node。这是 iptables 模式的默认行为。

四、第三跳:Ingress Controller#

流量从 NodeIP:30080 到达 Ingress Controller Pod。Ingress Controller(以 NGINX Ingress 为例)的处理流程:

1
接收 TLS 连接(NodePort)
2
TLS 终止(使用 tls-secret 证书)
3
HTTP 路由匹配(Host: api.example.com)
4
Path 匹配(/users → api-svc)
5
选择后端 Pod(通过 Endpoints)
6
转发请求(proxy_pass to Pod IP)

关键步骤:

  1. TLS 终止:使用 Ingress 中 tls.secretName 指定的证书解密流量
  2. Host 路由:根据 Host 头匹配 Ingress 规则
  3. Path 路由:根据 URL 路径匹配后端 Service
  4. 负载均衡:通过 Endpoints 列表选择后端 Pod,Ingress Controller 自己实现负载均衡(不依赖 kube-proxy)
Note

NGINX Ingress Controller 默认使用 round-robin 算法选择后端 Pod,并且会检查 Pod 的 Ready 条件。它直接连接 Pod IP,绕过了 Service ClusterIP 的 kube-proxy DNAT。但 Ingress Controller 本身如果通过 NodePort 暴露,那一段流量仍然会经过 kube-proxy。这里说的「绕过」仅指 Ingress 到 Pod 这一段。

五、第四跳:Service ClusterIP#

当集群内部应用通过 Service 访问时(非 Ingress 路径),请求到达 Service ClusterIP:

sequenceDiagram participant PA as Pod A participant DNS as 集群 DNS participant SVC as Service PA->>DNS: my-svc.default.svc.cluster.local:80 DNS-->>PA: 10.96.0.100:80(ClusterIP) PA->>SVC: 连接 10.96.0.100:80

ClusterIP 是一个虚拟 IP,它不存在于任何网络接口上,无法 ping 通。它只是一个 iptables/IPVS 规则的匹配条件。

六、第五跳:kube-proxy 与 DNAT#

这是整个路径中最关键的一跳。kube-proxy 将目标地址从 ClusterIP 改写为 Pod IP(DNAT),使数据包能够到达实际的 Pod。

6.1 iptables 模式的完整规则链#

# 1. PREROUTING 链:进入 Node 的流量
-A PREROUTING -j KUBE-SERVICES
# 2. KUBE-SERVICES 链:匹配 Service ClusterIP
-A KUBE-SERVICES -d 10.96.0.100/32 -j KUBE-SVC-MYAPP
# 3. KUBE-SVC 链:概率负载均衡
-A KUBE-SVC-MYAPP -m statistic --mode random --probability 0.333 -j KUBE-SEP-POD1
-A KUBE-SVC-MYAPP -m statistic --mode random --probability 0.500 -j KUBE-SEP-POD2
-A KUBE-SVC-MYAPP -j KUBE-SEP-POD3
# 4. KUBE-SEP 链:DNAT 改写目标地址
-A KUBE-SEP-POD1 -s 10.244.1.5/32 -j MARK --set-xmark 0x4000/0x4000
-A KUBE-SEP-POD1 -j DNAT --to-destination 10.244.1.5:8080

数据包转换过程:

阶段源地址目标地址
原始10.244.2.1010.96.0.100<80>
DNAT 后10.244.2.1010.244.1.5<8080>

6.2 conntrack:连接跟踪#

DNAT 发生后,Linux 内核的 conntrack 模块记录这条连接的映射关系:

阶段客户端服务端
原始10.244.2.10<45678>10.96.0.100<80>
DNAT10.244.2.10<45678>10.244.1.5<8080>

后续同一连接的数据包无需再次查 iptables 规则,conntrack 直接根据已有映射执行 DNAT,这是性能优化的关键。

回程数据包自动执行反向 DNAT(SNAT 的逆操作):

阶段源地址目标地址
Pod 回复10.244.1.5<8080>10.244.2.10<45678>
反向 DNAT10.96.0.100<80>10.244.2.10<45678>

6.3 IPVS 模式的数据路径#

IPVS 模式使用内核的 IPVS 模块,数据路径更短:

# IPVS 规则
TCP 10.96.0.100:80 rr
-> 10.244.1.5:8080 Masq 1
-> 10.244.2.3:8080 Masq 1
-> 10.244.3.7:8080 Masq 1

IPVS 的优势在于:

  • O(1) 查找:使用哈希表而非链式规则匹配
  • 更丰富的算法:rr/wrr/lc/wlc/sh/dh/sed/nq
  • 更少的 iptables 规则:IPVS 只处理 Service DNAT,其余(masquerade、NodePort)仍用 iptables
1
数据包到达(DST=10.96.0.100)
2
IPVS 哈希查找(O(1))
3
选择后端(rr: 10.244.1.5)
4
DNAT 改写(conntrack 记录)
5
转发到 Pod

七、第六跳:节点网络转发#

DNAT 后,数据包的目标地址是 Pod IP(如 10.244.1.5)。节点需要判断如何转发:

7.1 同节点转发#

如果 Pod IP 属于当前 Node 的 Pod 网段(如目标 10.244.1.5,本节点 Pod 网段 10.244.1.0/24),直接通过 cni0 网桥到达 Pod veth 对:

# 节点路由表
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1
graph LR NODE["Node 网络栈"] --> CNI["cni0 网桥"] CNI --> VETH1["veth1<br/>Pod A"] CNI --> VETH2["veth2<br/>Pod B"] VETH1 --> PA["eth0@Pod A<br/>10.244.1.5"] VETH2 --> PB["eth0@Pod B<br/>10.244.1.6"] style CNI fill:#e8f5e9,stroke:#2e7d32

7.2 跨节点转发#

如果 Pod IP 属于其他 Node 的网段(如目标 10.244.2.3,属于 Node2 的 Pod 网段 10.244.2.0/24),则通过 Node2 的物理 IP 转发:

# 节点路由表
10.244.2.0/24 via 192.168.1.20 dev eth0

跨节点转发的封装方式取决于 CNI 实现:

CNI封装方式性能
Flannel (VXLAN)VXLAN 封装,Overlay 网络有封装开销
Calico (BGP)BGP 路由,无封装接近原生性能
Cilium (eBPF)eBPF 路由/VXLAN高性能,可绕过 iptables
Weave NetVXLAN 封装有封装开销
1
:VXLAN/BGP 封装,目标 10.244.2.3
2
:接收封装数据包
3
:解封装,本地转发
4
:接收请求

八、第七跳:Pod 接收请求#

数据包最终到达 Pod 的网络命名空间,源地址 10.244.2.10:45678,目标 10.244.1.5:8080

flowchart LR VETH["veth pair"] --> ETH["Pod eth0"] --> PROC["容器进程监听 8080 端口"]

Pod 内进程看到的源地址取决于是否发生了 SNAT:

场景Pod 看到的源地址
同 Node Pod → Pod真实 Pod IP(无 SNAT)
跨 Node Pod → Pod真实 Pod IP(CNI 路由,无 SNAT)
外部 → NodePort → PodNode IP(kube-proxy 执行了 SNAT)
Note

NodePort 场景下,kube-proxy 执行 DNAT 后还会执行 SNAT(将源地址改写为 Node IP),否则 Pod 的回包无法正确路由。这导致 Pod 看不到真实客户端 IP。可以通过设置 externalTrafficPolicy: Local 避免 SNAT,但代价是流量只转发到本 Node 的 Pod。

九、完整路径追踪实战#

使用 iptables -t nat -L -n -vipvsadm -Ln 可以在节点上查看实际的转发规则:

# 在 Node 上追踪请求路径
# 1. 查看 Service 规则
iptables -t nat -L KUBE-SERVICES -n | grep 10.96.0.100
# 2. 查看负载均衡规则
iptables -t nat -L KUBE-SVC-MYAPP -n
# 3. 查看 DNAT 目标
iptables -t nat -L KUBE-SEP-POD1 -n
# 4. 查看连接跟踪表
conntrack -L | grep 10.96.0.100
# 5. 查看路由
ip route get 10.244.1.5
# 6. IPVS 模式查看规则
ipvsadm -Ln -t 10.96.0.100:80

理解这条完整路径后,网络问题的排查就有了清晰的思路:从 Pod 往外逐跳检查,Pod 内网络是否正常?节点路由是否正确?kube-proxy 规则是否存在?Ingress 路由是否匹配?DNS 解析是否正确?

下一篇讲另一个维度的底层剖析,创建一个 Deployment 时,Kubernetes 控制面和节点组件的协作流程。

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

深度剖析:一个请求从客户端到 Pod 的完整路径
https://blog.souloss.cn/posts/kubernetes/k8s-what-happens-request/
作者
Souloss
发布于
2021-11-06
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

相关文章 智能推荐
1
深度剖析:创建一个 Deployment 时,Kubernetes 底层发生了什么
云原生 从底层视角追踪创建一个 Deployment 的完整流程,kubectl → API Server 认证/鉴权/准入控制 → etcd 持久化 → Deployment Controller 创建 ReplicaSet → ReplicaSet Controller 创建 Pod → Scheduler 调度 → kubelet 启动容器,理解声明式 API 与控制器模式的协作机制。
2
Kubernetes 工作负载:从 Pod 到 Deployment
云原生 深入 Kubernetes 工作负载体系,Pod 生命周期与探针、ReplicaSet 副本管理、Deployment 滚动更新与回滚、StatefulSet 有状态应用、DaemonSet 节点守护、Job/CronJob 批处理、HPA 自动扩缩容,理解每种工作负载的设计意图与适用场景。
3
Kubernetes 服务发现与网络基础
云原生 深入 Kubernetes 服务发现与网络,Service 四种类型详解、Endpoints 与 EndpointSlice、Ingress 与 Ingress Controller、集群 DNS 服务发现、kube-proxy 的 iptables 与 IPVS 模式、NetworkPolicy 网络隔离,理解流量从客户端到 Pod 的路由机制。
4
Kubernetes 核心概念:Pod、Node 与控制面
云原生 深入 Kubernetes 核心概念,从控制面组件(API Server、etcd、Scheduler、Controller Manager)到工作节点组件(kubelet、kube-proxy),再到 Pod、Node、Namespace、Label 与 Selector 的设计哲学,理解声明式 API 与控制器模式的协作机制。
5
Kubernetes 网络
云原生 全面讲解 Kubernetes 网络体系,Pod 网络(CNI)、Service 网络(kube-proxy / iptables / ipvs)、Ingress 流量入口,以及 Flannel、Calico、Cilium 三大 CNI 插件的对比。