服务

本页面介绍 Kubernetes Service 及其在 Google Kubernetes Engine 中的使用。要了解如何创建 Service,请参阅使用 Service 公开应用

什么是 Service?

Service 的理念是将一组 Pod 端点划分为单一资源。您可以配置各种方式来访问该分组。默认情况下,您会获得稳定的集群 IP 地址,集群内部的客户端可以使用该 IP 地址与 Service 中的 Pod 通信。客户端向稳定 IP 地址发送请求,然后请求会被路由到 Service 的其中一个 Pod。

Service 通过选择器来识别其成员 Pod。为使 Pod 成为 Service 的成员,该 Pod 必须具有选择器中指定的所有标签。 标签是附加到对象的任意键值对。

下列 Service 清单所具有的选择器指定了两种标签。selector 字段指示同时具有 app: metrics 标签和 department:engineering 标签的任何 Pod 都是该 Service 的成员。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: metrics
    department: engineering
  ports:
  ...

为何使用 Service?

在 Kubernetes 集群中,每个 Pod 都具有内部 IP 地址。但是,Deployment 中的 Pod 可以随时加入和退出,而且它们的 IP 地址也不是固定的。因此,直接使用 Pod IP 地址毫无意义。通过 Service,您会获得稳定的 IP 地址,该 IP 地址在 Service 的生命周期内有效,即使成员 Pod 的 IP 地址发生变化也仍然有效。

Service 还提供负载平衡。客户端会调用单个稳定的 IP 地址,而且客户端请求在 Service 的成员 Pod 之间保持平衡。

Service 类型

提供五种类型的 Service:

  • ClusterIP(默认):内部客户端向稳定的内部 IP 地址发送请求。

  • NodePort:客户端向使用 Service 指定的一个或多个 nodePort 值的节点的 IP 地址发送请求。

  • LoadBalancer:客户端向网络负载平衡器的 IP 地址发送请求。

  • ExternalName:内部客户端使用 Service 的 DNS 名称作为外部 DNS 名称的别名。

  • Headless:如果您需要 Pod 分组,但不需要稳定的 IP 地址,则可以使用 Headless 服务

NodePort 类型是 ClusterIP 类型的扩展。因此,NodePort 类型的 Service 具有集群 IP 地址。

LoadBalancer 类型是 NodePort 类型的扩展。因此,LoadBalancer 类型的 Service 具有集群 IP 地址以及一个或多个 nodePort 值。

ClusterIP 类型的 Service

创建 ClusterIP 类型的 Service 时,Kubernetes 会创建一个稳定的 IP 地址,该 IP 地址可从集群中的节点访问。

以下是 ClusterIP 类型的 Service 的清单:

apiVersion: v1
kind: Service
metadata:
  name: my-cip-service
spec:
  selector:
    app: metrics
    department: sales
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

您可以使用 kubectl apply -f [MANIFEST_FILE] 创建 Service。创建服务后,您可以使用 kubectl get service 查看稳定的 IP 地址:

NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)
my-cip-service   ClusterIP   10.11.247.213   none          80/TCP

集群中的客户端通过使用集群 IP 地址和 Service 清单的 port 字段中指定的 TCP 端口来调用 Service。请求将转发到 targetPort 字段中指定的 TCP 端口上的其中一个成员 Pod。因此,对于上述示例,客户端在 TCP 端口 80 上调用 10.11.247.213 的 Service。请求将转发到 TCP 端口 8080 上的其中一个成员 Pod。请注意,成员 Pod 必须拥有一个侦听 TCP 端口 8080 的容器。如果没有容器侦听端口 8080,则客户端将看到类似于“连接失败”或“无法访问该网站”的消息。

NodePort 类型的 Service

创建 NodePort 类型的 Service 时,Kubernetes 会为您提供 nodePort 值。然后,您可以使用任何节点的 IP 地址及 nodePort 值来访问 Service。

以下是 NodePort 类型的 Service 的清单:

apiVersion: v1
kind: Service
metadata:
  name: my-np-service
spec:
  selector:
    app: products
    department: sales
  type: NodePort
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

创建 Service 后,您可以使用 kubectl get service -o yaml 查看其规范并查看 nodePort 值。

spec:
  clusterIP: 10.11.254.114
  externalTrafficPolicy: Cluster
  ports:
  - nodePort: 32675
    port: 80
    protocol: TCP
    targetPort: 8080

外部客户端使用节点的外部 IP 地址及 nodePort 指定的 TCP 端口来调用 Service。请求将转发到 targetPort 字段指定的 TCP 端口上的其中一个成员 Pod。

例如,假设其中一个集群节点的外部 IP 地址是 203.0.113.2。那么,对于上述示例,外部客户端在 TCP 端口 32675 上调用 203.0.113.2 的 Service。请求将转发到 TCP 端口 8080 上的其中一个成员 Pod。成员 Pod 必须拥有一个侦听 TCP 端口 8080 的容器。

NodePort Service 类型是 ClusterIP Service 类型的扩展。因此,内部客户端可通过以下两种方式来调用 Service:

  • 使用 clusterIPport
  • 使用节点的内部 IP 地址和 nodePort

对于某些集群配置,Google Cloud HTTP(S) 负载平衡器使用 NodePort 类型的 Service。如需了解详情,请参阅使用 Ingress 设置 HTTP 负载平衡

请注意,HTTP(S) 负载平衡器是代理服务器,与本主题中 LoadBalancer 类型的 Service 下介绍的网络负载平衡器完全不同。

LoadBalancer 类型的 Service

创建 LoadBalancer 类型的 Service 时,Google Cloud 控制器会在您的项目中唤醒并配置网络负载平衡器。负载平衡器具有稳定的 IP 地址,您可以从项目外部访问该 IP 地址。

请注意,网络负载平衡器不是代理服务器。它会转发数据包,但不更改来源和目标 IP 地址。

以下是 LoadBalancer 类型的 Service 的清单:

apiVersion: v1
kind: Service
metadata:
  name: my-nlb-service
spec:
  selector:
    app: metrics
    department: engineering
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 8080

创建 Service 后,您可以使用 kubectl get service -o yaml 查看其规范并查看稳定的外部 IP 地址:

spec:
  clusterIP: 10.11.242.115
  externalTrafficPolicy: Cluster
  ports:
  - nodePort: 32676
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: metrics
    department: engineering
  sessionAffinity: None
  type: LoadBalancer
  status:
    loadBalancer:
      ingress:
      - ip: 203.0.113.100

在输出中,网络负载平衡器的 IP 地址显示在 loadBalancer:ingress: 下方。外部客户端使用负载平衡器的 IP 地址和 port 指定的 TCP 端口来调用 Service。请求将转发到 targetPort 指定的 TCP 端口上的其中一个成员 Pod。因此,对于上述示例,客户端在 TCP 端口 80 上调用 203.0.113.100 的 Service。 请求将转发到 TCP 端口 8080 上的其中一个成员 Pod。 成员 Pod 必须拥有一个侦听 TCP 端口 8080 的容器。

LoadBalancer Service 类型是 NodePort 类型的扩展,而 NodePort 类型是 ClusterIP 类型的扩展。

ExternalName 类型的 Service

ExternalName 类型的 Service 为外部 DNS 名称提供内部别名。内部客户端使用内部 DNS 名称发出请求,然后请求会被重定向到外部名称。

以下是 ExternalName 类型的 Service 的清单:

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

创建 Service 时,Kubernetes 会创建一个 DNS 名称,内部客户端可以使用该名称来调用 Service。对于上述示例,DNS 名称是 my-xn-service.default.svc.cluster.local。当内部客户端向 my-xn-service.default.svc.cluster.local 发出请求时,请求会被重定向到 example.com。

ExternalName Service 类型与其他 Service 类型完全不同。事实上,ExternalName 类型的 Service 不符合本主题开头提出的 Service 定义。ExternalName 类型的 Service 不与一组 Pod 相关联,也没有稳定的 IP 地址。相反,ExternalName 类型的 Service 从内部 DNS 名称映射到外部 DNS 名称。

Service 抽象

Service 是一个抽象概念,因为它不是侦听某个网络接口的进程。部分抽象在集群节点的 iptables 规则中实现。根据 Service 类型,抽象的其他部分通过网络负载平衡HTTP(S) 负载平衡实现。

任意 Service 端口

Service 清单中 port 字段的值是任意的。但是,targetPort 的值不是任意值。每个成员 Pod 必须拥有一个侦听 targetPort 的容器。

以下是 port 值为 50000 的 LoadBalancer 类型的 Service:

apiVersion: v1
kind: Service
metadata:
  name: my-ap-service
spec:
  clusterIP: 10.11.241.93
  externalTrafficPolicy: Cluster
  ports:
  - nodePort: 30641
    port: 50000
    protocol: TCP
    targetPort: 8080
  selector:
    app: parts
    department: engineering
  sessionAffinity: None
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 203.0.113.200

客户端在 TCP 端口 50000 上调用 203.0.113.200 的 Service。请求将转发到 TCP 端口 8080 上的其中一个成员 Pod。

多个端口

Service 的 ports 字段是一组 ServicePort 对象。ServicePort 对象具有以下字段:

  • name
  • protocol
  • port
  • targetPort
  • nodePort

如果您具有多个 ServicePort,则每个 ServicePort 必须具有唯一的名称。

以下是具有两个 ServicePort 对象的 LoadBalancer 类型的 Service:

apiVersion: v1
kind: Service
metadata:
  name: my-tp-service
spec:
  clusterIP: 10.11.242.196
  externalTrafficPolicy: Cluster
  ports:
  - name: my-first-service-port
    nodePort: 31233
    port: 60000
    protocol: TCP
    targetPort: 50000
  - name: my-second-service-port
    nodePort: 31081
    port: 60001
    protocol: TCP
    targetPort: 8080
  selector:
    app: tests
    department: engineering
  sessionAffinity: None
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 203.0.113.201

在上述示例中,如果客户端在 TCP 端口 60000 上调用 203.0.113.201 的 Service,则请求将转发到 TCP 端口 50000 上的成员 Pod。但是,如果客户端在 TCP 端口 60001 上调用 203.0.113.201 的 Service,则请求将转发到 TCP 端口 8080 上的成员 Pod。

每个成员 Pod 必须拥有一个侦听 TCP 端口 50000 的容器和一个侦听 TCP 端口 8080 的容器。这可以是具有两个线程的单个容器,也可以是在同一 Pod 中运行的两个容器。

Service 端点

创建 Service 时,Kubernetes 会创建一个与 Service 同名的 Endpoints 对象。Kubernetes 使用 Endpoints 对象来跟踪哪些 Pod 是 Service 的成员。

后续步骤