mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1500 字
4 分钟
深度剖析:一个请求从客户端到 Pod 的完整路径
2023-09-16

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

理解这条完整路径,是排查 Kubernetes 网络问题的基础。本文从底层视角,逐层追踪一个请求从客户端到 Pod 的完整旅程。

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

flowchart LR C["1. 客户端<br/>DNS 解析"] --> LB["2. 负载均衡器<br/>L4 转发"] LB --> IC["3. Ingress Controller<br/>TLS 终止 + 路由"] IC --> SVC["4. Service ClusterIP<br/>虚拟 IP"] SVC --> KP["5. kube-proxy<br/>iptables/IPVS"] KP --> BR["6. 网桥/路由<br/>Node 间转发"] BR --> POD["7. Pod 容器<br/>处理请求"] style C fill:#e3f2fd,stroke:#1565c0 style IC fill:#e8f5e9,stroke:#2e7d32 style KP fill:#fff3e0,stroke:#e65100 style POD fill:#f3e5f5,stroke:#6a1b9a

每一层的职责:

层次组件核心动作
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 解析#

客户端 → DNS 服务器
查询: api.example.com A 记录
响应: 203.0.113.100(云负载均衡器公网 IP)

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

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

客户端 → 203.0.113.100:443
负载均衡器 → Node1:30080 或 Node2:30080(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 为例)的处理流程:

flowchart TB A["接收 TLS 连接<br/>NodePort:30080"] --> B["TLS 终止<br/>使用 tls-secret 证书"] B --> C["HTTP 路由匹配<br/>Host: api.example.com"] C --> D["Path 匹配<br/>/users → api-svc:80"] D --> E["选择后端 Pod<br/>通过 Endpoints"] E --> F["转发请求<br/>proxy_pass to Pod IP"] style A fill:#e3f2fd,stroke:#1565c0 style F fill:#c8e6c9,stroke:#2e7d32

关键步骤:

  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——这意味着 Ingress Controller 的流量不经过 kube-proxy。

五、第四跳:Service ClusterIP#

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

Pod A → my-svc.default.svc.cluster.local:80
DNS 解析 → 10.96.0.100:80(ClusterIP)

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

数据包转换过程:

原始: SRC=10.244.2.10 DST=10.96.0.100:80
DNAT后: SRC=10.244.2.10 DST=10.244.1.5:8080

6.2 conntrack:连接跟踪#

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

原始: 10.244.2.10:45678 → 10.96.0.100:80
DNAT: 10.244.2.10:45678 → 10.244.1.5:8080

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

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

Pod 回复: SRC=10.244.1.5:8080 DST=10.244.2.10:45678
反向DNAT: SRC=10.96.0.100:80 DST=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
flowchart LR A["数据包到达<br/>DST=10.96.0.100:80"] --> B["IPVS 哈希查找<br/>O(1)"] B --> C["选择后端<br/>rr: 10.244.1.5:8080"] C --> D["DNAT 改写<br/>conntrack 记录"] D --> E["转发到 Pod"] style A fill:#e3f2fd,stroke:#1565c0 style E fill:#c8e6c9,stroke:#2e7d32

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

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 封装有封装开销
flowchart LR N1["Node 1<br/>192.168.1.10"] -->|"VXLAN/BGP<br/>封装: 10.244.2.3"| N2["Node 2<br/>192.168.1.20"] N2 --> CNI2["cni0 网桥"] CNI2 --> POD["Pod C<br/>10.244.2.3"] style N1 fill:#e3f2fd,stroke:#1565c0 style N2 fill:#e8f5e9,stroke:#2e7d32

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

数据包最终到达 Pod 的网络命名空间:

数据包: SRC=10.244.2.10:45678 DST=10.244.1.5:8080
通过 veth pair → Pod eth0 → 容器内进程监听 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

总结#

跳数组件关键操作网络转换
1DNS域名 → 公网 IP-
2云 LBL4 转发到 NodePortDST: 公网IP → NodeIP
3Ingress ControllerTLS 终止 + HTTP 路由解密 + 路由到 Service/Pod
4ServiceClusterIP 虚拟入口-
5kube-proxyDNATDST: ClusterIP → PodIP
6节点网络路由/网桥同节点: cni0 网桥; 跨节点: 封装转发
7Pod容器接收veth pair → eth0

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

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

支持与分享

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

深度剖析:一个请求从客户端到 Pod 的完整路径
https://blog.souloss.com/posts/kubernetes/k8s-what-happens-request/
作者
Souloss
发布于
2023-09-16
许可协议
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 插件的对比。