使用服务公开你的应用程序

目标

  • 了解 Kubernetes 中的 Service。
  • 了解标签和选择器如何与 Service 相关。
  • 将应用程序暴露到 Kubernetes 集群之外。

Kubernetes Service 概述

Kubernetes Pod 是有生命周期的。Pod 具有 生命周期。当一个工作节点死亡时,在该节点上运行的 Pod 也会丢失。然后,Replicaset 可能会通过创建新的 Pod 将集群动态地恢复到所需的状态,以保持应用程序的运行。再举一个例子,考虑一个有 3 个副本的图像处理后端。这些副本是可互换的;前端系统不应该关心后端副本,甚至不应该关心 Pod 是否丢失并重新创建。也就是说,Kubernetes 集群中的每个 Pod 都有一个唯一的 IP 地址,即使是同一节点上的 Pod 也是如此,因此需要有一种方法来自动协调 Pod 之间的更改,以使您的应用程序能够继续运行。

Kubernetes 中的 Service 是一个抽象,它定义了一组逻辑 Pod 和访问它们的方式。Service 实现了依赖 Pod 之间的松散耦合。Service 与所有 Kubernetes 对象清单一样,使用 YAML 或 JSON 定义。Service 所指向的 Pod 集合通常由_标签选择器_决定(有关为何需要在不包含 selector 的情况下创建 Service,请参阅下文)。

尽管每个 Pod 都有唯一的 IP 地址,但如果没有 Service,这些 IP 地址就不会暴露在集群外部。Service 允许您的应用程序接收流量。通过在 Service 的 spec 中指定 type,Service 可以以不同的方式暴露。

  • ClusterIP(默认) - 在集群中的内部 IP 上暴露 Service。此类型使 Service 只能在集群内部访问。

  • NodePort - 使用 NAT 在集群中每个选定节点的同一端口上暴露 Service。使用 NodeIP:NodePort 使 Service 可以从集群外部访问。它是 ClusterIP 的超集。

  • LoadBalancer - 在当前云中(如果支持)创建外部负载均衡器,并为 Service 分配一个固定的外部 IP。它是 NodePort 的超集。

  • ExternalName - 通过返回包含其值的 CNAME 记录,将 Service 映射到 externalName 字段的内容(例如 foo.bar.example.com)。不设置任何类型的代理。此类型需要 kube-dns v1.7 或更高版本,或者 CoreDNS 0.0.8 或更高版本。

有关不同类型的 Service 的更多信息,请参阅使用源 IP 教程。另请参阅使用 Service 连接应用程序

此外,请注意,Service 有些用例不涉及在 spec 中定义 selector。在没有 selector 的情况下创建的 Service 也不会创建相应的 Endpoints 对象。这允许用户手动将 Service 映射到特定的端点。可能没有选择器的另一个原因是你严格使用 type: ExternalName

Service 和标签

Service 将流量路由到一组 Pod。Service 是一种抽象,允许 Pod 在 Kubernetes 中消亡和复制而不影响您的应用程序。依赖 Pod 之间(例如应用程序中的前端和后端组件)的发现和路由由 Kubernetes Service 处理。

Service 使用标签和选择器匹配一组 Pod,这是一种分组原语,允许对 Kubernetes 中的对象进行逻辑操作。标签是附加到对象的键/值对,可以以多种方式使用。

  • 指定用于开发、测试和生产的对象
  • 嵌入版本标签
  • 使用标签对对象进行分类

标签可以在创建时或之后附加到对象。它们可以随时修改。现在让我们使用 Service 暴露我们的应用程序并应用一些标签。

步骤 1:创建新的 Service

让我们验证一下我们的应用程序是否正在运行。我们将使用 kubectl get 命令并查找现有的 Pod。

kubectl get pods

如果没有 Pod 正在运行,则意味着之前教程中的对象已被清理。在这种情况下,请返回并从使用 kubectl 创建 Deployment 教程中重新创建 Deployment。请稍等几秒钟,然后再次列出 Pod。看到一个 Pod 运行后,即可继续。

接下来,让我们列出集群中当前的 Service。

kubectl get services

为了将 Deployment 暴露给外部流量,我们将使用带有 --type=NodePort 选项的 kubectl expose 命令。

kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080

我们现在有一个名为 kubernetes-bootcamp 的运行中的 Service。在这里我们看到 Service 收到了一个唯一的集群 IP、一个内部端口和一个外部 IP(节点的 IP)。

要找出外部打开的端口(对于 type: NodePort Service),我们将运行 describe service 子命令。

kubectl describe services/kubernetes-bootcamp

创建一个名为 NODE_PORT 的环境变量,其值为分配的节点端口。

export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')"
echo "NODE_PORT=$NODE_PORT"

现在我们可以使用 curl、节点的 IP 地址和外部暴露的端口来测试应用程序是否暴露在集群外部。

curl http://"$(minikube ip):$NODE_PORT"

我们收到了来自服务器的响应。Service 已暴露。

步骤 2:使用标签

Deployment 自动为我们的 Pod 创建了一个标签。通过 describe deployment 子命令,您可以看到该标签的名称(键)。

kubectl describe deployment

让我们使用此标签查询我们的 Pod 列表。我们将使用 kubectl get pods 命令,其中 -l 作为参数,后跟标签值。

kubectl get pods -l app=kubernetes-bootcamp

您可以使用相同的方法列出现有 Service。

kubectl get services -l app=kubernetes-bootcamp

获取 Pod 的名称并将其存储在 POD_NAME 环境变量中。

export POD_NAME="$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')"
echo "Name of the Pod: $POD_NAME"

要应用新标签,我们使用 label 子命令,后跟对象类型、对象名称和新标签。

kubectl label pods "$POD_NAME" version=v1

这将为我们的 Pod 应用一个新标签(我们将应用程序版本固定到 Pod),我们可以使用 describe pod 命令进行检查。

kubectl describe pods "$POD_NAME"

我们在这里看到标签现在已附加到我们的 Pod。现在我们可以使用新标签查询 Pod 列表。

kubectl get pods -l version=v1

我们看到了 Pod。

步骤 3:删除 Service

要删除 Service,您可以使用 delete service 子命令。标签也可以在这里使用。

kubectl delete service -l app=kubernetes-bootcamp

确认 Service 已消失。

kubectl get services

这确认了我们的 Service 已被删除。要确认路由不再暴露,您可以 curl 之前暴露的 IP 和端口。

curl http://"$(minikube ip):$NODE_PORT"

这证明应用程序无法再从集群外部访问。您可以从 Pod 内部使用 curl 确认应用程序仍在运行。

kubectl exec -ti $POD_NAME -- curl https://:8080

我们在这里看到应用程序正在运行。这是因为 Deployment 正在管理应用程序。要关闭应用程序,您还需要删除 Deployment。

下一步

上次修改于 2025 年 5 月 8 日上午 9:59 PST:更新 expose-intro.md 中的 expose 命令 (#50570) (894a84131b)