Kubernetes Service 深度解析

Service 对象:Kubernetes 网络抽象的核心

Kubernetes 中的 Service 是集群内部网络抽象的核心对象。它为一组功能相同的 Pod 提供了一个稳定的、统一的访问入口(虚拟 IP 地址和 DNS 名称),屏蔽了后端 Pod 实例的动态变化(如伸缩、故障恢复、滚动更新)。本文将深入探讨 Service 的核心概念、不同类型、实现机制及高级特性。

一、核心概念

1. 稳定的访问入口

Service 提供了一个虚拟 IP(ClusterIP),该 IP 地址在 Service 的生命周期内保持不变。集群内的其他 Pod 可以通过这个 ClusterIP 或者 Service 的 DNS 名称来访问后端的 Pod,而无需关心 Pod 的具体 IP 地址和状态变化。

2. 标签选择器(Selector)

Service 通过 spec.selector 字段来定义它所代理的后端 Pod 集合。selector 使用标签查询(Label Query)来匹配 Pod 的 metadata.labels

  • 工作机制: Service Controller 持续监控集群中的 Pod,并将标签匹配且状态为 Ready 的 Pod 的 IP 地址和端口信息更新到对应的 Endpoints (或 EndpointSlice) 对象中。
  • 示例: selector: {app: nginx} 会匹配所有带有 app=nginx 标签的 Pod。
  • 动态性: Pod 的创建、删除或标签变更都会实时触发 Endpoints (或 EndpointSlice) 的更新。

3. 端口映射

Service 定义了如何将外部请求的端口映射到后端 Pod 的端口:

  • port: Service 暴露的端口,即 ClusterIP 监听的端口。
  • targetPort: 后端 Pod 容器实际监听的端口。它可以是端口号(如 80)或端口名称(如 http,需在 Pod 的 spec.containers.ports 中定义)。
  • protocol: 支持的协议,如 TCP (默认)、UDPSCTP
  • nodePort: 当 Service 类型为 NodePortLoadBalancer 时,指定在每个节点上暴露的静态端口(范围通常是 30000-32767)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 示例 Service 配置
apiVersion: v1
kind: Service
metadata:
name: nginx-service # 生成 DNS 记录 nginx-service.<namespace>.svc.cluster.local
spec:
selector:
app: nginx # 关联具有 app=nginx 标签的 Pod
ports:
- name: http # 端口名称 (可选)
protocol: TCP # 传输层协议
port: 80 # Service ClusterIP 监听的端口
targetPort: 8080 # Pod 容器监听的端口 (可以是名称或数字)
type: ClusterIP # Service 类型 (默认为 ClusterIP)

二、Service 类型

Kubernetes 提供多种 Service 类型以满足不同的访问需求:

  1. ClusterIP (默认):

    • 分配一个集群内部唯一的虚拟 IP 地址。
    • 只能在集群内部通过 ClusterIP 或 Service DNS 名称访问。
    • 是 NodePort 和 LoadBalancer 类型的基础。
  2. NodePort:

    • 在 ClusterIP 的基础上,在集群中每个节点相同静态端口nodePort)上暴露服务。
    • 可以通过 <NodeIP>:<NodePort> 从集群外部访问服务。
    • 流量路径:外部客户端 -> <NodeIP>:<NodePort> -> ClusterIP:Port -> <PodIP>:<TargetPort>
    • 通常用于开发环境或不需要高可用负载均衡器的场景。
  3. LoadBalancer:

    • 在 NodePort 的基础上,集成云服务商(如 AWS, GCP, Azure)的负载均衡器。
    • 云服务商会为该 Service 创建一个外部负载均衡器,并将其 IP 地址(External IP)分配给 Service。
    • 流量路径:外部客户端 -> <LoadBalancerIP>:<Port> -> <NodeIP>:<NodePort> -> ClusterIP:Port -> <PodIP>:<TargetPort>
    • 是向公网暴露服务的标准方式。
  4. ExternalName:

    • 不创建 ClusterIP,也不代理任何 Pod。
    • 通过返回 CNAME 记录,将服务名称映射到 spec.externalName 字段指定的外部域名。
    • 适用于集群内部服务需要访问集群外部服务的场景,提供一个内部的稳定别名。
    • 示例:externalName: my.database.example.com
  5. Headless Service:

    • 通过将 spec.clusterIP 设置为 None 来创建。
    • 不分配 ClusterIP,kube-proxy 不会处理此 Service。
    • DNS 系统会为该 Service 创建多条 A/AAAA 记录,直接指向所有匹配 selector 的 Ready 状态 Pod 的 IP 地址。
    • 常用于 StatefulSet,允许直接发现和连接到特定的 Pod 实例,或用于需要自行实现服务发现和负载均衡逻辑的场景。

三、实现机制

Service 的功能主要依赖于 kube-proxy 组件以及 Endpoints/EndpointSlice 对象。

1. kube-proxy

kube-proxy 是运行在每个节点上的网络代理,负责实现 Service 的网络规则。它监听 API Server 中 Service 和 Endpoints/EndpointSlice 对象的变化,并据此修改节点的网络规则(如 iptables 或 IPVS)。

  • iptables 模式 (常用):

    • 为每个 Service 创建 iptables 规则链(如 KUBE-SERVICES, KUBE-SVC-<hash>, KUBE-SEP-<hash>)。
    • 使用 DNAT (目标网络地址转换) 将 Service 的 ClusterIP:Port 流量重定向到后端某个 Pod 的 IP:TargetPort。
    • 使用统计概率(默认)或随机模式实现 Pod 间的负载均衡。
    • 优点:成熟稳定,兼容性好。
    • 缺点:规则数量多时性能下降,规则更新效率较低。
  • IPVS 模式 (性能更优):

    • 使用 Linux 内核的 IP Virtual Server (IPVS) 来实现负载均衡。
    • 为每个 Service 创建一个 IPVS 虚拟服务器,后端 Pod 作为真实服务器。
    • 支持多种负载均衡算法(如 rr, lc, dh, sh)。
    • 优点:性能高,适用于大规模集群,规则更新效率高。
    • 缺点:需要节点内核支持 IPVS 模块。
  • userspace 模式 (已废弃):

    • kube-proxy 自身作为用户空间代理,性能较差,不推荐使用。

2. Endpoints 与 EndpointSlice

Service 的 selector 决定了哪些 Pod 是后端实例,但 Service 本身不直接存储这些 Pod 的 IP 地址。实际的 Pod IP 和端口信息由 EndpointsEndpointSlice 对象维护。

  • Endpoints 对象:

    • endpoint-controller 自动创建和管理,与 Service 同名同命名空间。
    • 包含一个 subsets 列表,每个 subset 对应 Service 的一个端口映射,并包含 addresses (Ready Pod IP) 和 notReadyAddresses (Not Ready Pod IP) 列表。
    • 就绪状态判断: Pod 是否被加入 addresses 取决于其 readinessProbe 的状态以及 service.spec.publishNotReadyAddresses 字段(默认为 false)。
    • 缺点: 当 Service 后端 Pod 数量非常多时(成百上千),单个 Endpoints 对象会变得非常大,更新和同步成本高,影响集群性能和可扩展性。
  • EndpointSlice 对象 (Kubernetes 1.16+ 引入, 1.21+ 默认启用):

    • 为了解决 Endpoints 的扩展性问题而引入。
    • 将一个 Service 的所有端点信息分片存储到多个 EndpointSlice 对象中。
    • 每个 EndpointSlice 通常包含不超过 100 个端点(可配置)。
    • 优点:
      • 可扩展性: 显著降低单个对象的更新和传输开销,提高大规模集群性能。
      • 更快的更新: 只需更新包含变更端点的 Slice。
      • 更丰富的拓扑信息: endpoints 字段内嵌 topology 信息(如节点名、可用区),为 Topology Aware Routing 等特性提供基础。
    • 结构示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      apiVersion: discovery.k8s.io/v1
      kind: EndpointSlice
      metadata:
      name: example-svc-abc # 名称通常为 <service-name>-<hash>
      labels:
      kubernetes.io/service-name: example-svc # 关联的 Service 名称
      endpointslice.kubernetes.io/managed-by: endpointslice-controller.k8s.io
      addressType: IPv4 # 地址类型 (IPv4, IPv6, FQDN)
      ports:
      - name: http
      protocol: TCP
      port: 80
      endpoints:
      - addresses:
      - "10.1.2.3" # Pod IP
      conditions:
      ready: true # Pod 就绪状态
      serving: true # 是否应该接收流量
      terminating: false # 是否正在终止
      nodeName: node-1 # Pod 所在节点 (可选)
      zone: us-west2-a # Pod 所在可用区 (可选)
      targetRef: # 指向关联的 Pod (可选)
      kind: Pod
      namespace: default
      name: example-pod-xyz
      uid: ...
      # ... 其他端点 ...

3. 服务发现

Kubernetes 提供两种主要的服务发现机制:

  • 环境变量:

    • 当 Pod 启动时,kubelet 会为每个已存在的 Service 注入一组环境变量。
    • 格式:<SERVICE_NAME>_SERVICE_HOST<SERVICE_NAME>_SERVICE_PORT (以及其他协议/端口相关变量)。
    • 缺点:Pod 只能发现其创建之前就存在的 Service;环境变量不会自动更新。
  • DNS (推荐):

    • 集群 DNS 服务(如 CoreDNS)监听 Service 和 Endpoints/EndpointSlice 对象。
    • 为每个 Service 创建 DNS 记录:
      • A/AAAA 记录: my-svc.my-namespace.svc.cluster.local 解析到 Service 的 ClusterIP。
      • SRV 记录: _http._tcp.my-svc.my-namespace.svc.cluster.local 解析到 Service 的端口和域名。
      • Headless Service: A/AAAA 记录直接解析到后端所有 Ready Pod 的 IP 地址。
    • 优点:标准、灵活,是 Kubernetes 中服务发现的首选方式。

四、高级特性

1. externalTrafficPolicy

此字段用于 NodePortLoadBalancer 类型的 Service,控制外部流量如何路由到 Pod。

  • Cluster (默认):

    • 流量到达节点后,可以被转发到集群中任何节点上的后端 Pod。
    • 优点:负载更均衡。
    • 缺点:
      • 可能发生额外的网络跳数。
      • 后端 Pod 看到的源 IP 地址是节点 IP(发生了 SNAT),而不是真实的客户端 IP(除非云提供商的 LoadBalancer 支持代理协议)。
  • Local:

    • 流量只会被转发到当前节点上的后端 Pod。如果当前节点没有健康的后端 Pod,则流量会被丢弃。
    • 优点:
      • 保留客户端源 IP 地址
      • 避免了额外的网络跳数。
    • 缺点:可能导致节点间的负载不均衡。

2. Service Topology (已废弃,被 Topology Aware Hints 取代)

旨在将流量优先路由到与客户端来源拓扑(如同一可用区、同一区域)更近的端点,以降低延迟和成本。

  • 机制: 在 Service spec 中定义 topologyKeys 列表,按优先级指定拓扑域(如 topology.kubernetes.io/zone, topology.kubernetes.io/region)。kube-proxy (或 EndpointSlice Controller) 会根据节点的拓扑标签和 topologyKeys 过滤 Endpoints/EndpointSlice,优先选择拓扑匹配的端点。
  • 配置示例:
    1
    2
    3
    4
    5
    6
    7
    8
    apiVersion: v1
    kind: Service
    # ... metadata, spec.selector, spec.ports ...
    spec:
    topologyKeys:
    - "topology.kubernetes.io/zone"
    - "topology.kubernetes.io/region"
    - "*" # 回退到任意可用端点
  • 局限性: 实现较为复杂,配置不够灵活,已被更优化的方案取代。

3. Topology Aware Hints (Kubernetes 1.21+ Beta, 1.27+ GA)

作为 Service Topology 的继任者,提供了一种更自动化、更精细的拓扑感知路由优化。

  • 机制:
    • endpoint-slice-controller 自动计算并向 EndpointSlice 对象添加“提示”(Hints)。
    • kube-proxy 利用这些提示,按比例将流量导向拓扑上更优的端点子集,而不是像 Service Topology 那样进行严格过滤。
    • 目标是尽量将流量保持在同一拓扑域内,同时避免因端点分布不均导致某些区域过载或无端点可用。
  • 启用: 通过在 Service 上添加注解 service.kubernetes.io/topology-aware-hints: "Auto" 来启用(需要 Feature Gate 开启)。
  • 对比:
维度 Service Topology (旧) Topology Aware Hints (新)
启用方式 spec.topologyKeys metadata.annotations
决策者 用户显式定义优先级 控制器自动计算提示
路由行为 严格过滤,全有或全无 按比例引导流量
灵活性 较低 较高,自动适应端点分布
回退机制 显式 * 或逐级回退 控制器内置比例和回退逻辑
管理复杂度 较高 较低 (Auto 模式)
当前状态 已废弃 GA (推荐)

五、总结

Service 是 Kubernetes 网络模型中不可或缺的一部分,它提供了:

  • 服务抽象: 解耦了服务消费者和提供者。
  • 负载均衡: 在多个 Pod 实例间分发流量。
  • 服务发现: 通过 DNS 或环境变量定位服务。

理解 Service 的不同类型、实现机制(kube-proxy, Endpoints/EndpointSlice)以及高级特性(externalTrafficPolicy, Topology Aware Hints)对于在 Kubernetes 中构建健壮、可扩展和高性能的应用至关重要。随着 EndpointSlice 和 Topology Aware Hints 的成熟,Kubernetes 在大规模集群中的服务路由能力得到了显著提升。


Kubernetes Service 深度解析
https://mfzzf.github.io/2025/04/03/kubernetes-Service对象/
作者
Mzzf
发布于
2025年4月3日
许可协议