使用 Service 公开应用

目标

  • 了解 Kubernetes 中的 Service。
  • 理解 label 和 selector 如何与 Service 相关联。
  • 将应用程序暴露到 Kubernetes 集群外部。

Kubernetes Service 概述

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

Kubernetes 中的 Service 是一个抽象概念,它定义了一组逻辑 Pods 和访问它们的方式。Service 实现了依赖 Pods 之间的松耦合。Service 和所有 Kubernetes 对象清单一样,可以使用 YAML 或 JSON 定义。Service 所针对的 Pods 通常通过一个 label selector(标签选择器)来确定(参阅下文了解为何可能需要一个 spec 中不包含 selector 的 Service)。

虽然每个 Pod 都有一个唯一的 IP 地址,但在没有 Service 的情况下,这些 IP 地址不会暴露在集群外部。Service 使您的应用程序能够接收流量。可以通过在 Service 的 spec 中指定 type 来以不同方式暴露 Service:

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

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

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

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

更多关于不同类型 Service 的信息可以在使用源 IP 教程中找到。另请参阅通过 Service 连接应用程序

此外,请注意在某些 Service 用例中,其 spec 中不定义 selector。不带 selector 创建的 Service 也不会创建相应的 Endpoints 对象。这允许用户手动将 Service 映射到特定的端点。另一种可能没有 selector 的情况是您严格使用了 type: ExternalName

Service 和 Label

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

Service 使用label 和 selector 来匹配一组 Pods,这是一种分组原语,允许对 Kubernetes 中的对象进行逻辑操作。Label 是附加到对象的键/值对,可以用于多种方式:

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

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

步骤 1:创建新的 Service

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

kubectl get pods

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

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

kubectl get services

现在我们有一个正在运行的 Service 叫做 kubernetes-bootcamp。在这里我们看到 Service 获得了唯一的 cluster-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:使用 Label

Deployment 自动为我们的 Pod 创建了一个 label。使用 describe deployment 子命令可以看到该 label 的名称(即 key)。

kubectl describe deployment

让我们使用此 label 来查询 Pods 列表。我们将使用带参数 -lkubectl get pods 命令,后跟 label 值。

kubectl get pods -l app=kubernetes-bootcamp

您也可以用同样的方式列出现有的 Services。

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,我们使用 label 子命令,后跟对象类型、对象名称和新的 label。

kubectl label pods "$POD_NAME" version=v1

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

kubectl describe pods "$POD_NAME"

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

kubectl get pods -l version=v1

我们看到了 Pod。

步骤 3:删除 Service

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

kubectl delete service -l app=kubernetes-bootcamp

确认 Service 已被删除。

kubectl get services

这证实了我们的 Service 已被移除。要确认该路由不再暴露,您可以对之前暴露的 IP 和端口执行 curl 命令。

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

这证明应用程序不再能从集群外部访问。您可以通过在 Pod 内部执行 curl 命令来确认应用程序仍在运行。

kubectl exec -ti $POD_NAME -- curl http://localhost:8080

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

下一步

最后修改于 2024 年 11 月 18 日太平洋标准时间下午 8:50:添加图片 CSS 和警告框 (b9ee7dbcba)