目标
- 了解 Kubernetes 中的 Service
- 了解标签(Label)和选择算符(Selector)如何与 Service 关联
- 在 Kubernetes 集群外用 Service 暴露应用
Kubernetes Service 总览
Kubernetes Pods are mortal. Pods have a lifecycle. When a worker node dies, the Pods running on the Node are also lost. A ReplicaSet might then dynamically drive the cluster back to the desired state via the creation of new Pods to keep your application running. As another example, consider an image-processing backend with 3 replicas. Those replicas are exchangeable; the front-end system should not care about backend replicas or even if a Pod is lost and recreated. That said, each Pod in a Kubernetes cluster has a unique IP address, even Pods on the same Node, so there needs to be a way of automatically reconciling changes among Pods so that your applications continue to function.
-->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 发布我们的应用并添加一些标签。
第一步:创建新 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
输出结果如下:
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。
准备好之后,继续学习运行应用的多个实例。