使用 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-dnsv1.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"
注意
如果您正在使用 Docker Desktop 作为容器驱动运行 minikube,则需要 minikube tunnel。这是因为 Docker Desktop 中的容器与您的宿主机相互隔离。
在另一个终端窗口中,执行:
minikube service kubernetes-bootcamp --url
输出如下所示:
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 已暴露。
步骤 2:使用 Label
Deployment 自动为我们的 Pod 创建了一个 label。使用 describe deployment 子命令可以看到该 label 的名称(即 key)。
kubectl describe deployment
让我们使用此 label 来查询 Pods 列表。我们将使用带参数 -l 的 kubectl 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 https://:8080
在这里我们看到应用程序正在运行。这是因为 Deployment 正在管理应用程序。要关闭应用程序,您还需要删除 Deployment。
下一步
- 教程:运行应用程序的多个实例。
- 了解更多关于 Service 的信息。