主题
补充:Kubernetes Service 为什么要用虚拟 IP 而不是 UUID?
本文件记录了对 "Service 中为什么要使用虚拟 IP 而不是 UUID,虚拟 IP 是如何实现的,好处是什么" 这一问题的系统解答。
一、为什么 Service 使用虚拟 IP 而不是 UUID?
1.1 网络协议栈只认识 IP:Port
TCP/UDP 通信的终点是 IP + Port,不是 UUID。如果 Kubernetes 给你一个服务的 UUID,应用程序仍然需要把它解析成 IP 才能建立 socket 连接。
应用调用 connect("uuid-of-service") → 无法直接通信
应用调用 connect("10.96.123.45:80") → 正常通信UUID 只是标识符,没有网络语义。
1.2 UUID 无法直接做负载均衡和路由
Kubernetes Service 的核心能力是把流量分发到多个后端 Pod。负载均衡器、iptables、IPVS、eBPF 这些内核网络机制都是基于 IP 地址工作的。
如果用 UUID,K8s 需要额外开发一套用户态代理来做解析和转发,性能和复杂度都会变差。
1.3 DNS 最终也要解析成 IP
即使通过名字访问服务,比如 curl http://nginx-service,DNS 解析的最后一步也是返回一个 IP 地址:
bash
nslookup nginx-service.default.svc.cluster.local
# 返回:10.96.123.45所以虚拟 IP 是 Service 的最终暴露形式,名字只是方便人类的入口。
二、ClusterIP 虚拟 IP 是如何实现的?
ClusterIP 是一个不绑定任何网络接口的虚拟 IP。它不会出现在 ip addr 的网卡上,而是通过内核规则"拦截"并转发流量。
2.0 ClusterIP 是"真实 IP"还是"虚拟 IP"?
这个问题需要从两个层面理解:
| 角度 | 结论 | 原因 |
|---|---|---|
| 从 TCP/IP 协议栈看 | 是真实 IP | 它是从 Service CIDR(如 10.96.0.0/12)里合法分配出来的 IPv4 地址,数据包的目标地址字段里填的就是这个 IP |
| 从网卡/链路层看 | 是虚拟 IP | 没有任何网卡(物理网卡、veth、bridge、dummy 等)配置上这个 IP,ip addr 也看不到它 |
| 从"谁持有这个 IP"看 | 是虚拟 IP | 网络中没有设备会 ARP 回应这个 IP,内核只是通过规则"假装"这个地址存在并拦截流量 |
内核没有给 ClusterIP 创建虚拟网卡。 流量不是先到达某个"虚拟网卡"再被转发,而是直接在内核网络协议栈的转发路径上被截获:
- iptables 模式:通过
PREROUTING/OUTPUT等 netfilter 钩子,匹配目的 IP 为 ClusterIP 的数据包,直接做 DNAT 到 Pod IP。 - IPVS 模式:在内核的 IPVS 子系统里注册一个 Virtual Server(VS),把
ClusterIP:Port当作 VS 的"服务地址",再调度到 Real Server(Pod IP)。 - eBPF 模式:在 socket 或 XDP/TC 层用 eBPF 程序改写目的地址。
所以更准确的说法是:ClusterIP 是"逻辑上真实、物理上不存在"的地址。内核认识它、规则匹配它,但它不绑定任何接口,也没有对应的网卡。
Kubernetes 通过 kube-proxy 组件来实现,主要有两种模式:
2.1 iptables 模式(传统默认)
text
客户端 Pod
|
v
访问 10.96.123.45:80
|
v
iptables PREROUTING 链匹配到这条规则:
-d 10.96.123.45 --dport 80 -j DNAT --to-destination 10.244.1.5:80 或 10.244.2.7:80
|
v
随机选择一个后端 Pod IPkube-proxy 会:
- 监听 API Server 中 Service 和 Endpoint(EndpointSlice)的变化
- 在节点上动态创建/更新 iptables 规则
- 访问 ClusterIP 时,iptables 做 DNAT,把目标 IP 改成后端 Pod IP
- 负载均衡通过 iptables 的
statistic模块实现随机分发
2.2 ipvs 模式(生产推荐)
text
客户端 Pod
|
v
访问 10.96.123.45:80
|
v
IPVS 虚拟服务器(VS):10.96.123.45:80
|
+--> 真实服务器 RS1: 10.244.1.5:80
+--> 真实服务器 RS2: 10.244.2.7:80kube-proxy 会:
- 为每个 Service 创建一个 IPVS 虚拟服务器(Virtual Server)
- 每个后端 Pod 作为真实服务器(Real Server)
- 支持多种负载均衡算法:rr(轮询)、lc(最少连接)、sh(源地址哈希)等
ipvs 性能更好、规则更清晰、支持更多调度算法,大规模集群建议用 ipvs。
2.3 eBPF 模式(未来趋势)
Cilium 等新型 CNI 直接用 eBPF 在数据路径实现 Service 负载均衡,绕过 iptables/ipvs,性能更高。
三、使用虚拟 IP 的好处
| 好处 | 说明 |
|---|---|
| 兼容现有网络 | 应用无需改造,使用标准 IP:Port 访问 |
| 稳定的访问入口 | Pod IP 会变化,但 ClusterIP 不变 |
| 自动负载均衡 | 流量自动分发到多个后端 Pod |
| 服务发现简单 | 配合 CoreDNS,可用域名访问 |
| 与内核集成 | 利用 iptables/ipvs/eBPF 高效转发 |
| 支持多种类型 | ClusterIP、NodePort、LoadBalancer 都基于同一套虚拟 IP 机制 |
| 故障自愈 | Pod 重建后 IP 变了,Service 后端自动更新,客户端无感知 |
四、实例理解
假设有一个 Service:
yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80
type: ClusterIP创建后:
bash
kubectl get svc nginx
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
# nginx ClusterIP 10.96.123.45 <none> 80/TCP
kubectl get endpoints nginx
# NAME ENDPOINTS
# nginx 10.244.1.5:80,10.244.2.7:80在节点上查看规则(ipvs 模式):
bash
ipvsadm -Ln
# TCP 10.96.123.45:80 rr
# -> 10.244.1.5:80 Masq 1 0 0
# -> 10.244.2.7:80 Masq 1 0 0当 Pod 访问 10.96.123.45:80 时,内核把请求转发到 10.244.1.5:80 或 10.244.2.7:80。
五、一句话总结
Service 的 ClusterIP 是"逻辑上的入口",iptables/ipvs/eBPF 是"物理上的转发"。
- 不用 UUID:UUID 没有网络语义,无法直接用于 TCP/UDP 通信和负载均衡。
- 虚拟 IP 实现:ClusterIP 不绑定网卡,由
kube-proxy通过iptables或ipvs规则将流量 DNAT 到后端 Pod IP。 - 核心好处:提供稳定、兼容、可负载均衡的服务访问入口,让客户端无需关心后端 Pod 的变化。