服务


本页面介绍了 Kubernetes Service 及其在 Google Kubernetes Engine (GKE) 中的用法。Service 分为多种不同类型,您可以使用它们将一组 Pod 端点划分为单一资源。如需了解如何创建 Service,请参阅使用 Service 公开应用

什么是 Kubernetes 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:
  ...

为何要使用 Kubernetes Service?

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

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

Kubernetes Service 的类型

提供五种类型的 Service:

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

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

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

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

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

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

对于某些集群配置,外部应用负载均衡器使用 NodePort 类型的 Service。

外部应用负载均衡器是代理服务器,与本主题中 LoadBalancer 类型的 Service 下介绍的外部直通网络负载均衡器完全不同。

LoadBalancer 类型的 Service

如需详细了解 LoadBalancer 类型的 Service,请参阅 LoadBalancer Service 概念

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 是一种不分配集群 IP 地址的 Kubernetes Service 类型。不过,无头 Service 会使用 DNS 公开与 Service 关联的 Pod 的 IP 地址。这样,您就可以直接连接到 Pod,而不是通过代理。

无头 Service 适用于各种场景,包括:

  • Pod 之间的负载均衡:您可以使用无头 Service 在 Pod 之前实现负载均衡。如需实现此目标,请使用与要负载均衡的 Pod 匹配的选择器创建 Service。然后,Service 会在与选择器匹配的所有 Pod 之前均匀分配流量。

  • 服务发现:您可以使用无头 Service 来实现服务发现。如需实现此目标,请使用名称和选择器创建 Service。无头服务的 DNS 记录包含与该选择器匹配且与该 Pod 匹配的所有 Pod 的 IP 地址。客户端可以使用这些 DNS 记录来查找与 Service 关联的 Pod 的 IP 地址。

  • 直接访问 Pod:客户端可以直接连接到与无头 Service 关联的 Pod,这对需要直接访问底层 Pod 的服务(例如负载均衡器和 DNS 服务器)非常有用。

  • 灵活性:无头服务可用于创建各种不同的拓扑,例如负载均衡器、DNS 服务器和分布式数据库。

如果您的工作负载有特殊的网络要求,而无法通过具有选择器的无头 Service 来解决,那么还可以使用无选择器的无头 Service。无头 Service 是访问不在 Kubernetes 集群本身内的 Service 的有用工具,由于控制平面不会创建 Endpointslice 对象,因此您可以在无选择器的 Service 中详细了解该服务

以下示例是无头 Service 的清单:

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  clusterIP: None
  selector:
    app: nginx
  ports:
  - name: http
    port: 80
    targetPort: 80

创建无头 Service 后,您可以通过查询 DNS 查找与 Service 关联的 Pod 的 IP 地址。例如,以下命令列出了与 nginx 服务关联的 Pod 的 IP 地址:

dig +short nginx.default.svc.cluster.local

另一个使用 Kubernetes 查询扩展的示例:

dig +short +search nginx

您只需一个命令即可创建无头 Service,而且无头 Service 可以轻松更新和扩缩。

kubectl create service clusterip my-svc --clusterip="None" --dry-run=client -o yaml > [file.yaml]

Service 抽象

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

任意 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 的成员。

单栈和双栈 Service

您可以创建 ClusterIPNodePort 类型的 IPv6 Service。GKE 在预览版期间支持 LoadBalancer 类型的双栈 Service,我们不为其提供服务等级协议 (SLA) 或技术支持。

对于每种 Service 类型,您可以将 ipFamiliesipFamilyPolicy 字段定义为 IPv4、IPv6 或双栈 Service。

后续步骤