使用 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"
注意
如果您正在使用 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 http://localhost:8080
在这里我们看到应用程序正在运行。这是因为 Deployment 正在管理应用程序。要关闭应用程序,您还需要删除 Deployment。
下一步
- 教程:运行应用程序的多个实例。
- 了解更多关于 Service 的信息。