Skip to content

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, localhost

runtime 调用 CNI plugin。CNI 的职责是按配置为 sandbox 增加和删除网络连接:常见做法是建立一对 veth,把一端放进 Pod namespace,另一端留在 Node;随后配置地址、路由、MTU 和需要的策略或隧道端点。

text
Pod netns                         Node netns
eth0 ---- veth pair ---- host veth
  Pod IP                 bridge / route / overlay device

veth 是成对虚拟网卡:一端发出的包会从另一端收到。它解决“不同 network namespace 如何接起来”;它不定义跨 Node 怎样转发,也不定义 Service 怎样选后端。后两件事由具体 CNI 和 Service 实现承担。

3. CNI、kube-proxy 与 eBPF 的边界 ​

不要把三个职责揉成一个组件:

组件或机制主要负责什么不保证什么
CNIPod 接入、地址、路由、网络策略等 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 netnsIP、默认路由、接口是否存在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. 从网络学到的架构规则 ​

  1. 用稳定语义隔离可替换实现:应用依赖 socket 和 Service;底层可以换 CNI、overlay 或 Service 数据面。
  2. 控制面和数据面分开验证:EndpointSlice 正确是控制面证据,Node 能转发包才是数据面证据。
  3. 每一个“映射”都要有归属与生命周期:Pod IP、veth、路由、NAT 状态、endpoint 都要能创建、更新、回收。
  4. 快路径不读中心数据库:数据面在 Node 本地按已下发规则转发;API Server 负责声明、观察和分发,而不是转发每个包。

源码与资料 ​

学习规划与研究资料库