主题
Kubernetes Service 原理由浅入深
本文从“为什么需要 Service”开始,逐步深入到 kube-proxy 实现、EndpointSlice、DNS、负载均衡、高级流量策略等核心机制。适合已经了解 Pod 概念、希望系统掌握 Service 原理的读者。
目录
- 初识 Service:解决 Pod 的动态性
- Service 的核心抽象
- Service 类型详解
- Endpoints 与 EndpointSlice
- kube-proxy:Service 的实现引擎
- Cluster DNS 与服务发现
- Headless Service 与有状态服务
- ExternalName 与外部服务映射
- LoadBalancer 与云厂商集成
- Ingress 与 Service 的关系
- 高级话题:拓扑感知、会话保持、流量策略
- 故障排查常用命令
- 总结
1. 初识 Service:解决 Pod 的动态性
1.1 Pod 的问题
在 Kubernetes 中,Pod 是最小的调度单元,但 Pod 具有以下特点:
- 生命周期短暂:Pod 可能因扩缩容、滚动更新、节点故障、资源不足等原因被销毁和重建。
- IP 不固定:每个 Pod 被调度后才会分配 IP,重建后 IP 通常改变。
- 动态扩缩容:ReplicaSet/Deployment 可以随时增加或减少 Pod 数量。
如果直接通过 Pod IP 访问服务,调用方需要时刻跟踪后端 Pod 的变化,这在分布式系统中不可行。
1.2 Service 的引入
Kubernetes 引入 Service 作为一层稳定的抽象:
- 为后端一组 Pod 提供一个 稳定的虚拟 IP(ClusterIP)。
- 通过 Label Selector 动态关联 Pod。
- 在 Pod 变化时自动更新转发目标。
- 提供 负载均衡 和 服务发现 能力。
┌─────────────────────────────────────┐
│ Service │
│ ClusterIP: 10.96.123.45 │
│ selector: app=web, tier=front │
└──────────────┬──────────────────────┘
│
┌───────┼───────┐
▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐
│ Pod │ │ Pod │ │ Pod │
│10.244│ │10.244│ │10.244│
│.1.10 │ │.1.11 │ │.1.12 │
└──────┘ └──────┘ └──────┘2. Service 的核心抽象
2.1 Service 定义示例
yaml
apiVersion: v1
kind: Service
metadata:
name: web-service
namespace: default
spec:
type: ClusterIP # Service 类型
selector:
app: web # 通过标签选择后端 Pod
ports:
- port: 80 # Service 暴露的端口
targetPort: 8080 # 后端 Pod 容器端口
protocol: TCP # 默认 TCP,可选 UDP/SCTP2.2 关键字段说明
| 字段 | 含义 |
|---|---|
spec.type | Service 类型:ClusterIP / NodePort / LoadBalancer / ExternalName |
spec.selector | 标签选择器,决定哪些 Pod 属于该 Service |
spec.ports[].port | Service 自身暴露的端口 |
spec.ports[].targetPort | 后端 Pod 上容器实际监听的端口 |
spec.ports[].nodePort | NodePort 类型时节点上暴露的端口(默认 30000-32767) |
spec.clusterIP | 自动分配的虚拟 IP,可手动指定 None 表示 Headless |
spec.sessionAffinity | 会话保持策略,None 或 ClientIP |
spec.externalTrafficPolicy | 外部流量策略,Cluster 或 Local |
spec.internalTrafficPolicy | 内部流量策略,Cluster 或 Local |
2.3 Service 与 Pod 的关联
Service 本身不直接代理流量,它通过 Endpoints/EndpointSlice 对象记录后端 Pod 的 IP:Port。kube-proxy 监听这些对象,并在节点上配置转发规则。
3. Service 类型详解
3.1 ClusterIP(默认)
- 分配一个仅在集群内部可达的虚拟 IP。
- 适用于集群内部微服务之间的调用。
- 流量路径:
Client Pod -> Service ClusterIP -> kube-proxy 转发 -> Backend Pod。
yaml
spec:
type: ClusterIP
selector:
app: web
ports:
- port: 80
targetPort: 80803.2 NodePort
- 在 ClusterIP 基础上,在每个节点上开放一个固定端口(NodePort)。
- 外部流量可通过
<NodeIP>:<NodePort>访问 Service。 - NodePort 范围默认
30000-32767,可通过 apiserver 参数调整。
yaml
spec:
type: NodePort
selector:
app: web
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # 可选,不指定则自动分配3.3 LoadBalancer
- 在 NodePort 基础上,请求云厂商(或 MetalLB 等)创建一个外部负载均衡器。
- 自动分配一个外部 IP(
EXTERNAL-IP),外部用户可直接访问。 - 云厂商负责将流量转发到节点 NodePort 或 Pod(取决于实现)。
yaml
spec:
type: LoadBalancer
selector:
app: web
ports:
- port: 80
targetPort: 80803.4 ExternalName
- 没有 selector,也不分配 ClusterIP。
- 将 Service 名称映射到一个外部 DNS 名称(CNAME)。
- 常用于将集群内服务平滑迁移到外部服务,或反之。
yaml
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: db.example.com查询 external-db.default.svc.cluster.local 会返回 db.example.com 的 CNAME 记录。
4. Endpoints 与 EndpointSlice
4.1 Endpoints(旧机制)
当 Service 定义了 selector,Kubernetes 控制平面会自动创建同名的 Endpoints 对象,记录所有匹配 Pod 的 IP:targetPort。
yaml
apiVersion: v1
kind: Endpoints
metadata:
name: web-service
subsets:
- addresses:
- ip: 10.244.1.10
- ip: 10.244.1.11
ports:
- port: 80804.2 EndpointSlice(推荐)
Endpoints 在大规模集群中存在性能和可扩展性问题(单个对象过大)。Kubernetes 1.21+ 默认使用 EndpointSlice:
- 将后端端点拆分为多个 Slice,每个最多 100 个端点。
- 支持更丰富的元数据(拓扑信息、条件状态等)。
- 更高效的增量更新,降低网络和控制平面压力。
yaml
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: web-service-abc12
labels:
kubernetes.io/service-name: web-service
addressType: IPv4
ports:
- name: http
protocol: TCP
port: 8080
endpoints:
- addresses:
- 10.244.1.10
conditions:
ready: true
nodeName: node-1
zone: cn-north-1a4.3 无 Selector 的 Service
如果 Service 没有 selector,需要手动创建 Endpoints/EndpointSlice,用于指向集群外部服务或自定义后端。
5. kube-proxy:Service 的实现引擎
Service 的 ClusterIP 是一个虚拟 IP,本身没有进程监听。真正实现负载均衡的是运行在每个节点上的 kube-proxy。
5.1 kube-proxy 三种工作模式
5.1.1 userspace 模式(已废弃)
- kube-proxy 作为用户态代理进程监听端口。
- 流量先进入 kube-proxy,再由它转发给后端 Pod。
- 性能差、用户态/内核态切换开销大,现代集群已不再使用。
5.1.2 iptables 模式(默认)
- kube-proxy 监听 Service/EndpointSlice 变化,动态生成 iptables 规则。
- 数据包在内核态直接被 DNAT 到后端 Pod,无需用户态进程参与。
- 负载均衡采用随机算法(probability 规则链)。
优点:性能较好,无额外进程。 缺点:
- 后端 Pod 数量多时规则链庞大,更新慢。
- 负载均衡算法单一。
- 无法处理长连接的优雅重试(老连接可能仍指向失效 Pod)。
数据包流向:
Client -> PREROUTING -> KUBE-SERVICES -> KUBE-SVC-XXX -> KUBE-SEP-XXX -> DNAT -> Pod5.1.3 ipvs 模式(推荐大规模使用)
- kube-proxy 调用 Linux IPVS(IP Virtual Server)模块创建虚拟服务器。
- 每个 Service 对应一个 IPVS virtual server,每个后端 Pod 对应一个 real server。
- 支持多种调度算法:
rr(轮询)、lc(最少连接)、dh、sh、sed、nq。
优点:
- 转发性能接近直接路由,规则更新快。
- 支持的负载均衡算法丰富。
- 后端数量大时优势明显。
启用方式:
bash
kube-proxy --proxy-mode=ipvs --ipvs-scheduler=rr需要节点加载 ip_vs 等相关内核模块。
5.2 数据包完整流程(以 iptables 为例)
┌─────────────┐ ┌─────────────────┐ ┌─────────────┐
│ Client Pod │ ──────> │ Service:80 │ │ │
│ 10.244.1.5 │ │ 10.96.123.45 │ │ Backend │
└─────────────┘ └────────┬────────┘ │ Pod │
│ │ 10.244.1.10 │
│ kube-proxy └─────────────┘
│ iptables DNAT ┌─────────────┐
└────────────────> │ Backend │
│ Pod │
│ 10.244.1.11 │
└─────────────┘5.3 查看 kube-proxy 规则
bash
# 查看 iptables 中 Service 相关链
iptables -t nat -L KUBE-SERVICES -n | grep <service-name>
# 查看 ipvs 虚拟服务
ipvsadm -Ln6. Cluster DNS 与服务发现
6.1 DNS 解析机制
Kubernetes 集群通常运行 CoreDNS(或旧版 kube-dns),为 Service 提供 DNS 记录:
| 记录类型 | 示例 | 说明 |
|---|---|---|
| A / AAAA | web-service.default.svc.cluster.local | 解析为 ClusterIP |
| SRV | _http._tcp.web-service.default.svc.cluster.local | 端口服务记录 |
| PTR | 反向解析 Pod / Service IP | 用于日志、监控 |
| CNAME | external-db.default.svc.cluster.local | ExternalName 类型 |
6.2 Pod DNS 策略
通过 dnsPolicy 控制 Pod 的 DNS 行为:
ClusterFirst(默认):优先使用集群 DNS。Default:继承节点 DNS 配置。ClusterFirstWithHostNet:hostNetwork Pod 使用集群 DNS。None:完全自定义 DNS 配置,配合dnsConfig。
6.3 同命名空间与跨命名空间访问
bash
# 同命名空间
curl http://web-service
# 跨命名空间
curl http://web-service.production
# 完整 FQDN
curl http://web-service.production.svc.cluster.local7. Headless Service 与有状态服务
7.1 什么是 Headless Service
将 spec.clusterIP 设为 None,Service 不再分配 ClusterIP,kube-proxy 也不为其创建转发规则。
DNS 查询直接返回后端 Pod 的 IP 列表(A 记录)或有状态 Pod 的 SRV 记录。
yaml
apiVersion: v1
kind: Service
metadata:
name: web-headless
spec:
clusterIP: None
selector:
app: web
ports:
- port: 80
targetPort: 80807.2 适用场景
- 需要客户端自己决定连接哪个后端(如自定义负载均衡、分片)。
- 有状态服务需要稳定的网络标识(配合 StatefulSet 使用)。
- 需要直接获取所有后端 Pod IP(如 Prometheus 服务发现)。
7.3 StatefulSet 与 Headless Service
StatefulSet 每个 Pod 拥有稳定的:
- 名称:
<statefulset-name>-<ordinal>,如web-0、web-1。 - 网络标识:
<pod-name>.<service-name>.<namespace>.svc.cluster.local。 - 存储:通过 volumeClaimTemplates 每个 Pod 拥有独立 PVC。
Pod: web-0
DNS: web-0.web-headless.default.svc.cluster.local8. ExternalName 与外部服务映射
ExternalName 本质是一个 CNAME 别名:
yaml
apiVersion: v1
kind: Service
metadata:
name: external-api
spec:
type: ExternalName
externalName: api.external-provider.com集群内应用可以像访问内部 Service 一样访问外部域名:
bash
curl http://external-apiDNS 会返回 api.external-provider.com 的 CNAME,再由外部 DNS 解析为实际 IP。
9. LoadBalancer 与云厂商集成
9.1 云厂商 LoadBalancer
在公有云(AWS、GCP、Azure、阿里云、腾讯云等)中,创建 type: LoadBalancer 的 Service 会触发云控制器管理器(Cloud Controller Manager)调用云平台 API 创建负载均衡器:
- 分配外部 IP。
- 配置健康检查。
- 将流量转发到节点 NodePort 或 Pod(部分云厂商支持直接路由)。
9.2 裸金属方案:MetalLB
在自建机房或裸金属集群中,可使用 MetalLB 为 LoadBalancer Service 分配 IP:
- Layer 2 模式:通过 ARP/NDP 宣告 IP,简单但存在单点瓶颈。
- BGP 模式:与路由器建立 BGP 会话,实现真正的负载均衡和高可用。
9.3 负载均衡器健康检查
云厂商 LB 通常探测节点上的 NodePort。如果节点上没有后端 Pod(例如 externalTrafficPolicy=Local),LB 不应将流量发送到该节点,否则会出现连接失败。
10. Ingress 与 Service 的关系
Ingress 不是 Service 类型,而是基于 HTTP/HTTPS 的七层路由规则。Ingress Controller(如 NGINX Ingress Controller、Traefik)通过监听 Ingress 资源,将外部流量按域名、路径路由到不同的 Service。
Internet
│
▼
┌─────────────┐
│ Ingress │ <- Ingress Controller (e.g., NGINX)
│ Controller │
└──────┬──────┘
│
▼
┌─────────────┐
│ Service │ <- 四层 ClusterIP/NodePort
│ ClusterIP │
└──────┬──────┘
│
▼
Backend PodsService 是四层的,Ingress 是七层的。Ingress 最终依赖 Service 将流量转发到 Pod。
11. 高级话题:拓扑感知、会话保持、流量策略
11.1 会话保持(Session Affinity)
yaml
spec:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 默认 3 小时开启后,同一客户端 IP 的流量始终转发到同一个后端 Pod(基于 iptables/ipvs 的 recent 模块)。注意:对 NAT 后的多客户端可能集中到同一 IP。
11.2 外部流量策略(externalTrafficPolicy)
Cluster(默认):外部流量进入任意节点后,可能再被转发到其他节点的 Pod。SNAT 后源 IP 被改写为节点 IP。Local:流量只转发到本节点上的 Pod,保留真实客户端源 IP。但如果本节点没有 Pod,连接会被丢弃。
11.3 内部流量策略(internalTrafficPolicy)
Cluster(默认):内部流量可转发到任意节点的 Pod。Local:只转发到与客户端同一节点的 Pod,减少跨节点流量,但可能降低负载均衡效果。
11.4 拓扑感知路由(Topology Aware Routing / Topology Aware Hints)
EndpointSlice 可携带 zone 或 nodeName 等拓扑信息,配合 Service 的注解或 topologyKeys 历史字段,使流量优先在同一可用区或同一节点内路由,降低延迟和跨区带宽。
yaml
metadata:
annotations:
service.kubernetes.io/topology-mode: Auto11.5 健康检查与就绪
只有 Readiness Probe 通过且未处于 Terminating 状态的 Pod 才会被加入 Endpoints/EndpointSlice。因此,务必为生产服务配置 readinessProbe,否则未就绪的 Pod 会收到流量。
12. 故障排查常用命令
bash
# 查看 Service 列表
kubectl get svc -o wide
# 查看 Endpoints/EndpointSlice
kubectl get endpoints <svc-name>
kubectl get endpointslices -l kubernetes.io/service-name=<svc-name>
# 查看 Pod 标签是否匹配 Service selector
kubectl get pods -l app=web --show-labels
# 从 Pod 内访问 Service
curl http://<svc-name>.<namespace>.svc.cluster.local:<port>
# 查看 kube-proxy 日志
kubectl logs -n kube-system -l k8s-app=kube-proxy
# 节点上查看 iptables/ipvs 规则
iptables -t nat -L KUBE-SERVICES -n | grep <svc-name>
ipvsadm -Ln
# 检查 DNS 解析
kubectl run -it --rm debug --image=busybox:1.28 --restart=Never -- nslookup <svc-name>
# 查看 Service 事件
kubectl describe svc <svc-name>13. 总结
| 主题 | 核心要点 |
|---|---|
| Service 本质 | 为一组 Pod 提供稳定访问入口的抽象 |
| 服务发现 | 通过 DNS(CoreDNS)+ Endpoints/EndpointSlice 实现 |
| 流量转发 | kube-proxy 通过 iptables/ipvs 将 ClusterIP 映射到后端 Pod |
| 类型选择 | ClusterIP 内网、NodePort 测试、LoadBalancer 公网、ExternalName 别名 |
| 有状态服务 | Headless Service + StatefulSet 提供稳定网络标识 |
| 七层路由 | Ingress 依赖 Service 完成最终 Pod 转发 |
| 高级策略 | 会话保持、externalTrafficPolicy、internalTrafficPolicy、拓扑感知 |
| 生产注意 | 配置 readinessProbe,理解 externalTrafficPolicy=Local 的源 IP 保留与风险 |
掌握 Service,就掌握了 Kubernetes 集群内部服务通信的“交通枢纽”。
文档生成时间:2026-07-17