Kubernetes Service 原理
这篇文章整理 Kubernetes 中 Service 从创建到生效的大致过程。虽然平时使用时只需要写一份 YAML,但在真正生效的背后,实际上会涉及 API Server、Controller、Endpoints 以及 kube-proxy 等多个组件协同工作。
核心问题
很多时候我们只是在 YAML 里定义了一个 Service,然后就直接通过名称或 ClusterIP 去访问它。但真正值得理解的问题是:这个 Service 为什么会生效,它的流量又是怎样准确转发到后端 Pod 的?
Service 是怎么生效的
从流程上看,Service 的创建大致可以分成下面几个阶段:
- 用户提交 Service 定义
- API Server 接收并保存对象
- 控制器根据标签选择后端 Pod
- Endpoints 或 EndpointSlice 被更新
- kube-proxy 刷新转发规则
- 集群内访问开始生效
用户提交 Service 定义
用户通过 kubectl apply -f service.yaml 提交一个 Service 定义,例如:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx # 选择标签为 app: nginx 的 Pod
ports:
- protocol: TCP
port: 80 # Service 暴露的端口
targetPort: 80 # Pod 监听的端口
type: ClusterIP # 默认类型API Server 接收并存储 Service 定义
- API Server 是 Kubernetes 的“大脑”,负责接收所有资源创建/更新请求。
- Service 的定义会被持久化存储到 etcd(Kubernetes 的分布式键值数据库)。
Endpoint Controller 监听 Service 和 Pod 变化
- Endpoint Controller(运行在 Controller Manager 中)会监听两类资源:
- Service 的创建/更新/删除事件。
- Pod 的变动(如新建、删除、标签变更)。
- 关键动作:
- 根据 Service 的
selector(如app: nginx)筛选匹配的 Pod。 - 生成 Endpoints 对象(或更新现有的),记录当前匹配 Pod 的 IP 和端口列表。
- 根据 Service 的
kubectl get endpoints nginx-service输出示例:
NAME ENDPOINTS AGE
nginx-service 10.244.1.2:80,10.244.2.3:80 5s- 如果没有任何 Pod 匹配
selector,Endpoints 列表为空(但 Service 仍存在)。
kube-proxy 监听 Service 和 Endpoints 变化
- kube-proxy 运行在每个节点上,负责实现 Service 的流量转发逻辑。
- 它通过监听 API Server 获取以下信息:
- Service 的虚拟 IP(ClusterIP)和端口。
- Endpoints 中 Pod 的 IP:Port 列表。
根据模式不同,kube-proxy 会把转发逻辑落到不同的网络实现上。
iptables 模式
- kube-proxy 会在节点的 iptables 中插入规则:
- 当访问
ClusterIP:Port时,DNAT(目标地址转换)到某个 Pod IP:Port。 - 使用
random或probability算法实现负载均衡。
- 当访问
- 查看规则示例:
iptables-save | grep nginx-serviceIPVS 模式
- kube-proxy 直接调用 Linux 内核的 IPVS 模块:
- 创建虚拟服务器(VIP = ClusterIP)。
- 添加多个真实服务器(Real Server = Pod IP:Port)。
- 查看规则示例:
ipvsadm -LnDNS 记录如何生效
- CoreDNS(或 kube-dns)会监听 Service 的创建:
- 为 Service 分配一个 DNS 名称,格式为
<service-name>.<namespace>.svc.cluster.local。 - 例如,
nginx-service.default.svc.cluster.local解析到 ClusterIP。
- 为 Service 分配一个 DNS 名称,格式为
- 集群内的 Pod 可以通过 DNS 名称访问 Service,无需硬编码 IP。
完整流程示例
假设创建一个 ClusterIP 类型的 Service:
- 用户提交 YAML:
kubectl apply -f nginx-service.yaml- API Server 存储到 etcd。
- Endpoint Controller 发现 Service 和匹配的 Pod,生成 Endpoints。
- kube-proxy 在各节点更新 iptables/IPVS 规则。
- CoreDNS 添加 Service 的 DNS 记录。
- 其他 Pod 可通过以下方式访问 Service:
- DNS 名称:
nginx-service.default.svc.cluster.local - ClusterIP:
10.96.123.456:80 - 流量会被自动转发到后端的 Pod(如
10.244.1.2:80)。
关键组件交互图
+---------+ +------------+ +---------------+ +-----------+
| kubectl | ----> | API Server | ----> | Endpoint Ctrl | ----> | Endpoints |
+---------+ +------------+ +---------------+ +-----------+
|
v
+------------+ +---------------+ +----------------+ +-----+
| kube-proxy | <---- | Service Watch | <---- | Endpoints Watch | <-- | Pods |
+------------+ +---------------+ +----------------+ +-----+
|
v
+------------------+
| iptables/IPVS规则 |
+------------------+如何验证 Service 是否创建成功
- 检查 Service 和 Endpoints:
kubectl get svc nginx-service
kubectl get endpoints nginx-service- 检查 DNS 解析(在 Pod 内执行):
nslookup nginx-service.default.svc.cluster.local- 检查节点上的 iptables/IPVS 规则:
iptables-save | grep nginx-service # iptables 模式
ipvsadm -Ln | grep 10.96.123.456 # IPVS 模式常见扩展场景
Headless Service
- 设置
clusterIP: None,不分配 ClusterIP。 - DNS 直接返回 Pod IP 列表(适用于 StatefulSet)。
Session Affinity
- 通过
sessionAffinity: ClientIP让同一客户端的请求固定到同一个 Pod。
ExternalName Service
- 将 Service 映射到外部 DNS(如数据库服务):
kind: Service
spec:
type: ExternalName
externalName: mysql.example.com总结
| 步骤 | 关键组件 | 动作 |
|---|---|---|
| 1. 提交 YAML | kubectl/API Server | 接收并存储 Service 定义到 etcd |
| 2. 匹配 Pod | Endpoint Controller | 根据 selector 生成 Endpoints 对象 |
| 3. 配置转发规则 | kube-proxy | 更新 iptables/IPVS 规则 |
| 4. 注册 DNS | CoreDNS | 添加 Service 的 DNS 记录 |
| 5. 流量代理 | kube-proxy | 将请求转发到后端 Pod |
Service 的创建过程体现了 Kubernetes 的声明式 API 和控制器模式。真正让访问生效的,并不是单一组件,而是 API Server、Controller、CoreDNS 和 kube-proxy 等多个组件协同工作的结果。理解这条链路之后,再排查 Service 不通、DNS 异常或流量不均衡时,思路会清晰很多。