在 Kubernetes 集群中,Pod 的生命周期是动态的,其 IP 地址也可能随时发生变化。为了确保客户端或其他应用能够稳定、可靠地访问后端服务,Kubernetes 引入了 Service(服务)这一重要组件。 Service 为一组可变的、同类型的 Pod 提供了一个持久且唯一的访问入口,屏蔽了背后 Pod 实例频繁变化所带来的复杂性。

理解 Service 的运作方式,需要关注其核心机制,包括标签选择器(Label Selector)与 Endpoints 机制等。
Service 并不直接管理 Pod 实例,而是通过标签(Labels)与选择器(Label Selectors)来动态选定为其提供服务的 Pod。当我们为 Pod 指定如 app: web-frontend 或 env: production 等标签,并在 Service 配置中通过 selector 指定筛选规则后,所有满足相应标签的 Pod 都会自动受 Service 管控。
这种解耦模型保障了 Service 与 Pod 的松散连接——无论新增、删除还是变更标签,Pod 都会根据当前标签状态自动纳入或移出 Service 的管理范围。
如上图所示,Service 借助如 app=chicken 这样的选择器,将流量精准路由至所有标签匹配的目标 Pod,同时自动忽略标签不匹配的 Pod。
对于每个 Service,Kubernetes 自动维护一个 Endpoints 对象,用以记录所有当前可被该 Service 路由的 Pod 的 IP 地址与端口。该对象随集群内 Pod 状态的变化而实时同步——只要满足选择条件的 Pod 健康可用,其信息即被自动添加至 Endpoints;相应地,失效或被删除的 Pod 会及时从列表中移除。
在流量转发过程中,Service 始终查询对应的 Endpoints 信息,动态选择健康的后端 Pod 处理客户端请求,这一机制确保了服务访问的高可用性与稳定性。
为满足各类应用场景中的访问需求,Kubernetes 提供了多种 Service 类型。每种类型均针对不同的网络暴露需求和部署环境进行优化,用户可根据实际场景灵活选用。

ClusterIP 是 Kubernetes Service 的默认类型。该类型会为 Service 分配一个仅能被集群内部访问的虚拟 IP,所有集群内的 Pod、节点均可通过该 IP 地址进行通信,无法被集群外部直接访问。此类型广泛用于集群内部各服务间的互联。例如,Web 应用服务器通过 ClusterIP 访问后端数据库服务,实现微服务间安全、稳定的内部通信。
NodePort 类型会在每个节点(Node)上开放一个同样的、预定的端口,并将流量转发至后端的 Service。通过访问集群内任意节点的 [节点IP]:[NodePort],外部客户端即可访问到服务。这一机制基于 ClusterIP 实现,并向外层拓展了访问范围。NodePort 适用于开发、测试等环境下的简易外部暴露,但在生产环境下需谨慎使用,因为其端口分配有限且占用全局资源。
尽管 NodePort 能够快速将服务暴露到外部,但在生产环境建议谨慎使用,避免端口资源冲突及安全隐患。
LoadBalancer 类型适用于需要直接暴露至公网的生产级服务。该类型在 NodePort 基础上,借助所使用的云服务平台(如 AWS、Azure、GCP)自动分配和配置外部负载均衡器。负载均衡器分配一个公网 IP,并将访问流量转发至所有后端节点上的 NodePort。该方式为服务引流提供了高可用、可扩展的流量入口,是云端场景下推荐的对外暴露方式。
ExternalName 类型并不直接将流量导向集群内部的 Pod,而是通过为 Service 配置 DNS CNAME,从而将服务名称映射为外部可访问的域名。当集群内部的服务访问此 Service 时,实际上将被自动重定向至指定的外部域名。这种用法对于集群内应用统一访问第三方托管服务(如外部数据库、API 接口)场景非常有价值。
让我们来亲手创建一个 Service 吧!在 Kubernetes 中,我们强烈推荐使用声明式的 YAML 文件来定义和管理对象。
虽然可以使用 kubectl expose 这样的命令式操作来快速创建 Service,但这被认为是不好的实践。因为它会让你的操作脱离版本控制,导致配置文件和实际运行状态不一致,给未来的维护带来隐患。
首先,我们需要创建一个 Deployment,这个对象将帮助我们在 Kubernetes 集群中部署和管理我们的应用程序。
|# deploy.yml apiVersion: apps/v1 kind: Deployment metadata: name: web-deploy spec: replicas: 3 selector: matchLabels: app: hello-world template: metadata: labels: app: hello-world # 注意这里的标签,Service将通过它来找到Pod spec: containers
接下来,我们需要为刚才创建的 Deployment 配置一个 NodePort 类型的 Service。这个 Service 的作用是将外部的请求流量引导到集群内部的 Pod 上。
通过这种方式,我们可以在开发和测试环境中快速地将应用程序暴露给外部用户。具体来说,NodePort 会在每个节点上开放一个特定的端口,这样外部用户就可以通过节点的 IP 地址和这个端口来访问我们的应用程序。
|# svc.yml apiVersion: v1 kind: Service metadata: name: hello-svc spec: type: NodePort # 定义 Service 类型为 NodePort selector: app: hello-world # 选择所有带有 app=hello-world 标签的 Pod ports: - protocol: TCP port: 8080 # Service 在集群内部监听的端口 (ClusterIP 的端口) targetPort: 8080
现在我们需要使用 kubectl 命令来应用我们刚才编写的 YAML 配置文件。这个过程就像是给 Kubernetes 下达指令,让它根据我们的配置去创建和管理资源。
通过执行这些命令,Kubernetes 会在集群中创建相应的 Deployment 和 Service,从而实现应用程序的部署和对外暴露。
|$ kubectl apply -f deploy.yml deployment.apps/web-deploy created $ kubectl apply -f svc.yml service/hello-svc created
现在,你可以通过 kubectl get svc 查看 Service 的状态:
|$ kubectl get svc hello-svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE hello-svc NodePort 10.108.144.24 <none> 8080:30001/TCP 1m
输出显示,我们的 Service hello-svc 已经成功创建,并且可以通过任意节点的 IP 地址加上 30001 端口从外部访问。
Service 最强大的功能之一,就是能够与 Deployment 配合,实现应用的无缝升级(也称为蓝绿部署或金丝雀发布)。
想象一下,你的网站 v1 版本正在线上运行。现在你开发了 v2 版本,希望在不影响用户的情况下完成上线。
version: v1,所有流量都流向 v1 版本的 Pod。v2 版本的 Pod(带有 version: v2 标签)部署到集群中。此时,因为标签不匹配,Service 不会把流量发给它们。v2 版本工作正常后,只需修改 Service 的 YAML 文件,将选择器从 selector: { version: v1 } 改为 selector: { version: v2 },然后执行 kubectl apply -f svc.yml。就在你应用修改的一瞬间,Kubernetes 就会更新 Service 的路由规则,所有新的用户请求都会被平滑地切换到 v2 版本的 Pod 上,整个过程用户毫无感知。如果 v2 版本出现问题,你只需用同样的方法把选择器改回 v1,就能立刻回滚。
这种简单而强大的机制,正是 Kubernetes 在保障应用高可用性和敏捷性方面的核心魅力所在。
完成实验后,不要忘记删除我们刚刚创建的资源:
|$ kubectl delete -f deploy.yml $ kubectl delete -f svc.yml
在本部分,我们学习了 Kubernetes Service 如何为应用提供稳定可靠的网络。它通过固定的前端(IP、DNS)和动态的后端(通过标签选择的 Pods),优雅地解决了微服务架构中的服务发现和负载均衡难题。