Service 详解:服务发现与负载均衡机制

摘要:Pod 的 IP 是短暂的,随 Pod 重建、扩缩容而变化。Service 提供稳定的虚拟 IP 和 DNS 名称,实现服务发现与负载均衡,是 Kubernetes 网络模型的核心组件。

一、为什么需要 Service?

Pod IP 会随 Pod 重建、扩缩容而变化,客户端若直接使用 Pod IP 将面临连接失效。Service 提供稳定的虚拟 IP(ClusterIP)和 DNS 名称,将流量负载均衡到后端 Pod,实现服务发现与流量转发。
Service 方案
Service ClusterIP: 10.96.0.100
Pod 1
Pod 2
Pod 3
问题:Pod IP 不固定
IP 变化
T1: Pod A 10.244.1.5
T2: Pod B 重建后 10.244.3.7

Service 的 ClusterIP 在生命周期内保持不变,DNS 名称稳定。后端 Pod 变更时由 Endpoints Controller 与 kube-proxy 自动更新转发规则。

二、Service 的工作原理

2.1 Label Selector(标签选择器)

Service 通过标签选择器(Label Selector)匹配后端 Pod,只有 metadata.labels 满足 spec.selector 的 Pod 才会被加入 Endpoints。
不匹配
Service selector: app=nginx
Pod nginx-1 app=nginx
Pod nginx-2 app=nginx
Pod redis-1 app=redis

selector 与 Pod 的 metadata.labels 匹配时,该 Pod 被纳入 Service 的后端;不匹配的 Pod 不参与负载均衡。

2.2 Endpoints 与 EndpointSlice

Service 会对应一个 Endpoints 对象,记录所有匹配 Pod 的 IP 和端口,由 Endpoints Controller 自动维护。Kubernetes 1.17+ 引入 EndpointSlice,将 Endpoints 拆分为多个 Slice,每个 Slice 最多 100 个端点,便于大规模集群扩展。
Service nginx-svc
Endpoints
10.244.1.5:80
10.244.2.3:80
10.244.3.7:80

EndpointSlice 由 kube-controller-manager 的 EndpointSlice Controller 创建,当 Endpoints 超过 100 个端点时自动拆分。kube-proxy 监听 Endpoints/EndpointSlice 变化并更新转发规则。

对象 说明
Endpoints 传统对象,存储所有匹配 Pod 的 IP:Port 列表
EndpointSlice 新对象,将端点分片存储,单 Slice 最多 100 个端点,支持大规模集群

2.3 kube-proxy 工作流程

kube-proxy 运行在每个节点上,监听 Service 和 Endpoints 变化,在本地配置流量转发规则。支持三种模式:userspace(已废弃)、iptables、IPVS。
Watch Service/Endpoints
更新规则
请求 ClusterIP:port
DNAT 转发
API Server
kube-proxy
iptables / IPVS
Client Pod
Pod 1
Pod 2
Pod 3

kube-proxy 通过 Watch API 监听 Service 和 Endpoints 对象,当变更发生时在节点上更新 iptables 或 IPVS 规则,将发往 ClusterIP 的流量 DNAT 到后端 Pod。

2.3.1 iptables 模式

kube-proxy 在 iptables 的 NAT 表中创建链:KUBE-SERVICES(入口)、KUBE-SVC-xxx(按 Service 分)、KUBE-SEP-xxx(按端点分)。流量命中 ClusterIP 后,经 KUBE-SVC-xxx 随机选择一条 KUBE-SEP-xxx 规则完成 DNAT。规则数量与 Service 数、端点数成正比,超大规模集群下规则膨胀可能影响性能。

2.3.2 IPVS 模式

IPVS 基于内核 Netfilter,支持多种负载均衡算法(rr、lc、dh、sh、sed、nq)。kube-proxy 创建虚拟 Service(ClusterIP)并绑定后端 Real Server(Pod IP),转发在内核完成,性能优于 iptables。支持 sessionAffinity: ClientIP 实现会话保持。启用方式:kube-proxy --proxy-mode=ipvs

模式 实现方式 负载均衡算法 会话保持 适用场景
iptables NAT 表规则 随机 需 sessionAffinity 中小规模
IPVS 内核 IPVS 模块 rr/lc/dh/sh 等 原生支持 大规模、高并发

三、Service 的四种类型

不同类型的 Service 提供不同的访问范围和实现方式。
Service 类型
ClusterIP 集群内部
NodePort 节点端口
LoadBalancer 云负载均衡
ExternalName CNAME

类型 访问范围 说明
ClusterIP 集群内部 默认类型,分配虚拟 IP,最常用
NodePort 集群外可通过节点 IP:端口 在每个节点开放 30000--32767 端口
LoadBalancer 外部 依赖云厂商,创建云 LB,包含 NodePort 与 ClusterIP
ExternalName 集群内部 不创建代理,通过 CNAME 指向外部域名

3.1 ClusterIP

ClusterIP 是默认类型,分配一个集群内可路由的虚拟 IP,仅集群内 Pod 可访问。

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: nginx
  ports:
  - name: http
    port: 80
    targetPort: 8080
    protocol: TCP

ClusterIP 字段说明

字段 类型 说明
spec.type string ClusterIP(默认,可省略)
spec.selector map 与 Pod 的 labels 匹配,为空则需手动创建 Endpoints
spec.ports[].port int32 Service 暴露的端口
spec.ports[].targetPort int/string Pod 容器端口,可为数字或 named port
spec.ports[].protocol string TCP(默认)或 UDP
spec.clusterIP string 可指定固定 IP,省略则自动分配
bash 复制代码
# 创建 Deployment 与 Service
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.25
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
EOF

# 验证
kubectl get svc nginx-svc
kubectl get endpoints nginx-svc
kubectl run test --rm -it --image=busybox -- wget -qO- http://nginx-svc

3.2 NodePort

NodePort 在每个节点上开放 30000--32767 范围内的端口,外部可通过任意节点 IP:NodePort 访问。

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080

NodePort 字段说明

字段 类型 说明
spec.type string NodePort
spec.ports[].nodePort int32 30000--32767,省略则自动分配
spec.externalTrafficPolicy string Cluster(默认)或 Local,Local 仅转发到本节点 Pod
bash 复制代码
kubectl apply -f service-nodeport.yaml
kubectl get svc nginx-nodeport
# 外部访问:curl http://<任意节点IP>:30080

3.3 LoadBalancer

在云环境中,type 为 LoadBalancer 时,云控制器会创建负载均衡器并分配外部 IP。

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: nginx-lb
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80

LoadBalancer 字段说明

字段 类型 说明
spec.type string LoadBalancer
spec.loadBalancerIP string 部分云厂商支持指定 IP
spec.loadBalancerSourceRanges []string 限制 LB 访问 IP 段
status.loadBalancer.ingress array 云厂商分配的 External IP

外部用户
云 LB
Node 1:30080
Node 2:30080
Node 3:30080
Service:80
Pod 1/2/3

流量路径:云 LB → NodePort → Service → Pod。裸金属环境需配合 MetalLB 等实现 LoadBalancer。

3.4 ExternalName

ExternalName 不创建 ClusterIP,仅创建一条 CNAME DNS 记录,指向集群外域名。适用于将集群内服务名映射到外部服务。

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: mysql.example.com

ExternalName 字段说明

字段 类型 说明
spec.type string ExternalName
spec.externalName string 外部域名,DNS 解析为 CNAME
spec.selector - 不可用,无 Endpoints
spec.ports - 不可用,无端口映射

使用场景 :迁移阶段应用配置使用 mysql-svc,将 ExternalName 指向 mysql.legacy.example.com;多集群场景下用 Service 名统一访问外部服务。

bash 复制代码
# 创建后,集群内解析 external-db 得到 mysql.example.com
kubectl run test --rm -it --image=busybox -- nslookup external-db

四、DNS 服务发现

Kubernetes 内置 CoreDNS,为每个 Service 自动创建 DNS 记录。
DNS 查询
CoreDNS
..svc.cluster.local
ClusterIP

DNS 解析规则

格式 示例 说明
<service> nginx-svc 同命名空间可简写
<service>.<namespace> nginx-svc.production 跨命名空间
<service>.<namespace>.svc nginx-svc.production.svc 含 svc 子域
FQDN nginx-svc.production.svc.cluster.local 完整域名

CoreDNS 的 Kubernetes 插件监听 Service 和 Pod 资源,按上述规则生成 DNS 记录。Pod 的 dnsPolicy: ClusterFirst 时,集群 DNS 为 kube-dns Service 的 ClusterIP(通常 10.96.0.10)。

bash 复制代码
# 同命名空间
curl http://nginx-svc:80

# 跨命名空间
curl http://nginx-svc.production:80

# 完整域名
curl http://nginx-svc.default.svc.cluster.local

五、Headless Service

当不需要负载均衡、需直接访问每个 Pod 时,使用 Headless Service(clusterIP: None)。
Headless Service
DNS 查询
Pod IP 列表
普通 Service
DNS 查询
ClusterIP

普通 Service 的 DNS 解析为单个 ClusterIP;Headless Service 返回所有匹配 Pod 的 IP 列表(A 记录),由客户端选择目标。StatefulSet 常配合 Headless Service 使用,Pod 具有稳定的 DNS 名 pod-name.service-name.namespace.svc.cluster.local

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: nginx-headless
spec:
  clusterIP: None
  selector:
    app: nginx
  ports:
  - port: 80
bash 复制代码
# 解析 Headless Service 会返回多个 A 记录
kubectl run test --rm -it --image=busybox -- nslookup nginx-headless

六、多端口 Service

Pod 暴露多个端口时,Service 可配置多个端口映射,每个端口需指定 name

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: my-app
spec:
  selector:
    app: my-app
  ports:
  - name: http
    port: 80
    targetPort: 8080
  - name: https
    port: 443
    targetPort: 8443
  - name: metrics
    port: 9090
    targetPort: 9090
字段 说明
ports[].name 必填,多端口时用于区分
ports[].port Service 暴露端口
ports[].targetPort Pod 端口,可引用容器 named port

七、流量转发流程

在 iptables 模式下,客户端访问 Service 的完整路径如下:
请求 nginx-svc:80
ClusterIP
随机选择
Client Pod
DNS 解析
iptables DNAT
Pod 1
Pod 2
Pod 3

客户端通过 DNS 解析得到 ClusterIP,请求到达节点后由 kube-proxy 配置的 iptables 规则进行 DNAT,将流量转发到其中一个后端 Pod。

八、生产环境建议

建议 说明
集群内访问优先 ClusterIP 避免通过 NodePort 在集群内访问,减少一跳
生产外部访问用 LoadBalancer 或 Ingress NodePort 适合开发测试,生产建议云 LB 或 Ingress
设置 sessionAffinity 时考虑 IPVS iptables 模式对 sessionAffinity 支持有限,IPVS 更佳
监控 Endpoints 与 Service 健康 Ready 的 Pod 才加入 Endpoints,关注 readiness 探针
大规模集群启用 EndpointSlice 降低 Endpoints 对象体积,提升 Watch 效率

九、常用命令

bash 复制代码
# 创建 Service
kubectl apply -f service.yaml

# 查看 Service
kubectl get svc
kubectl get services -o wide

# 查看详情
kubectl describe svc nginx-svc

# 查看 Endpoints
kubectl get endpoints nginx-svc

# 查看 EndpointSlice(若启用)
kubectl get endpointslices

# 为 Deployment 快速创建 Service
kubectl expose deployment nginx --port=80 --target-port=8080

# 测试连通性(集群内 Pod)
kubectl run test --rm -it --image=busybox -- wget -qO- http://nginx-svc

十、常见问题(FAQ)

Q1:Service 无法访问的可能原因?

检查顺序:1)selector 是否与 Pod 标签匹配;2)Pod 是否 Ready(readiness 探针);3)Endpoints 是否有条目(kubectl get endpoints);4)NetworkPolicy 是否拦截;5)同一命名空间内优先使用 Service 名称访问;6)targetPort 是否与容器端口一致。

Q2:ClusterIP 和 NodePort 如何选择?

仅集群内访问用 ClusterIP;需要从集群外访问时,开发测试可用 NodePort,生产环境在云上优先使用 LoadBalancer 或 Ingress。

Q3:如何实现会话保持?

配置 sessionAffinity: ClientIP,并设置 sessionAffinityConfig.clientIP.timeoutSeconds(默认 10800)。iptables 模式支持有限,建议改用 kube-proxy 的 IPVS 模式;或使用支持会话保持的 Ingress Controller。

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: nginx-session
spec:
  type: ClusterIP
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 3600
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80

Q4:没有 selector 的 Service 如何使用?

创建不指定 selector 的 Service,手动创建同名 Endpoints,将外部服务 IP 加入。适用于将集群内服务名映射到集群外 IP。

yaml 复制代码
# Service 无 selector
apiVersion: v1
kind: Service
metadata:
  name: external-mysql
spec:
  ports:
  - port: 3306
---
# 手动创建 Endpoints 指向外部 IP
apiVersion: v1
kind: Endpoints
metadata:
  name: external-mysql
subsets:
- addresses:
  - ip: 192.168.1.100
  ports:
  - port: 3306

集群内访问 external-mysql:3306 即转发到 192.168.1.100:3306。

十一、总结

要点 说明
核心作用 解决 Pod IP 不稳定、多副本负载均衡和服务发现问题
类型选择 ClusterIP(集群内)、NodePort(简单外部访问)、LoadBalancer(生产云环境)、ExternalName(访问集群外服务)
转发机制 Label Selector → Endpoints/EndpointSlice → kube-proxy(iptables/IPVS)
DNS CoreDNS 提供 <svc>.<ns>.svc.cluster.local 解析
特殊类型 Headless Service(直接解析 Pod IP)、ExternalName(CNAME 到外部)
相关推荐
Mr.小海3 小时前
Docker+K8s 集成部署实战
docker·容器·kubernetes
叱咤少帅(少帅)3 小时前
基于ELK 收集K8S的日志
elk·容器·kubernetes
没有bug.的程序员3 小时前
云原生 CI/CD 深度实战:GitLab CI 与 Jenkins 协同内核、Pipeline 自动化精髓与容器化交付指南
ci/cd·云原生·pipeline·gitlab·jekins
猫头虎3 小时前
猫头虎AI分享:[转载]2025 年 HAMi 社区年度回顾 | 从 GPU 调度器到云原生 AI 基础设施的中流砥柱
运维·人工智能·云原生·开源·gateway·github·ai编程
灰子学技术3 小时前
Envoy与Istio HTTP流量故障转移机制介绍
网络·网络协议·http·云原生·istio
学Linux的语莫12 小时前
k8s常用命令
linux·容器·kubernetes
帷幄庸者16 小时前
跨网的Kubernetes集群:从零构建混合云架构
容器·架构·kubernetes
bepeater123416 小时前
使用Kubernetes部署Spring Boot项目
spring boot·容器·kubernetes
终生成长者18 小时前
Kubernetes常用操作与概念总结--从服务器导出mongo数据,并下载到本地
服务器·容器·kubernetes