Service 不是代理,是一个承诺
一句话总结
Service 对象不传送流量。它只是声明通过 selector 建立 backend 列表(EndpointSlice),实际转发 packet 的是节点 data plane(kube-proxy 的 iptables/nftables 或 CNI 的 eBPF)。理解这种分离,就能把“Service 存在却无法连接”拆成三层检查。
为什么需要它
Pod 死亡重建时 IP 会改变,因此需要名称和稳定的虚拟 IP。Service 提供了这两项。
问题在于谁维护 backend 列表。Service 写入 selector 后,endpoint 控制器会寻找标签匹配且 Ready 的 Pod,填充 EndpointSlice。selector 哪怕差一个字符,EndpointSlice 也会正常创建,但内容为空,不会报错。Service、Deployment 和 Pod 全都 Ready,只有连接失败。
所以诊断 Service 故障的第一个问题永远相同:**“endpoint 中有 IP 吗?”**有,就是 data plane 问题;没有,就是 selector 或 Pod Ready 问题。
它如何运作
| 类型 | 工作 | 使用场景 |
|---|---|---|
| ClusterIP | 一个集群内部虚拟 IP | 默认值 |
| NodePort | 打开所有节点的 30000~32767 端口 | 临时从外部访问 |
| LoadBalancer | NodePort + 外部 LB provisioning | 云/MetalLB |
| ExternalName | 不使用 IP,只返回 CNAME | 指向集群外系统 |
| headless (clusterIP None) | 不使用 VIP,直接返回 Pod IP | StatefulSet、客户端 LB |
有两个以上端口时,每个端口都必须有 name,以便 Ingress 或其他对象通过名称引用端口。
NetworkPolicy 最容易写错的是 from 数组的结构。
- 有两个
from项,分别是 namespaceSelector、podSelector → OR。任一个匹配就允许。 - 只有一个
from项,其中同时包含两者 → AND。只允许该命名空间中带该标签的 Pod。
YAML 只差一个短横线(-),含义却完全相反。考试和实际工作都会在这里分出结果。
在实际工作中会遇到的情况
**案例 1——完全没有 kube-proxy 的集群。**用 kubeadm 1.34 重建家庭实验室时,通过 --skip-phases=addon/kube-proxy 从一开始就不安装 kube-proxy,并以 kubeProxyReplacement=true 安装 Cilium 1.20.1。仅凭声称不够,还进行了实际计数。
kube-proxy 파드 수: 0
노드의 iptables KUBE- 체인 개수: 0
使用 kube-proxy 的集群中,KUBE-SERVICES、KUBE-SVC-*、KUBE-SEP-* 链会随服务数量达到数十到数百条,但这里为 0。那么 Service 如何工作?dump eBPF map 会看到如下内容。
SERVICE ADDRESS BACKEND ADDRESS (REVNAT_ID) (SLOT)
10.96.0.10:53/TCP (1) 10.244.0.150:53/TCP (4) (1)
10.96.0.1:443/TCP (1) 10.0.0.120:6443/TCP (1) (1)
frontend 与 backend 直接保存在内核 hash map 中。iptables 方式会随服务增多而线性拉长规则链,eBPF 使用 hash map 查询,与服务数量无关。Service 对象保持不变,只替换 data plane,这种分离在这里成为实物。
随之而来还有 bootstrap 问题。启用 kubeProxyReplacement 时,必须同时设置 k8sServiceHost=10.0.0.120、k8sServicePort=6443。没有 kube-proxy,就还没有把 kubernetes Service ClusterIP(10.96.0.1)转换为真实 apiserver 地址的主体,而将承担该职责的 Cilium 自己尚未启动。为解决鸡生蛋问题,必须直接告知真实地址。
**案例 2——iptables 无法处理 L7。**同一集群中曾应用一条让 GET 返回 200、POST 返回 403 的策略。IP 和端口相同,只按 HTTP method 区分。iptables 位于 L3/L4,看不到 method,因此原理上就不可能做到。标准 NetworkPolicy 同样只到 L3/L4。这个例子清楚划定了 CKA 范围内 NetworkPolicy 的能力边界。
**案例 3——Endpoints 已成为旧称。**v1.33 中 Endpoints API 已 deprecated。直接读取 Endpoints 的代码或脚本,应改为通过 kubernetes.io/service-name 标签查询 EndpointSlice。这是升级前检查的常见项目。
后续实验要做什么
第一个实验会创建 ClusterIP、NodePort、headless、multi-port、ExternalName 和 Ingress,并故意让 selector 拼错,使 endpoint 为空,再进行修复。第二个实验从默认拒绝开始,把 NetworkPolicy 逐步缩小为六种形式。本实验环境没有执行策略的 CNI,因此无法确认真实流量拦截,只对对象 spec 评分,转而集中处理 AND 与 OR 等 spec 陷阱。