k8s
官方教程
Kubernetes Basics
Expose Your App

Using a Service to Expose Your App

Objectives

  • Learn about a Service in Kubernetes
  • Understand how labels and selectors relate to a Service
  • Expose an application outside a Kubernetes cluster using a Service

Overview of Kubernetes Services

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

在 Kubernetes 中,服务(Service)是一种抽象,它定义了一组逻辑上的 Pod 以及访问它们的策略。服务实现了相互依赖的 Pod 之间的松散耦合。服务的定义使用 YAML 或 JSON,就像所有 Kubernetes 对象清单一样。通常,服务所针对的 Pod 集合是由标签选择器(label selector)确定的(在下面会说明为什么您可能想要一个在规范中不包含选择器的服务)。

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

  • ClusterIP(默认) - 在集群内部的一个内部 IP 上暴露服务。这种类型使得服务只能从集群内部访问。
  • NodePort - 使用 NAT 在集群中每个选定的节点的相同端口上暴露服务。使用 <NodeIP>:<NodePort> 从集群外部访问服务。它是 ClusterIP 的超集。
  • LoadBalancer - 在当前云环境中(如果支持)创建一个外部负载均衡器,并为服务分配一个固定的外部 IP。它是 NodePort 的超集。
  • ExternalName - 通过返回一个具有其值的 CNAME 记录,将服务映射到 externalName 字段的内容(例如,foo.bar.example.com)。不会设置任何类型的代理。这种类型需要 kube-dns 的 v1.7 或更高版本,或者 CoreDNS 版本 0.0.8 或更高版本。

有关不同类型服务的更多信息,可以在“使用源 IP 教程”中找到。还可以查看“使用服务连接应用程序”。

此外,请注意,在某些服务的使用场景中,可能会在规范中不定义选择器。没有选择器创建的服务也不会创建相应的端点(Endpoints)对象。这允许用户将服务手动映射到特定的端点。另一个可能没有选择器的原因是您严格使用 type: ExternalName 。

Services and Labels

服务会在一组 Pod 之间路由流量。在 Kubernetes 中,服务是一种抽象概念,它使得 Pod 可以在不影响应用程序的情况下消亡和复制。依赖的 Pod 之间的发现和路由(例如应用程序中的前端和后端组件)由 Kubernetes 服务来处理。

Kubernetes 服务使用标签(labels)和选择器(selectors)来匹配一组 Pod,标签和选择器是一种分组原语,允许对 Kubernetes 中的对象进行逻辑操作。标签是附加到对象上的键值对,可以以多种方式使用:

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

![](public/kubernetes/offical-tutorial/module_04_labels.svg)

Labels can be attached to objects at creation time or later on. They can be modified at any time. Let's expose our application now using a Service and apply some labels.

Step 1: Creating a new Service

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

kubectl get pods

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

接下来,让我们列出我们集群中当前的服务:

kubectl get services

当 minikube 启动集群时,默认会创建一个名为 kubernetes 的服务。要创建一个新的服务并将其暴露到外部流量,我们将使用 expose 命令,并将 NodePort 作为参数。

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

让我们再次运行 get services 子命令:

kubectl get services

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

为了找出外部开放的端口(对于 NodePort 类型的服务),我们将运行 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"

注意:

如果您使用 Docker Desktop 作为 minikube 的容器驱动程序,则需要一个 minikube tunnel。这是因为 Docker Desktop 内部的容器与您的主机计算机是隔离的。

在一个单独的终端窗口中,执行:

minikube service kubernetes-bootcamp --url

输出如下:

http://127.0.0.1:51082
!  因为您在 darwin 上使用 Docker 驱动程序,所以需要打开终端才能运行它。

然后使用给定的 URL 访问应用:

curl 127.0.0.1:51082

我们从服务器得到一个响应。该服务已暴露。

Step 2: Using labels

部署会自动为我们的 Pod 创建一个标签。使用 describe deployment 子命令,您可以看到该标签的名称(键):

kubectl describe deployment

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

kubectl get pods -l app=kubernetes-bootcamp

您可以对列出现有的服务执行相同的操作:

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 "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。

Step 3: Deleting a service

要删除服务,您可以使用删除服务子命令。这里也可以使用标签:

kubectl delete service -l app=kubernetes-bootcamp

确认服务已删除:

kubectl get services

这确认了我们的服务已被移除。要确认路由不再暴露,您可以使用 curl 命令访问之前暴露的 IP 和端口:

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

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

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

我们在这里看到应用程序是启动的。这是因为部署(Deployment)在管理应用程序。要关闭应用程序,您还需要删除部署。

当您准备好后,继续进行“运行应用程序的多个实例”。

Summary

  • 将 Pod 暴露给外部流量
  • 在多个 Pod 之间进行流量负载均衡
  • 使用标签

Kubernetes 服务是一个抽象层,它定义了一组逻辑 Pod,并为这些 Pod 实现外部流量暴露、负载均衡和服务发现。