Skip to content

Service 控制面与节点代理 ​

专题索引 / 上一篇:Pod 网络:从 socket 到 Service

Service 把“我要访问这组后端”声明成一个稳定入口;它不等于一个常驻的用户态代理进程。Kubernetes 需要把这个声明逐步变成两类可用状态:API 中当前可选的后端,以及每个 Node 能快速执行的本地转发规则。

text
Service selector + Pod labels + Pod readiness
  -> EndpointSlice controller
  -> EndpointSlice(API 中的可选后端)
  -> kube-proxy 或 eBPF 数据面 watch
  -> Node 本地的 Service / endpoint 转发状态
  -> 新连接命中一个后端

1. 谁拥有哪一段事实 ​

对象或组件它拥有的状态不负责什么
Service名称、ClusterIP、端口、selector、流量策略直接保存每个 Pod 是否可用
Pod标签、IP、Ready condition、生命周期决定自己属于哪个 Service
EndpointSlice controller根据 selector 和 Pod 条件物化 endpoint 集合为每个包执行负载均衡
kube-proxy在 Node 维护 Service 到 endpoint 的可执行规则创建 Pod 网络或判断业务健康
CNI / eBPF 数据面连接、路由、策略和可能的 Service 实现维护 Service selector 的业务语义

把这些状态分开,才能解释“Service 存在但连不上”:Service API 对象、EndpointSlice、节点规则和目标进程监听,至少有四个独立的正确性条件。

2. EndpointSlice:把选择器变成可消费的后端集合 ​

对有 selector 的 Service,EndpointSlice controller 根据标签选择 Pod,并将可用地址、端口、地址类型和条件写入一个或多个 EndpointSlice。大规模 Service 会拆成多个 Slice,避免任何单对象无限变大。

text
Pod label matches selector
  + Pod 具备 IP
  + endpoint 条件允许接收流量
  -> EndpointSlice 中出现地址和端口

其中 readiness 不能简化成“Pod 已创建”。通常只有 endpoint 条件允许的后端才被常规 Service 流量选择;终止中的 Pod、publishNotReadyAddresses、serving 与 terminating 条件会改变边界。排障时应读 EndpointSlice 的 endpoints[].conditions,不要只看 kubectl get pods 是否为 Running。

没有 selector 的 Service 是另一条边界:Kubernetes 不会自动替它创建 EndpointSlice,使用者必须自己维护后端。这常用于把集群内入口指向外部系统;也意味着后端正确性不再由 Pod label 自动收敛。

3. Node 如何把 API 状态变成快路径 ​

kube-proxy 在每个 Node watch Service 和 EndpointSlice。它先维护内存中的 Service/endpoint 视图,再在同步时将差异编译为 Node 可执行的规则。传统后端包括 iptables 或 IPVS;较新的 kube-proxy 可使用 nftables。eBPF CNI 也可能替代 kube-proxy 的主要 Service 路径,但这不是 kube-proxy 本身的工作。

text
API watch event
  -> kube-proxy 本地对象缓存
  -> syncProxyRules
  -> iptables / IPVS / nftables 规则或表
  -> 新连接匹配 ClusterIP:port
  -> DNAT 或等价映射到 endpoint IP:targetPort

这里的设计取舍很明确:API Server 不在每个包上参与决策。它只传播声明状态;Node 物化一份本地数据面,报文才能在不读取 etcd 的前提下转发。代价是状态传播有延迟,排障必须分别验证控制面已收敛和本地规则已更新。

4. 一次连接与 endpoint 变化 ​

Service 的负载均衡通常以连接为粒度,而不是每个 TCP 包随机选择后端。首次匹配时,数据面选择一个 endpoint 并建立 NAT 或等价的流状态;后续同一连接要保持一致,回包才能正确回到客户端。

text
client -> ClusterIP:443
  -> 选择 10.2.4.18:8443
  -> 保存连接映射
  -> 后续同一流量继续到 10.2.4.18:8443

EndpointSlice 删除一个后端后,新连接应不再选择它;已有连接是否继续、何时断开,取决于协议、conntrack/NAT 状态、应用优雅终止和具体数据面。发布系统不能只依赖“endpoint 已移除”来保证长连接立即迁走。

5. 用四段证据排查 Service 不通 ​

层先确认什么常见断点
声明Service 的 port、targetPort、selector 与类型selector 写错、targetPort 不对应
后端事实EndpointSlice 是否存在且有 ready endpointPod 未 Ready、端口未解析、无 selector 却没人维护 Slice
Node 物化目标 Node 已消费变更并安装规则kube-proxy 异常、规则同步延迟、模式判断错误
目标可达性endpoint IP 可路由,进程监听并允许回程CNI 路由、NetworkPolicy、MTU、进程未监听

不要跳过第二层。一个没有 endpoint 的 Service 在不同实现中可能表现为拒绝、超时或无可用后端;这首先是后端收敛问题,不是应用 DNS 问题。

6. 从实现读架构 ​

读源码时先跟状态流,而不是先读规则生成细节:

text
Service / Pod 变化
  -> EndpointSlice controller 写 API
  -> kube-proxy config watcher 收到 Slice
  -> serviceChangeTracker / endpointsChangeTracker 合并变化
  -> syncProxyRules 物化本地状态

Kubernetes 主仓库的常用入口:

text
pkg/controller/endpointslice/endpointslice_controller.go
pkg/proxy/config/config.go
pkg/proxy/iptables/proxier.go
pkg/proxy/ipvs/proxier.go
pkg/proxy/nftables/proxier.go

路径和内部对象会随 Kubernetes 版本演进,阅读时要固定 tag;重要的是识别这条架构链:声明对象 -> 派生对象 -> 本地物化 -> 数据面执行。

资料与边界 ​

本文描述的是 Kubernetes 的对象与职责关系。ClusterIP 如何落实为规则、是否使用 conntrack、是否由 eBPF 接管,取决于安装的 kube-proxy 模式、CNI、内核能力和节点配置,需要在目标环境验证。

学习规划与研究资料库