Skip to content

Kubernetes-Service

Service 是 Kubernetes 的核心抽象之一,用于为一组 Pod 提供稳定的网络端点(IP + 端口),实现服务发现和负载均衡。

Service 的核心概念与作用

Service 是 Kubernetes 中用于解决 Pod 动态性问题的关键抽象资源。在 Kubernetes 集群中,每个 Pod 都有自己的 IP 地址,但这些 IP 地址会随着 Pod 的重启、扩缩容或重新调度而发生变化。Service 通过提供稳定的虚拟 IP 地址(VIP)和 DNS 名称,为客户端提供了不随 Pod 变化而变化的访问入口。

Service 的三大核心功能:

  1. 服务发现:通过 DNS 名称或固定 IP 地址,为客户端提供稳定的服务访问端点,无需关心后端 Pod 的具体 IP 地址。Service 的 DNS 名称遵循<service-name>.<namespace>.svc.cluster.local的格式。
  2. 负载均衡:自动将客户端请求分发到后端多个 Pod 实例上,默认采用轮询(Round Robin)算法,避免单点压力。Service 通过 kube-proxy 组件实现流量分发,支持 iptables 或 ipvs 两种模式。
  3. 网络抽象:为不同环境(开发、测试、生产)提供一致的访问方式,无论是集群内部还是外部访问,都可以通过统一的接口实现。

Service 通过**标签选择器(Label Selector)**与 Pod 关联,当 Pod 的标签与 Service 的 selector 匹配时,这些 Pod 就会被纳入 Service 的负载均衡池中。Endpoint 资源会实时记录这些 Pod 的 IP 地址和端口,确保流量能够正确路由到健康的 Pod 实例。

Service 的底层工作原理

Service 的实现依赖于 Kubernetes 核心组件的协作,主要包括 kube-proxy、CoreDNS 和 Endpoint Controller。

核心组件协作

Endpoint Controller

  • 监控 Service 和 Pod 的变化,维护 Endpoints 对象
  • 当 Service 的 selector 匹配的 Pod 发生变化时,实时更新 Endpoints 记录
  • 确保只有健康的 Pod 被纳入 Endpoints(通过 Pod 的 readinessProbe 检查)

kube-proxy

  • 运行在每个节点上的网络代理组件
  • 监听 Service 和 Endpoints 的变化,更新节点上的网络规则
  • 支持两种主要模式:iptables 和 ipvs

CoreDNS

  • 为 Service 提供集群内部的 DNS 解析服务
  • 实现 Service 名称到 ClusterIP 的映射

流量转发机制

在 iptables 模式下,kube-proxy 会为每个 Service 创建复杂的 iptables 规则链:

  1. 当客户端请求到达 Service 的 ClusterIP 时,首先匹配 KUBE-SERVICES
  2. 然后跳转到特定 Service 的链(如 KUBE-SVC-XXXXXX
  3. 最后通过 DNAT 规则将请求转发到具体的 Pod IP(记录在 Endpoints 中)

示例 iptables 规则

bash
Chain KUBE-SVC-KEAUNL7HVWWSEZA6 (1 references)
pkts bytes target     prot opt in out source destination
   0     0 KUBE-SEP-SKMF2UJJQ24AYOPG  all -- * * 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.33333333349
   0     0 KUBE-SEP-BAKPXLXOJZJDGFKA  all -- * * 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.50000000000
   0     0 KUBE-SEP-M4RM3QHTJOBNSPNE  all -- * * 0.0.0.0/0 0.0.0.0/0

对于生产环境,ipvs 模式比 iptables 更具扩展性和性能优势,特别是当 Service 数量庞大时。

服务发现机制

Kubernetes 提供了两种主要的服务发现方式:

  1. 环境变量注入
    • kubelet 会在 Pod 创建时,将集群中所有 Service 的信息作为环境变量注入到 Pod 中
    • 变量命名格式如 MY_SERVICE_SERVICE_HOSTMY_SERVICE_SERVICE_PORT
  2. DNS 解析
    • CoreDNS 为每个 Service 创建 DNS 记录
    • 集群内 Pod 可以通过 <service-name>.<namespace>.svc.cluster.local 解析到 Service 的 ClusterIP
    • 同一命名空间下可以省略后缀,直接使用 <service-name>

Service 绑定

绑定基本原理

Kubernetes Service 通过**标签选择器(Label Selector)**与 Pod 建立绑定关系。当创建 Service 时,Kubernetes 会持续监控集群中所有 Pod 的标签,并将匹配 selector 的 Pod 纳入 Service 的后端端点(Endpoints)。

这种绑定机制解决了 Pod 动态性带来的问题:

  • Pod IP 地址会随着重启、扩缩容或重新调度而变化
  • 直接依赖 Pod IP 会导致前端应用频繁修改配置
  • 手动维护服务发现信息在分布式系统中不可行

绑定组件协作

Service 绑定涉及多个 Kubernetes 核心组件的协作:

  1. Endpoint Controller:监控 Service 和 Pod 的变化,维护 Endpoints 对象
  2. kube-proxy:运行在每个节点上,根据 Endpoints 更新本地转发规则
  3. CoreDNS:为 Service 提供 DNS 名称解析服务

标签选择器机制

Service 通过 .spec.selector 字段定义标签选择器

yaml
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
    tier: frontend
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

这个 Service 会选择所有具有 app=my-apptier=frontend 标签的 Pod 作为后端

Endpoints 动态更新

Kubernetes 会自动创建与 Service 同名的 Endpoints 对象,记录所有匹配 Pod 的 IP 和端口。当 Pod 发生变化时,Endpoints 会实时更新

bash
# 查看ep
kubectl get endpoints <service-name>

流量转发机制

kube-proxy 负责实现 Service 的流量转发,支持三种模式:

  1. iptables 模式(默认):通过大量 NAT 规则进行流量转发
  2. IPVS 模式:使用更高效的哈希表存储转发规则,适合大规模集群
  3. userspace 模式(已弃用):流量在用户空间和内核空间来回切换,性能较差

Service 的类型

Kubernetes 提供了四种主要的 Service 类型,每种类型适用于不同的访问需求和环境场景。

Service ClusterIP类型

核心功能

  1. 集群内部访问:ClusterIP 提供一个集群内部的虚拟 IP(VIP),只能在 Kubernetes 集群内部访问,外部网络无法直接访问。

  2. 服务发现:通过 Kubernetes 的 DNS 服务,其他 Pod 可以通过 Service 名称访问 ClusterIP。

  3. 负载均衡:ClusterIP 会自动将流量分发到后端的多个 Pod,实现负载均衡。

  4. 稳定的 IP 和端口:无论后端 Pod 如何变化,ClusterIP 的 IP 和端口保持不变,确保服务调用的稳定性。

使用场景

  1. 微服务间通信

    • 在微服务架构中,服务之间通过 ClusterIP 进行内部通信。
    • 例如,前端服务通过 ClusterIP 访问后端 API 服务。
  2. 数据库访问

    • 数据库服务(如 MySQL、PostgreSQL)通常只在集群内部暴露,使用 ClusterIP 可以确保外部无法直接访问。
  3. 内部工具和服务

    • 例如监控系统(Prometheus)、日志系统(Elasticsearch)等,通常只在集群内部使用。
  4. 服务发现和负载均衡

    • ClusterIP 提供稳定的访问端点,并自动实现负载均衡,适合需要高可用的服务。

ClusterIP 的工作原理

  1. Service 创建

    • 当创建一个 ClusterIP Service 时,Kubernetes 会为其分配一个虚拟 IP(ClusterIP),这个 IP 来自集群的 Service CIDR 范围(例如 10.96.0.0/12)。
    • Service 通过 selector 字段选择一组 Pod 作为后端。
  2. Endpoints 创建

    • Kubernetes 会自动创建与 Service 同名的 Endpoints 对象,其中包含所有匹配 selector 的 Pod 的 IP 和端口。
    • 如果 Pod 发生变化(例如扩容或重启),Endpoints 会自动更新。
  3. kube-proxy 的作用

    • 每个节点上的 kube-proxy 组件会监听 Service 和 Endpoints 的变化。
    • kube-proxy 通过以下方式实现流量转发:
      • iptables 模式:在节点上配置 iptables 规则,将流量从 ClusterIP 转发到后端 Pod。
      • IPVS 模式:使用更高效的 IPVS 实现流量转发。
    • 无论使用哪种模式,kube-proxy 都会确保流量被正确负载均衡到后端 Pod。
  4. DNS 解析

    • Kubernetes 的 CoreDNS 或 kube-dns 会为每个 Service 创建一条 DNS 记录。
    • 例如,Service 名称为 my-service,命名空间为 default,则其 DNS 名称为 my-service.default.svc.cluster.local。
    • 集群内的 Pod 可以通过 DNS 名称访问 Service,DNS 会解析为 ClusterIP。

优缺点

优点

  • 简单易用,是 Kubernetes 默认的 Service 类型。
  • 提供稳定的 IP 和 DNS 名称,适合服务发现。
  • 自动负载均衡,支持后端 Pod 的动态变化。

缺点

  • 只能在集群内部访问,无法直接从外部访问。
  • 对于需要对外暴露的服务,需要结合 NodePort 或 LoadBalancer 使用。

ClusterIP 绑定

ClusterIP 是默认的 Service 类型,绑定机制特点:

  • 仅在集群内部可访问
  • 分配一个虚拟 IP(VIP)作为稳定访问入口
  • 通过内部 DNS 提供服务发现(格式:<service-name>.<namespace>.svc.cluster.local

YAML 示例

yaml
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80       # Service 暴露的端口
      targetPort: 8080  # Pod 内部应用端口
  • selector:用于选择后端 Pod,只有匹配标签的 Pod 才会被纳入 Service 的后端。
  • ports
    • port:Service 暴露的端口,集群内的其他服务通过该端口访问 Service。
    • targetPort:后端 Pod 实际监听的端口。
    • protocol:支持的协议类型,默认为 TCP。

Service NodePort 类型

NodePort 是 Kubernetes 中一种重要的服务类型,它通过在集群的每个节点上开放一个静态端口,将应用程序暴露到集群外部,使得外部用户可以通过节点的 IP 地址和分配的端口访问集群内部的服务。本文将全面解析 NodePort 的工作原理、配置方法、适用场景以及最佳实践。

NodePort 的核心概念与特性

基本定义

NodePort 是 Kubernetes Service 的一种类型,它在 ClusterIP 基础上扩展了外部访问能力。创建 NodePort 服务时,Kubernetes 会:

  • 分配一个集群内部可访问的 ClusterIP(与 ClusterIP 类型服务相同)
  • 在每个节点上开放一个静态端口(默认范围 30000-32767)
  • 将发送到该端口的流量路由到对应的 Service,再由 Service 转发到后端 Pod

核心特性

NodePort 服务具有以下关键特性:

  • 端口范围:默认使用 30000-32767 范围内的端口,可通过 kube-apiserver 的 --service-node-port-range 参数调整
  • 双重访问:既可通过 <节点IP>:<NodePort> 从外部访问,也可通过 <ClusterIP>:<端口> 在集群内部访问
  • 负载均衡:自动将流量均衡分发到后端多个 Pod,支持轮询(Round Robin)等算法
  • 节点无关性:可通过集群中任意节点的 IP 和 NodePort 访问服务,不依赖特定节点

与其它服务类型的对比

特性ClusterIPNodePortLoadBalancerIngress
外部访问不支持支持支持支持
内部访问支持支持支持支持
端口范围无限制30000-32767无限制80/443
适用场景集群内部通信开发测试/临时访问生产环境高级流量管理
依赖云提供商可选

表:NodePort 与其他服务类型的对比

NodePort 的工作原理与架构

工作流程

NodePort 服务的工作流程可分为以下几个步骤:

  1. 服务创建:当创建 NodePort 服务时,Kubernetes 控制平面会:

    • 分配一个 ClusterIP 作为内部访问地址
    • 分配/指定一个 NodePort(30000-32767)
    • 创建与 Service 同名的 Endpoints 对象,记录匹配 selector 的 Pod IP 和端口
  2. 规则同步:kube-proxy 组件监听到 Service 变化后,会在各节点上:

    • iptables 模式:配置 NAT 规则,将 NodePort 流量转发到后端 Pod
    • IPVS 模式:设置 IPVS 规则,实现更高效的流量转发
  3. 流量转发

    mermaid
    graph TD
        A[外部流量] --> B[节点IP:NodePort]
        B --> C[ClusterIP:端口]
        C --> D[目标Pod]

    NodePort 流量转发流程图

  4. 外部访问:用户通过任意节点的 <IP>:<NodePort> 访问服务,请求会被:

    • 节点上的 kube-proxy 拦截
    • 根据规则转发到 Service 的 ClusterIP
    • 最终路由到后端健康的 Pod

核心组件协作

NodePort 的实现依赖于多个 Kubernetes 核心组件的协作:

  1. Endpoint Controller

    • 监控 Service 和 Pod 的变化
    • 维护 Endpoints 对象,确保只包含健康 Pod 的地址(通过 readinessProbe 检查)
  2. kube-proxy

    • 运行在每个节点上的网络代理
    • 监听 Service 和 Endpoints 变化
    • 更新节点上的 iptables/IPVS 规则
  3. CoreDNS

    • 为 Service 提供集群内部 DNS 解析
    • 解析格式:<service-name>.<namespace>.svc.cluster.local

NodePort 的配置与实践

YAML 配置详解

以下是一个完整的 NodePort 服务配置示例:

yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nodeport-service
  namespace: default
spec:
  type: NodePort  # 服务类型
  selector:
    app: my-app   # 匹配Pod的标签
    tier: frontend # 可指定多个标签提高精确度
  ports:
    - name: http  # 端口名称(多端口时必须)
      protocol: TCP  # 协议类型(TCP/UDP/SCTP)
      port: 80    # Service的ClusterIP端口
      targetPort: 8080  # Pod内容器端口
      nodePort: 30080  # 手动指定的NodePort(可选)

关键字段说明

  • selector:用于选择后端 Pod,必须与 Pod 的 labels 匹配
  • ports
    • port:Service 暴露的端口,集群内部通过 <ClusterIP>:<port> 访问
    • targetPort:Pod 内容器实际监听的端口
    • nodePort:手动指定的节点端口(30000-32767),不指定则自动分配

创建与验证

  1. 创建服务

    bash
    kubectl apply -f nodeport-service.yaml
  2. 验证服务

    bash
    kubectl get svc my-nodeport-service

    输出示例:

    NAME                 TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
    my-nodeport-service NodePort   10.96.123.45   <none>        80:30080/TCP   5m

    其中 80:30080/TCP 表示 ClusterIP 端口 80 映射到 NodePort 30080

  3. 查看 Endpoints

    bash
    kubectl get endpoints my-nodeport-service

    确认列出的 Pod IP 和端口与预期一致

  4. 访问测试

    • 集群内部:
      bash
      curl http://<ClusterIP>:80
    • 集群外部:
      bash
      curl http://<任意节点IP>:30080

修改现有服务为 NodePort

可以将现有的 ClusterIP 服务改为 NodePort 类型:

  1. 编辑现有服务

    bash
    kubectl edit svc my-existing-service
  2. 修改类型并指定端口

    yaml
    spec:
      type: NodePort
      ports:
      - port: 80
        targetPort: 8080
        nodePort: 30081  # 新增此字段
  3. 应用更改

    bash
    kubectl apply -f modified-service.yaml

注意:直接编辑时更改会立即生效,无需apply

NodePort 的高级特性

会话保持(Session Affinity)

通过配置 sessionAffinity 实现基于客户端 IP 的会话保持:

yaml
spec:
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 3600  # 会话保持时间

此配置确保同一客户端的请求总是转发到同一个后端 Pod

外部流量策略

通过 externalTrafficPolicy 控制外部流量的路由行为:

  1. Cluster(默认):

    • 流量可以在集群内跨节点转发
    • 保留客户端源 IP 需要额外配置
    • 可能导致跨节点额外跳转
  2. Local

    • 流量只转发到接收节点上的 Pod
    • 保留客户端真实 IP
    • 可能导致负载不均

配置示例:

yaml
spec:
  externalTrafficPolicy: Local

多端口暴露

单个 NodePort 服务可以暴露多个端口:

yaml
spec:
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080
    nodePort: 30080
  - name: metrics
    protocol: TCP
    port: 9090
    targetPort: 9090
    nodePort: 30090

注意:每个端口需要唯一的 nodePort

Service LoadBalancer 类型

特点

  • 在 NodePort 基础上,集成云提供商的负载均衡器(如AWS ELB、GCP LB等)
  • 自动分配外部可访问的 IP 地址,并配置负载均衡规则
  • 通常会产生云服务商的负载均衡器费用

适用场景

  • 生产环境中需要对外暴露的服务(如Web API、前端应用)
  • 需要云平台提供的自动扩缩、健康检查等高级负载均衡功能
  • 多可用区部署的高可用服务

YAML 示例

yaml
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

Service ExternalName 类型

特点

  • 将 Service 映射到外部 DNS 名称,而不是集群内部的 Pod
  • 不创建任何端口或代理规则,仅作为 DNS CNAME 记录
  • 集群内部访问 Service 名称时会被解析为外部域名

适用场景

  • 集群内访问外部服务(如云数据库、第三方API)
  • 需要将外部服务抽象为集群内部服务名称的场景
  • 迁移过程中逐步将外部服务引入集群

YAML 示例

yaml
apiVersion: v1
kind: Service
metadata:
  name: external-mysql
spec:
  type: ExternalName
  externalName: mysql.example.com

Kubernetes 无选择器 Service 与自定义 Endpoints 实现负载均衡

在 Kubernetes 中,确实可以通过创建不设定 selector 的 Service,然后手动创建对应的 Endpoints 对象来实现自定义的负载均衡机制。这种方法特别适用于需要将流量路由到集群外部服务或需要精细控制后端端点的情况。

无选择器 Service 的工作原理

核心概念

  • 无选择器 Service:Service 对象中不定义 .spec.selector 字段
  • 手动创建 Endpoints:用户自行创建与 Service 同名的 Endpoints 对象,明确指定后端端点
  • 负载均衡机制:kube-proxy 仍然会为这种 Service 创建负载均衡规则,但后端由用户完全控制

工作流程

mermaid
graph TD
    A[创建无selector的Service] --> B[创建同名Endpoints]
    B --> C[kube-proxy监控Endpoints变化]
    C --> D[根据Endpoints配置负载均衡规则]

具体实现步骤

创建无选择器的 Service

yaml
apiVersion: v1
kind: Service
metadata:
  name: external-service  # 重要:Endpoints必须与此同名
spec:
  ports:
  - protocol: TCP
    port: 80             # Service暴露的端口
    targetPort: 8080     # 后端服务实际端口
  # 注意:这里没有selector字段

创建对应的 Endpoints

yaml
apiVersion: v1
kind: Endpoints
metadata:
  name: external-service  # 必须与Service同名
subsets:
- addresses:
  - ip: 192.168.1.100    # 外部服务IP地址
  - ip: 192.168.1.101    # 可以添加多个IP
  ports:
  - port: 8080          # 必须与Service的targetPort匹配
    protocol: TCP

高级使用场景

混合集群内外端点

可以在 Endpoints 中同时包含集群内部 Pod IP 和外部服务 IP:

yaml
subsets:
- addresses:
  - ip: 10.244.1.10      # 集群内Pod IP
    nodeName: node1       # 可选指定节点
  - ip: 192.168.1.100    # 外部服务IP
  ports:
  - port: 8080

使用域名而非IP

通过创建 ExternalName Service 的变体:

yaml
apiVersion: v1
kind: Service
metadata:
  name: external-dns-service
spec:
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 80
---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-dns-service
subsets:
- addresses:
  - ip: 93.184.216.34   # example.com的IP
  ports:
  - port: 80

动态更新 Endpoints

可以通过以下方式动态更新 Endpoints:

bash
# 获取当前Endpoints配置
kubectl get endpoints external-service -o yaml > endpoints.yaml

# 修改endpoints.yaml后应用
kubectl apply -f endpoints.yaml

实际应用案例

访问外部数据库

yaml
# Service定义
apiVersion: v1
kind: Service
metadata:
  name: external-mysql
spec:
  ports:
  - port: 3306
    targetPort: 3306
---
# Endpoints定义
apiVersion: v1
kind: Endpoints
metadata:
  name: external-mysql
subsets:
- addresses:
  - ip: 10.20.30.40    # 外部MySQL服务器IP
  ports:
  - port: 3306

应用中的连接字符串可以使用 external-mysql:3306,当需要切换数据库时只需更新 Endpoints。

蓝绿部署过渡

yaml
# 初始Endpoints指向v1版本
subsets:
- addresses:
  - ip: 192.168.1.100  # v1版本
  ports:
  - port: 8080

# 更新Endpoints指向v2版本
subsets:
- addresses:
  - ip: 192.168.1.200  # v2版本
  ports:
  - port: 8080

Service 名称保持不变,实现无缝切换。

注意事项

  1. 命名一致性:Endpoints 必须与 Service 同名且在同一命名空间
  2. 端口匹配:Endpoints 中定义的端口必须与 Service 的 targetPort 匹配
  3. 健康检查:对于外部端点,考虑在应用层实现健康检查机制
  4. 安全考虑
    • 使用 NetworkPolicy 限制访问来源
    • 对于敏感服务,结合 RBAC 控制 Endpoints 的修改权限
  5. 监控:监控自定义端点的可用性和性能
  6. 文档:记录哪些服务使用了自定义 Endpoints 及其原因

与标准 Service 的对比

特性标准 Service无选择器 Service + 自定义 Endpoints
后端选择方式通过 Pod 标签自动选择手动指定 IP 地址
适用目标集群内 Pod任意可达的 TCP 端点
动态更新自动需要手动更新
负载均衡算法由 kube-proxy 决定由 kube-proxy 决定
DNS 解析自动自动
典型用途常规微服务外部服务集成、特殊路由需求

实现会话保持

yaml
apiVersion: v1
kind: Service
metadata:
  name: test-session
  namespace: default
  annotations:
    # 负载均衡器专属配置(如使用云厂商LB)
    service.cloudprovider.kubernetes.io/session-affinity: "ClientIP"
spec:
  selector:
    app: ss-nginx  # 必须匹配后端Pod标签
  type: ClusterIP  # 也可使用NodePort/LoadBalancer
  sessionAffinity: ClientIP  # 启用基于客户端IP的会话保持
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 30  # 会话保持超时时间(默认10800秒/3小时)
  ports:
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP

关键参数说明

参数类型默认值作用
sessionAffinitystringNoneClientIPNone
timeoutSecondsint10800会话保持持续时间(秒)
externalTrafficPolicystringCluster设为Local可保留真实客户端IP