使用 Service 暴露应用

KubernetesBeginner
立即练习

简介

在之前的实验中,你已经了解到 Pod 会不断创建和销毁。如果一个 Pod 出现故障,由新的 Pod 替代它,新 Pod 会获得完全不同的 IP 地址。想象一下:你每次挂断电话后,朋友的电话号码都会改变!

Kubernetes 使用 Service 解决这个问题。

即使后端 Pod 发生变化,Service 也能为工作负载提供稳定的网络身份。将 Pod 生命周期与 Service 端点分离,是 Kubernetes 网络机制中的基本理念。

这是 Kubernetes 网络中最重要的思维模型之一:Pod 应该可以被替换,但客户端仍然需要一个可靠的地址。Service 位于不断变化的 Pod 前面,通过稳定的名称和虚拟 IP 对外提供访问入口,从而解决了这一矛盾。

在本实验中,你将:

  • 创建一个 Service,为应用提供稳定的地址。
  • 了解 Service 如何知道应该将请求发送给哪些 Pod(使用 Selector)。
  • 使用一种名为 NodePort 的 Service 类型从集群外部访问应用。

创建 Service YAML

在此步骤中,你将为 Service 创建配置文件。我们将使用一种名为 NodePort 的特定类型。

NodePort Service 会在集群中的每个 Node(服务器)上开放一个指定端口。这就像在房屋上打开一扇特定的门,让外部世界可以进入。

NodePort 易于理解,因为它会直接映射到每个节点上的端口,但在生产环境中通常不是对外部流量的最终设计方案。在较大的环境中,NodePort 通常位于云负载均衡器或 ingress 层之后。在本实验中使用 NodePort,是因为它能让初学者清楚地看到网络请求的路径。

在项目目录中创建名为 service.yaml 的文件:

nano service.yaml

粘贴以下内容。注意 selector 部分——我们暂时故意将其设置为 app: unknown,以便展示它的工作方式。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  ports:
    - port: 80 ## The port the Service exposes internally
      targetPort: 80 ## The port the container is listening on
      nodePort: 30008 ## The static door we are opening to the outside
  selector:
    app: unknown ## This needs to match your Pod's label

保存并退出(Ctrl+X、Y、Enter)。

此文件定义了一个名为 my-service 的 Service。它会监听端口 30008(即 nodePort)。所有到达此端口的流量都会被转发到带有 app: unknown 标签的 Pod(当前不存在这样的 Pod!)。

修正选择器

Service 使用 Selector 查找应该接收流量的 Pod。它的作用类似于邮寄地址标签。如果信封上的地址(Service Selector)与房屋上的地址(Pod Label)不匹配,邮件就无法送达。

标签和选择器不仅用于 Service,还会出现在 Kubernetes 的各个部分中。它们是该平台的核心分组机制之一。Kubernetes 资源通常不会写死具体的 Pod 名称,而是通过匹配标签来查找相关对象。这样,即使 Pod 被重新创建,系统仍然保持灵活。

在 Setup 步骤中,我们已经为你自动启动了一个 Web 应用。现在找出它的「地址标签」。

运行以下命令,查看正在运行的 Pod 的标签:

kubectl get pods --show-labels

你应该会看到类似这样的内容:web-app-xxxxx ... app=web-app

标签是 app=web-app。但是,我们的 Service 查找的是 app: unknown。现在修正这个配置。

编辑 service.yaml 文件:

nano service.yaml

将 selector 部分修改为与你的 Pod 标签匹配:

selector:
  app: web-app

保存并退出。现在,Service 已经准确知道应该将流量发送到哪里。

应用并验证

现在地址已经正确,接下来启用 Service。

应用此清单后,Kubernetes 会创建 Service 对象,并设置内部网络规则,使到达 Service 的请求可以转发到匹配的 Pod 端点。需要注意的是,Service 本身不会运行你的应用。它是位于应用 Pod 前面的网络抽象层。

将配置应用到集群:

kubectl apply -f service.yaml

你应该会看到:service/my-service created。

确认 Service 已启动并正在运行,查看指定 Service:

kubectl get svc my-service

你将看到:

  • NAME:my-service
  • TYPE:NodePort
  • **PORT(S)**:80:30008/TCP

这表示你的「电话号码」已经启用,并正在端口 30008 上等待请求。

访问应用

现在到了验证结果的时候。我们能否从「外部」(你的终端)访问应用?

应用已经暴露在 Kubernetes Node 的 30008 端口上。

这里涉及三个端口,初学者经常会混淆它们。nodePort 是节点上对外开放的端口,port 是 Service 抽象层暴露的端口,targetPort 是容器内部实际提供应用服务的端口。区分这三个端口的作用,可以避免后续产生混淆。

首先,获取 Minikube 节点的 IP 地址:

minikube ip

假设结果是 192.168.49.2。应用的完整地址就是 http://192.168.49.2:30008。

我们可以使用 curl 访问这个地址,验证应用是否响应:

curl http://$(minikube ip):30008

你应该会看到「Welcome to nginx!」页面的 HTML 代码。

这说明请求完整地经过了以下路径:

  1. 你访问了 Node 的外部端口(30008)。
  2. Service 接收了请求。
  3. 它检查了自己的 Selector(app: web-app)。
  4. 它将请求转发给了带有该标签的某个 Pod。
  5. Pod 返回了网页。

总结

在本实验中,你使用 Service 解决了 Pod IP 地址不断变化的问题。

你已经成功完成以下操作:

  1. 创建了一个 NodePort Service(service.yaml),为应用打开访问入口。
  2. 配置了 Selector,告诉 Service 应该管理哪些 Pod。
  3. 使用稳定地址从外部访问了正在运行的应用。

Service 是连接 Kubernetes 应用各部分的纽带,使系统的不同部分能够可靠地相互通信。