使用 Service 暴露你的应用

了解 Kubernetes 中的 Service。 理解标签和选择算符如何关联到 Service。 在 Kubernetes 集群外暴露应用。

目标

  • 了解 Kubernetes 中的 Service
  • 了解标签(Label)和选择算符(Selector)如何与 Service 关联
  • 在 Kubernetes 集群外用 Service 暴露应用

Kubernetes Service 总览

Kubernetes Pod 是转瞬即逝的。 Pod 拥有 生命周期。 当一个工作节点挂掉后, 在节点上运行的 Pod 也会消亡。 ReplicaSet 会自动地通过创建新的 Pod 驱动集群回到目标状态,以保证应用正常运行。 换一个例子,考虑一个具有 3 个副本的用作图像处理的后端程序。 这些副本是可替换的。前端系统不应该关心后端副本,即使某个 Pod 丢失或被重新创建。 此外,Kubernetes 集群中的每个 Pod 都有一个唯一的 IP 地址,即使是在同一个 Node 上的 Pod 也是如此, 因此需要一种方法来自动协调 Pod 之间的变化,以便应用保持运行。

Kubernetes 中的服务(Service)是一种抽象概念,它定义了 Pod 的逻辑集和访问 Pod 的协议。 Service 使从属 Pod 之间的松耦合成为可能。 和所有 Kubernetes 对象清单一样, Service 用 YAML 或者 JSON 来定义。 Service 下的一组 Pod 通常由一个 标签选择算符 来标记 (请参阅下面的说明为什么你可能想要一个 spec 中不包含 selector 的 Service)。

尽管每个 Pod 都有一个唯一的 IP 地址,但是如果没有 Service,这些 IP 不会被公开到集群外部。 Service 允许你的应用接收流量。 通过设置 Service 的 spec 中的 type,你可以用不同的方式公开 Service:

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

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

另外,需要注意的是有一些 Service 的用例不需要在 spec 中定义 selector。 一个创建时未设置 selector 的 Service 也不会创建相应的 Endpoint 对象。 这允许用户手动将 Service 映射到特定的端点。 没有 selector 的另一种可能是你在严格使用 type: ExternalName 服务。

总结

  • 将 Pod 暴露给外部通信
  • 跨多个 Pod 的负载均衡
  • 使用标签(Label)

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

Service 和 Label

Service 为一组 Pod 提供流量路由。Service 是一种抽象,允许 Kubernetes 中的 Pod 死亡和复制,而不会影响应用。 在依赖的 Pod(如应用中的前端和后端组件)之间进行发现和路由是由 Kubernetes Service 处理的。

Service 通过标签和选择算符来匹配一组 Pod, 它们是允许对 Kubernetes 中的对象进行逻辑操作的一种分组原语。 标签是附加在对象上的键/值对,可以以多种方式使用:

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

使用 Service 暴露你的应用 - 图1

标签可以在对象创建时或之后附加到对象上。它们可以随时被修改。现在使用 Service 发布我们的应用并添加一些标签。

第一步:创建新 Service

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

**kubectl get pods**

如果没有 Pod 正在运行,则意味着之前教程中的对象已被清理。这时, 请返回并参考 使用 kubectl 创建 Deployment 教程重新创建 Deployment。 请等待几秒钟,然后再次列举 Pod。一旦看到一个 Pod 正在运行,你就可以继续了。

接下来,让我们列举当前集群中的 Service:

**kubectl get services**

我们有一个名为 kubernetes 的 Service ,它在 minikube 启动集群时默认创建。 要创建一个新的 Service 然后暴露给外部流量,我们将使用 expose 命令,并将 NodePort 作为参数。

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

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

**kubectl get services**

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

要得到外部打开的端口号(对于 type: NodePort 的服务),我们需要运行 describe service 子命令:

**kubectl describe services/kubernetes-bootcamp**

创建一个名为 NODE_PORT 的环境变量,它的值为所分配的 Node 端口:

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

现在我们可以使用 curl、Node 的 IP 地址和对外暴露的端口,来测试应用是否已经被公开到了集群外部:

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

说明:

如果你正在使用 Docker Desktop 作为容器驱动来运行 minikube, 需要使用 minikube tunnel。 这是因为 Docker Desktop 内部的容器和宿主机是隔离的。

在另一个终端窗口中,执行:
**minikube service kubernetes-bootcamp --url**

输出结果如下:

  1. http://127.0.0.1:51082! Because you are using a Docker driver on darwin, the terminal needs to be open to run it.

然后使用提供的 URL 访问应用:
**curl 127.0.0.1:51082**

然后我们就会收到服务器的响应。Service 已经被暴露。

第二步:使用标签

Deployment 自动给我们的 Pod 创建了一个标签。通过 describe deployment 子命令你可以看到那个标签的名称(对应 key):

**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 pods 命令检查它:

**kubectl describe pods "$POD_NAME"**

我们可以看到现在标签已经被附加到我们的 Pod 上。我们可以通过新的标签来查询 Pod 列表:

**kubectl get pods -l version=v1**

我们看到了对应的 Pod。

第三步:删除一个 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 http://localhost:8080**

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

准备好之后,继续学习运行应用的多个实例