什么是 Service?

在 Kubernetes 中,Service 是一种抽象机制,用于将运行在一组 Pod 上的网络应用程序公开为网络服务。它解决了 Kubernetes 中一个关键问题:当 Pod 会动态创建和销毁时,如何让客户端持续找到这些服务。

想象一下,你有一个应用,它需要多个副本(Replicas)来处理请求,但这些副本的 IP 地址会随时变化。Service 就像一个"虚拟的入口",让你可以始终通过同一个地址访问这些应用,而不需要关心后端具体是哪些 Pod。

为什么需要 Service?

Kubernetes 中的 Pod 是临时性的,会随时创建和销毁。每个 Pod 有自己的 IP 地址,但这个地址会变化。这就带来了一个问题:

如果前端应用需要调用后端服务,如何知道后端服务的当前 IP 地址?如何处理后端 Pod 的变化?

Service 解决了这个问题,它提供了一个稳定的网络端点,让你可以:

  1. 无需修改应用代码就能使用 Kubernetes 服务发现
  2. 无论后端 Pod 如何变化,前端都能持续访问
  3. 实现负载均衡,将流量分发到多个后端 Pod

Service 的工作原理

Service 通过以下方式工作:

  1. 选择器(Selector):定义一个标签选择器,匹配一组 Pod
  2. 端点(Endpoints):Service 会自动跟踪匹配的 Pod,并将流量路由到这些 Pod
  3. 虚拟 IP(ClusterIP):Service 被分配一个虚拟 IP 地址,客户端通过这个 IP 访问

例如,一个 Service 可以这样定义:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376

这个 Service 会将流量发送到带有标签 app.kubernetes.io/name: MyApp 的 Pod 的 9376 端口。

Service 的类型

Kubernetes 提供了几种 Service 类型,满足不同场景需求:

1. ClusterIP(默认类型)

  • 通过集群内部 IP 暴露服务
  • 只能在集群内部访问
  • 适用于应用内部的通信

2. NodePort

  • 通过每个节点的 IP 和静态端口(NodePort)暴露服务
  • 在 ClusterIP 基础上额外开放一个端口
  • 适合需要从集群外部访问的场景

示例:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - port: 80
      targetPort: 9376
      nodePort: 30007  # 可选,指定特定端口

3. LoadBalancer

  • 使用云平台的负载均衡器向外部公开服务
  • 适合生产环境,云平台会自动创建负载均衡器
  • 通常用于需要公网访问的场景

示例:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: LoadBalancer
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - port: 80
      targetPort: 9376

4. ExternalName

  • 将 Service 映射到外部 DNS 名称
  • 不使用选择器,而是使用 DNS 名称
  • 适合连接集群外部的数据库或服务

示例:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: ExternalName
  externalName: my.database.example.com

无头服务(Headless Service)

当不需要负载均衡或集群 IP 时,可以创建"无头服务":

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  clusterIP: None  # 关键设置
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - port: 80
      targetPort: 9376

无头服务不会分配集群 IP,而是直接返回 Pod 的 IP 地址,允许客户端直接连接到特定 Pod。

服务发现

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

1. DNS

  • 为每个 Service 创建 DNS 记录
  • 例如:my-service.my-namespace.svc.cluster.local
  • 在集群内,Pod 可以通过 DNS 名称访问 Service

2. 环境变量

  • 当 Pod 启动时,kubelet 会添加 Service 相关的环境变量
  • 例如:REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
  • 但 DNS 是更推荐的方式

高级特性

1. Session Affinity(会话亲和性)

确保来自同一客户端的请求总是被路由到同一个 Pod:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - port: 80
      targetPort: 9376
  sessionAffinity: ClientIP

2. External IPs

通过外部 IP 访问 Service:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - port: 80
      targetPort: 9376
  externalIPs:
    - 198.51.100.32

3. TrafficDistribution(流量分发)

控制流量如何路由到后端:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - port: 80
      targetPort: 9376
  trafficDistribution: PreferClose

4. 不带选择器的 Service

通过手动配置 EndpointSlice 将 Service 映射到外部后端:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

---
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: my-service-1
  labels:
    kubernetes.io/service-name: my-service
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 9376
endpoints:
  - addresses: ["10.4.5.6"]
  - addresses: ["10.1.2.3"]

Service 与 Endpoints 的关系

Service 本身不直接知道后端 Pod 的地址,它通过 Endpoints 或 EndpointSlice 来跟踪这些信息:

  • Endpoints:旧版 API,有端点数量限制(1000个以内)
  • EndpointSlice:新版 API,解决了端点数量限制问题

Kubernetes 会自动为 Service 创建 EndpointSlice,跟踪匹配的 Pod。

为什么 Service 重要?

  1. 解耦:前端和后端解耦,前端不需要知道后端的具体实现
  2. 弹性:后端 Pod 可以动态扩缩容,不影响前端
  3. 可移植性:应用无需修改就能在 Kubernetes 上运行
  4. 负载均衡:自动将流量分发到多个后端实例

实际应用场景

  1. 前端应用访问后端 API:前端通过 Service 名称调用后端,无需关心后端 Pod 的 IP
  2. 微服务架构:多个微服务之间通过 Service 相互调用
  3. 外部服务集成:使用 ExternalName Service 连接外部数据库
  4. 无状态应用:为无状态应用提供统一入口

总结

Service 是 Kubernetes 中的核心概念,它解决了应用在动态环境中如何稳定访问的问题。通过 Service,你可以:

  • 为应用提供稳定的网络端点
  • 实现负载均衡和自动故障转移
  • 与 Kubernetes 的其他组件(如 DNS、Ingress)无缝集成
  • 适应各种部署场景(内部通信、外部访问、云平台集成等)

无论你是在开发、测试还是生产环境中使用 Kubernetes,理解 Service 是构建健壮、可扩展应用的关键。

提示:在 Kubernetes 中,Service 是应用网络的"灵魂",它让应用在容器化环境中能够像传统应用一样可靠地运行。理解 Service 的工作原理,是掌握 Kubernetes 网络的基础。

更多推荐