Appearance
Pod 网络:从 socket 到 Service
专题索引 / 上一篇:控制回路、调度与 cgroup
网络最容易被一句“CNI 打通了”掩盖。实际要分开看:谁创建网络身份,谁配置 Node 数据面,谁把 Service 名称或 ClusterIP 变成后端,谁保存一条连接的状态。
1. 两条常见路径
Pod 访问普通地址和访问 Kubernetes Service,会在中间走不同规则。
text
普通出站:
进程 -> socket -> Pod network namespace -> veth -> Node 网络 -> 路由/隧道 -> 对端
访问 Service:
进程 -> DNS 得到 Service IP -> socket -> Pod network namespace -> veth
-> Service 转发规则选择 Endpoint -> Node 网络 -> 目标 Pod两条路径都从 Linux socket 开始。应用只知道目的 IP、端口和协议;它不知道包会经过 bridge、路由、VXLAN、Geneve、iptables、nftables 还是 eBPF。这正是把应用语义和数据面实现分开的价值。
2. Pod 的网络身份从哪里来
创建 Pod 时,kubelet 让 CRI runtime 建立 Pod sandbox。sandbox 持有 network namespace;同一个 Pod 中的容器通常共享这个 network namespace,因此共享 IP、端口空间和 localhost。
text
Pod sandbox network namespace
├─ container A: eth0, Pod IP, localhost
└─ container B: eth0, Pod IP, localhostruntime 调用 CNI plugin。CNI 的职责是按配置为 sandbox 增加和删除网络连接:常见做法是建立一对 veth,把一端放进 Pod namespace,另一端留在 Node;随后配置地址、路由、MTU 和需要的策略或隧道端点。
text
Pod netns Node netns
eth0 ---- veth pair ---- host veth
Pod IP bridge / route / overlay deviceveth 是成对虚拟网卡:一端发出的包会从另一端收到。它解决“不同 network namespace 如何接起来”;它不定义跨 Node 怎样转发,也不定义 Service 怎样选后端。后两件事由具体 CNI 和 Service 实现承担。
3. CNI、kube-proxy 与 eBPF 的边界
不要把三个职责揉成一个组件:
| 组件或机制 | 主要负责什么 | 不保证什么 |
|---|---|---|
| CNI | Pod 接入、地址、路由、网络策略等 Node 网络配置 | Kubernetes Service 必然由它转发 |
| kube-proxy | 为 Service 建立后端选择与转发规则 | Pod 网络本身一定由它创建 |
| eBPF 数据面 | 可由 CNI 或独立实现承担路由、策略、Service、负载均衡等 | 所有集群都使用它 |
| CoreDNS | 把 Service 名称解析为 IP | 连接建立后如何转发 |
传统 kube-proxy 可以使用 iptables 或 IPVS;较新的部署也可能使用 nftables。部分 eBPF CNI 直接实现 Service 负载均衡,绕过 kube-proxy 的主要规则路径。必须先看目标集群的 CNI、kube-proxy 模式和 Node 实际规则,不能从一段 YAML 推断数据面。
4. Service:稳定入口,不是稳定后端
Service 给调用方提供稳定名称和虚拟 IP;EndpointSlice 保存当前可选后端。控制面负责让 EndpointSlice 随 Pod readiness 和标签匹配变化,数据面负责在每条新连接上把 Service 目标转换为某个后端。
text
Deployment / Pod labels
-> EndpointSlice controller 维护 ready endpoints
-> Service 转发实现选择一个 endpoint
-> 后续报文沿已建立连接的状态转发因此 Service 的 API 对象存在,不表示流量一定能通。还要同时满足:EndpointSlice 有 ready endpoint、Node 上的转发实现已更新、路由可达、策略允许、目标进程正在监听。
5. conntrack:连接状态不是业务状态
NAT 需要在回包时恢复原来的五元组,所以 Linux netfilter 常使用 conntrack 保存连接跟踪记录:源/目的 IP 与端口、协议、NAT 映射和生命周期。它解释了为什么“新连接失败,但已有连接还在工作”以及为什么高并发短连接会耗尽 Node 的 conntrack 表。
text
新 TCP 流
-> 匹配 Service / NAT 规则
-> 选择后端并建立跟踪项
-> 回包按同一映射恢复
-> 超时或关闭后回收这不是所有 eBPF 网络实现都必须采用的内部路径;有些实现维护自己的连接或 NAT 状态。可迁移的事实是:任何做状态化负载均衡或 NAT 的数据面,都必须保存足以让同一流量前后一致的状态。
6. 一次失败怎样定位
从一条失败请求开始,逐层保留证据,而不是先改 NetworkPolicy:
| 节点 | 要确认的事实 | 典型证据 |
|---|---|---|
| 应用 | DNS、目的地址、连接超时或被拒绝 | 应用日志、getaddrinfo/DNS 查询、socket 错误 |
| Pod netns | IP、默认路由、接口是否存在 | ip addr、ip route、ss |
| Service 控制面 | Service selector 与 ready endpoint 是否正确 | Service、EndpointSlice、Pod Ready 条件 |
| Node 数据面 | 后端选择、NAT、路由或策略是否命中 | iptables/nftables/IPVS/eBPF 的目标工具 |
| 对端 | 进程监听、回程路由、策略允许 | ss -lntp、路由和网络策略记录 |
一个常见的误判是:DNS 已解析,所以网络没问题。DNS 只证明名称到 IP 的映射;它不证明 Service 有 endpoint,也不证明数据面规则、MTU、NetworkPolicy、conntrack 容量或对端进程正确。
7. 从网络学到的架构规则
- 用稳定语义隔离可替换实现:应用依赖 socket 和 Service;底层可以换 CNI、overlay 或 Service 数据面。
- 控制面和数据面分开验证:EndpointSlice 正确是控制面证据,Node 能转发包才是数据面证据。
- 每一个“映射”都要有归属与生命周期:Pod IP、veth、路由、NAT 状态、endpoint 都要能创建、更新、回收。
- 快路径不读中心数据库:数据面在 Node 本地按已下发规则转发;API Server 负责声明、观察和分发,而不是转发每个包。
源码与资料
- Kubernetes 网络模型:Cluster Networking、Service、EndpointSlice,访问于 2026-07-31。
- CNI 规范与参考实现:containernetworking/cni、CNI specification,访问于 2026-07-31。CNI 定义插件调用与网络配置边界,不规定所有集群的 Service 实现。
- Linux network namespace:Network Namespaces,Netfilter 文档:Netfilter,访问于 2026-07-31。
- Kubernetes 网络代码入口:
kubernetes/pkg/proxy/、kubernetes/pkg/controller/endpointslice/,访问于 2026-07-31。目标版本的后端与调用链须在源码和 Node 配置上复核。