Service 服务发现
Pod IP 会随重建变化,因此不能直接作为服务地址。Service 通过标签选择一组健康 Pod,并提供稳定虚拟 IP 和 DNS 名称。本章聚焦集群内部的服务发现与流量入口。
本篇目标:理解服务发现需求,能写出生产可用的 ClusterIP Service,掌握四种 Service 类型,理解 kube-proxy/iptables/IPVS 的转发机制,并会配置 Session Affinity。

1. 服务发现与网络暴露需求
用户 / 外部系统
|
Ingress / Gateway / LoadBalancer
|
Service(稳定 DNS 与后端选择器)
|
Ready Pod(动态 IP,来自 Deployment 等工作负载)
每一层都应独立排查。应用监听端口正常,不代表 Service selector 正确;Service 有后端,也不代表 Ingress Controller 已安装或 DNS 已解析。
| 需要哪种访问? | 选择 |
|---|---|
| 集群内部服务 | ClusterIP(默认首选) |
| 临时调试、内部测试 | NodePort 或 port-forward |
| 云环境四层入口(TCP/UDP) | LoadBalancer |
| HTTP/HTTPS 域名和路径路由 | Ingress(需要 Ingress Controller,见第 10 章) |
| 更精细的网关控制 | Gateway API(见第 10 章) |
2. Service 的四种核心类型
| 类型 | 适用场景 | 注意事项 |
|---|---|---|
ClusterIP | 集群内部服务 | 默认首选,不直接暴露公网 |
NodePort | 临时调试、简单内网环境 | 开放每个节点端口,生产需谨慎 |
LoadBalancer | 云环境的四层入口 | 依赖云控制器,关注公网暴露和成本 |
ExternalName | 把集群内部的 Service 名解析到外部域名 | 仅 DNS 别名,不代理流量 |
2.1 ClusterIP(默认)
apiVersion: v1
kind: Service
metadata:
name: api
namespace: learning
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: api
ports:
- name: http
port: 80 # Service 对外暴露的稳定端口
targetPort: 8080 # 容器实际监听端口
同 Namespace 内可访问 http://api;跨 Namespace 使用 http://api.learning.svc.cluster.local。
kubectl get service,endpointslice -n learning
kubectl describe service api -n learning
若 EndpointSlice 没有地址,优先检查 Service selector、Pod labels 和 Pod readiness。
2.2 NodePort
spec:
type: NodePort
selector:
app.kubernetes.io/name: api
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # 省略时自动分配 30000-32767
每个节点都会开放 nodePort 端口,curl http://<node-ip>:30080 可访问。适合本地实验与临时调试,生产需谨慎(端口开放面大)。
2.3 LoadBalancer
spec:
type: LoadBalancer
selector:
app.kubernetes.io/name: api
ports:
- port: 80
targetPort: 8080
云环境下由 cloud-controller-manager 创建云负载均衡器,EXTERNAL-IP 变为公网/内网 LB 地址。关注公网暴露范围与成本。
2.4 ExternalName
spec:
type: ExternalName
externalName: db.example.com
只返回 CNAME 别名,不代理流量。适合让集群内的应用以 Service 名方式访问外部域名。
3. 负载均衡与流量转发底层
3.1 EndpointSlice
Service 通过 selector 匹配 Pod,Kubernetes 自动维护 EndpointSlice 记录后端地址:
kubectl get endpointslice -n learning -l kubernetes.io/service-name=api
kubectl get endpoints -n learning # 旧 API,新版本用 EndpointSlice
只有 Ready 的 Pod 会进入 EndpointSlice(readiness 探针失败会自动摘除,见第 15 章)。
3.2 kube-proxy:iptables / IPVS
ClusterIP 是虚拟 IP,并不对应某个具体的网卡或 Pod。每个节点上的 kube-proxy 会 watch Service 与 EndpointSlice,把发往 ClusterIP 的流量负载均衡地转发到后端 Ready Pod:
| 模式 | 实现 | 特点 |
|---|---|---|
iptables(默认) | 用 iptables 规则做 DNAT | 简单可靠,但规则随服务数量增长,转发为随机选择 |
ipvs | 用内核 IPVS 做负载均衡 | 支持更多调度算法(rr/wrr/lc 等)、扩展性更好 |
kubectl get cm -n kube-system kube-proxy -o yaml | grep -i mode
kubectl describe svc api -n learning # 查看 ClusterIP 分配
3.3 会话保持(Session Affinity)
默认情况下,同一客户端的多次请求可能被转发到不同后端 Pod。需要"同一来源固定到同一 Pod"时(如基于会话状态的应用、WebSocket),配置 Session Affinity:
spec:
sessionAffinity: ClientIP # 按客户端 IP 保持会话
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 默认 3 小时
3.4 Headless Service
clusterIP: None 的 Service 不分配虚拟 IP,DNS 直接返回所有后端 Pod IP。用于 StatefulSet 的稳定身份(第 6 章)、需要客户端自行负载均衡的场景。
4. 集群 DNS 解析链
集群 DNS(默认是 CoreDNS)为每个 Service 自动生成一条 DNS 记录,完整格式为:
svc.namespace.svc.cluster.local
| 段 | 含义 |
|---|---|
cluster.local | 集群私有 DNS 根域,只在集群内解析 |
svc | 服务子域 |
learning | Namespace 限定 |
api | Service 短名,只有同 Namespace 内的 Pod 能直接使用 |
Pod 的 /etc/resolv.conf 会配置以自身 Namespace 开头的搜索域,因此:
- 同 Namespace 内可直接用短名
api,或用api.learning; - 跨 Namespace 必须使用完整名称
api.learning.svc.cluster.local。
kubectl run tmp --image=busybox:1.36 --rm -it --restart=Never -- sh
# 进入容器后:
nslookup api.learning.svc.cluster.local
nslookup api.learning
5. 排障顺序
- Pod 是否
Ready,应用是否监听targetPort; - Service selector 是否选中目标 Pod,EndpointSlice 是否有地址;
- 集群内 DNS 是否能解析 Service 名(
nslookup api.learning); - Ingress / Gateway 规则、Class 和 Controller 日志是否正确;
- 最后检查外部 DNS、证书、安全组、防火墙和负载均衡配置。
kubectl run tmp --image=busybox:1.36 --rm -it --restart=Never -- \
wget -q -O- http://api.learning:80/healthz
6. 练习
练习 A
- 为之前创建的 Deployment 创建一个 ClusterIP Service,用
kubectl run tmp --image=busybox从集群内部访问它 - 修改 Service selector 使它不匹配 Pod,观察 EndpointSlice 变为空
- 修复 selector,确认 EndpointSlice 恢复
练习 B
- 创建一个 NodePort Service,从宿主机通过
<node-ip>:<node-port>访问 - 给 Service 添加
sessionAffinity: ClientIP,连续访问多次,确认请求落在同一 Pod - 用
kubectl get endpointslice -o wide观察后端列表随 Pod Ready 状态变化
下一篇:Ingress 流量入口。