Langchain系列文章目录

01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来

Python系列文章目录

PyTorch系列文章目录

机器学习系列文章目录

深度学习系列文章目录

Java系列文章目录

JavaScript系列文章目录

Python系列文章目录

Go语言系列文章目录

Docker系列文章目录

01-【Docker-Day 1】告别部署噩梦:为什么说 Docker 是每个开发者的必备技能?
02-【Docker-Day 2】从零开始:手把手教你在 Windows、macOS 和 Linux 上安装 Docker
03-【Docker-Day 3】深入浅出:彻底搞懂 Docker 的三大核心基石——镜像、容器与仓库
04-【Docker-Day 4】从创建到删除:一文精通 Docker 容器核心操作命令
05-【Docker-Day 5】玩转 Docker 镜像:search, pull, tag, rmi 四大金刚命令详解
06-【Docker-Day 6】从零到一:精通 Dockerfile 核心指令 (FROM, WORKDIR, COPY, RUN)
07-【Docker-Day 7】揭秘 Dockerfile 启动指令:CMD、ENTRYPOINT、ENV、ARG 与 EXPOSE 详解
08-【Docker-Day 8】高手进阶:构建更小、更快、更安全的 Docker 镜像
09-【Docker-Day 9】实战终极指南:手把手教你将 Node.js 应用容器化
10-【Docker-Day 10】容器的“持久化”记忆:深入解析 Docker 数据卷 (Volume)
11-【Docker-Day 11】Docker 绑定挂载 (Bind Mount) 实战:本地代码如何与容器实时同步?
12-【Docker-Day 12】揭秘容器网络:深入理解 Docker Bridge 模式与端口映射
13-【Docker-Day 13】超越默认Bridge:精通Docker Host、None与自定义网络模式
14-【Docker-Day 14】Docker Compose深度解析
15-【Docker-Day 15】一键部署 WordPress!Docker Compose 实战终极指南
16-【Docker-Day 16】告别单机时代:为什么 Docker Compose 不够用,而你需要 Kubernetes?
17-【Docker-Day 17】K8s 架构全解析:深入理解 Kubernetes 的大脑 (Master) 与四肢 (Node)
18-【Docker-Day 18】告别选择困难症:一文掌握 Minikube、kind、k3d,轻松搭建你的第一个 K8s 集群
19-【Docker-Day 19】万物皆 YAML:掌握 Kubernetes 声明式 API 的艺术
20-【Docker-Day 20】揭秘 Kubernetes 的原子单位:深入理解 Pod
21-【Docker-Day 21】Pod的守护神:ReplicaSet与ReplicationController,轻松实现应用高可用
22-【K8s-Day 22】深入解析 Kubernetes Deployment:现代应用部署的基石与滚动更新的艺术
23-【K8s-Day 23】从 Pod 的“失联”到 Service 的“牵线”:深入理解 ClusterIP 核心原理
24-【Docker-Day 24】K8s网络解密:深入NodePort与LoadBalancer,让你的应用走出集群
25-【Docker-Day 25】深入理解 Kubernetes Namespace:实现多租户与环境隔离的利器
26-【Docker-Day 26】K8s实战演练:从零开始部署一个完整的前后端分离Web应用



摘要

在前面的章节中,我们已经系统学习了 Kubernetes 的核心构建块,如 Pod、Deployment 和 Service。理论知识是基石,但只有通过实践才能真正将其内化。本文是您将理论付诸实践的绝佳机会。我们将手把手地带领您,从零开始在一个 Kubernetes 集群中,部署一个完整的前后端分离 Web 应用。通过这个实战项目,您将深刻理解 Deployment 如何管理应用生命周期,Service 如何实现服务发现与负载均衡,以及前后端服务如何在 K8s 的世界里协同工作。这不仅仅是一次操作演练,更是一次对 K8s 设计哲学的深度体验。

一、项目背景与架构概览

在开始编写 YAML 文件之前,我们必须先明确目标。一个清晰的蓝图是成功部署的第一步。

1.1 定义我们的目标应用

我们将部署一个经典的前后端分离 Web 应用。这种架构在现代软件开发中非常普遍,其核心思想是将用户界面(前端)和业务逻辑(后端)解耦。

  • 前端 (Frontend): 一个基于 React/Vue/Angular 的单页应用(SPA),负责渲染用户界面和与用户交互。它本身不处理业务逻辑,而是通过 HTTP API 调用后端服务。
  • 后端 (Backend): 一个提供 RESTful API 的服务,负责处理业务逻辑、数据持久化等。它可以是任何语言编写的,如 Node.js、Go、Python 或 Java。

为了专注于 Kubernetes 的部署,我们将使用已经构建好的公开镜像,避免在应用开发和 Dockerfile 编写上花费时间。

1.2 Kubernetes 中的实现思路

现在,让我们将上述架构映射到 Kubernetes 的资源对象上。我们的目标是在集群中清晰、可靠地运行这个应用。

  1. 后端服务:

    • 工作负载: 我们将使用 Deployment 来管理后端应用的 Pod。这能确保我们的 API 服务始终按预期的副本数运行,并支持滚动更新。
    • 网络: 后端服务仅需被前端调用,不需要直接暴露给集群外部的用户。因此,我们将为其创建一个 ClusterIP 类型的 Service。这个 Service 会提供一个稳定的、仅限集群内部访问的虚拟 IP 和 DNS 名称,前端应用可以通过这个 DNS 名称来发现和访问后端。
  2. 前端服务:

    • 工作负载: 同样,我们使用 Deployment 来管理前端应用的 Pod。
    • 网络: 前端是用户访问的入口,必须从集群外部可以访问。因此,我们将为其创建一个 NodePort 类型的 Service。这会在集群的每个节点上开放一个相同的端口,用户通过 [NodeIP]:[NodePort] 即可访问我们的应用。

下图清晰地展示了我们的部署架构:

graph TD
    subgraph Kubernetes Cluster
        subgraph Frontend
            F_Service[Service: NodePort] -->|selects| F_Pod1(Pod: Frontend)
            F_Service -->|selects| F_Pod2(Pod: Frontend)
            F_Deployment(Deployment) -.-> F_Pod1
            F_Deployment -.-> F_Pod2
        end
        
        subgraph Backend
            B_Service[Service: ClusterIP] -->|selects| B_Pod1(Pod: Backend)
            B_Service -->|selects| B_Pod2(Pod: Backend)
            B_Deployment(Deployment) -.-> B_Pod1
            B_Deployment -.-> B_Pod2
        end

        F_Pod1 -->|HTTP API Call via Service DNS| B_Service
        F_Pod2 -->|HTTP API Call via Service DNS| B_Service
    end

    User(👤 User) -->|访问 [NodeIP]:[NodePort]| F_Service

二、部署后端服务

让我们从后端开始,它是整个应用的数据和逻辑核心。

2.1 准备后端镜像

为简化流程,我们使用一个公开的、简单的 Go API 服务镜像 registry.cn-hangzhou.aliyuncs.com/google_containers/echoserver:1.10。这个服务会回显(echo)收到的请求信息,非常适合用于测试。它默认监听在 8080 端口。

2.2 编写后端 Deployment

创建一个名为 backend-deployment.yaml 的文件,并填入以下内容。这个 Deployment 声明了我们希望运行 2 个后端的 Pod 副本。

# backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-deployment
  labels:
    app: backend
spec:
  replicas: 2 # 声明我们期望运行2个Pod副本
  selector:
    matchLabels:
      app: backend # 选择器,用于找到要管理的Pod
  template: # Pod模板,定义了要创建的Pod的规格
    metadata:
      labels:
        app: backend # Pod的标签,必须与上面的selector匹配
    spec:
      containers:
      - name: backend-container
        image: registry.cn-hangzhou.aliyuncs.com/google_containers/echoserver:1.10 # 使用的镜像
        ports:
        - containerPort: 8080 # 容器内应用监听的端口

2.2.1 关键字段解析

  • replicas: 2:K8s 会始终确保有两个标记为 app: backend 的 Pod 在运行。
  • selector.matchLabels:这是 Deployment 和它管理的 Pod 之间的纽带。Deployment 通过这个选择器来识别哪些 Pod 属于自己。
  • template.metadata.labels:这是 Pod 模板的一部分,所有由此模板创建的 Pod 都会带上这个标签 (app: backend),从而被 selector 匹配到。

2.3 编写后端 Service

接下来,为后端 Deployment 创建一个稳定的访问入口。创建一个名为 backend-service.yaml 的文件。

# backend-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: backend-service # Service的名称,将作为内部DNS记录
spec:
  type: ClusterIP # Service类型,仅限集群内部访问
  selector:
    app: backend # 选择器,将流量转发到带有此标签的Pod
  ports:
    - protocol: TCP
      port: 80 # Service暴露的端口
      targetPort: 8080 # 流量要转发到的目标Pod的端口

2.3.1 关键字段解析

  • name: backend-service:这是至关重要的一点。在 K8s 集群内部,其他 Pod 可以通过 http://backend-service 这个 DNS 名称来访问该服务。
  • type: ClusterIP:明确指出这是一个内部服务。
  • selector.app: backend:Service 通过这个标签选择器,找到所有后端 Pod (app: backend),并将流量负载均衡到它们之上。
  • port: 80:Service 自身监听的端口。
  • targetPort: 8080:后端容器实际监听的端口。流量路径为:前端 Pod -> backend-service:80 -> 后端 Pod:8080

2.4 部署与验证

现在,使用 kubectl 将这两个资源对象应用到集群中。

# 部署后端 Deployment 和 Service
kubectl apply -f backend-deployment.yaml
kubectl apply -f backend-service.yaml

检查部署状态:

# 查看 Deployment 状态,确保 AVAILABLE 是 2
$ kubectl get deployment backend-deployment
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE
backend-deployment   2/2     2            2           30s

# 查看 Pod 状态,应该有两个 running 的 Pod
$ kubectl get pods -l app=backend
NAME                                  READY   STATUS    RESTARTS   AGE
backend-deployment-6b8f8f9c8b-abcde   1/1     Running   0          45s
backend-deployment-6b8f8f9c8b-fghij   1/1     Running   0          45s

# 查看 Service 状态,获取 ClusterIP
$ kubectl get service backend-service
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
backend-service   ClusterIP   10.108.110.120   <none>        80/TCP    60s

至此,我们的后端服务已经在集群中稳定运行了。

三、部署前端服务

有了后端,我们现在可以部署面向用户的前端了。

3.1 准备前端镜像与配置

为了演示前后端交互,我们使用一个特别准备的镜像 kodekloud/simple-webapp-frontend。这个应用会尝试连接一个名为 backend-service 的后端。

最关键的一点是,前端应用代码中访问后端的地址不是硬编码的 IP,而是后端 Service 的名称,即 http://backend-service。这就是 Kubernetes 服务发现的魔力!

3.2 编写前端 Deployment

创建一个名为 frontend-deployment.yaml 的文件。

# frontend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-deployment
  labels:
    app: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend-container
        image: kodekloud/simple-webapp-frontend # 使用特制的前端镜像
        ports:
        - containerPort: 80 # 前端应用在容器内监听80端口

这个 Deployment 的结构与后端类似,只是标签和镜像不同。

3.3 编写前端 Service

为了让外部用户能访问到前端应用,我们使用 NodePort 类型的 Service。创建一个名为 frontend-service.yaml 的文件。

# frontend-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: NodePort # Service类型,暴露到集群外部
  selector:
    app: frontend # 选择带有 app: frontend 标签的Pod
  ports:
    - protocol: TCP
      port: 80 # Service 在集群内部监听的端口
      targetPort: 80 # 流量转发到前端Pod的80端口
      nodePort: 30080 # 在每个Node上暴露的静态端口(可选,不写会自动分配)

3.3.1 关键字段解析

  • type: NodePort:告诉 Kubernetes 在所有节点上打开一个端口,并将流量转发到这个 Service。
  • nodePort: 30080:我们静态指定了节点端口为 30080。如果不指定,K8s 会在 30000-32767 范围内自动分配一个。静态指定便于我们记忆和访问。

3.4 部署与访问

部署前端资源:

# 部署前端 Deployment 和 Service
kubectl apply -f frontend-deployment.yaml
kubectl apply -f frontend-service.yaml

检查所有资源的状态:

$ kubectl get all
NAME                                      READY   STATUS    RESTARTS   AGE
pod/backend-deployment-6b8f8f9c8b-abcde   1/1     Running   0          10m
pod/backend-deployment-6b8f8f9c8b-fghij   1/1     Running   0          10m
pod/frontend-deployment-7d5dcf5f6f-klmno   1/1     Running   0          2m
pod/frontend-deployment-7d5dcf5f6f-pqrst   1/1     Running   0          2m

NAME                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
service/backend-service   ClusterIP   10.108.110.120   <none>        80/TCP         10m
service/frontend-service  NodePort    10.102.1.200     <none>        80:30080/TCP   2m

NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/backend-deployment    2/2     2            2           10m
deployment.apps/frontend-deployment   2/2     2            2           2m

# ... ReplicaSet objects ...

要访问应用,您需要获取任意一个工作节点的 IP 地址。如果您使用 Minikube,可以通过以下命令获取:

$ minikube ip
192.168.49.2

现在,在您的浏览器中打开 http://<Node-IP>:<NodePort>,即 http://192.168.49.2:30080。您应该能看到一个简单的网页,它显示了从后端服务获取到的信息!

四、联调与排错

部署成功只是第一步,理解如何排查问题才能让您在生产环境中游刃有余。

4.1 验证前后端通信

当您访问前端页面时,页面上显示的内容(如 “Request served by…”)就是前端 Pod 调用后端 Service (backend-service) 后,由后端 Pod 返回的数据。这直接证明了:

  1. 前端 NodePort Service 工作正常,将外部流量导入了前端 Pod。
  2. 前端 Pod 内的应用成功通过 backend-service 这个 DNS 名称解析并连接到了后端 ClusterIP Service。
  3. 后端 ClusterIP Service 成功将流量负载均衡到了某个后端 Pod。

4.2 常见问题与排查思路

(1) 问题一:前端页面无法连接后端 (显示错误信息)

排查思路:

  1. 确认 Service 名称: 检查前端应用的配置或代码,确保它连接的后端地址是 http://backend-service,与后端 Service 的 metadata.name 完全一致。
  2. DNS 解析测试: 进入一个前端 Pod 内部,手动测试网络连通性。
    # 获取一个前端Pod的名称
    FRONTEND_POD=$(kubectl get pods -l app=frontend -o jsonpath='{.items[0].metadata.name}')
    
    # 进入Pod内部的shell
    kubectl exec -it $FRONTEND_POD -- /bin/sh
    
    # 在Pod内部,使用curl测试后端Service (需要Pod内有curl工具)
    # / # curl http://backend-service
    # 如果返回后端服务的响应,说明K8s网络层面是通的,问题在前端应用代码。
    # 如果超时或无法解析,说明可能是网络策略或DNS问题。
    

(2) 问题二:Pod 状态为 ImagePullBackOff

排查思路:

这表示 Kubelet 无法拉取镜像。

  1. 检查镜像名称: 确认 .yaml 文件中 spec.containers.image 的值是否正确,有无拼写错误。
  2. 查看 Pod 详情: 使用 kubectl describe pod <pod-name> 查看 Events 部分,会有详细的错误信息,例如 “image not found” 或 “permission denied”。
  3. 网络问题: 确保您的节点可以访问您指定的镜像仓库。

(3) 问题三:Pod 状态为 CrashLoopBackOff

排查思路:

这表示容器启动后立即崩溃,Kubelet 不断尝试重启它。

  1. 查看日志: 这是最重要的排查手段。
    # 查看当前这次失败的日志
    kubectl logs <pod-name>
    
    # 如果Pod重启太快,查看上一次失败的日志
    kubectl logs --previous <pod-name>
    
  2. 检查配置: 日志通常会告诉您为什么应用无法启动,可能是配置文件缺失、环境变量错误、端口冲突等。

五、总结

恭喜您!通过本次实战,您已经成功地在 Kubernetes 上部署并运行了一个完整的多层应用。让我们回顾一下核心收获:

  1. Deployment 是无状态应用管理的基石:我们使用 Deployment 来声明式地管理前后端应用的副本数、镜像版本和更新策略,极大地简化了运维工作。
  2. Service 是服务发现与网络访问的核心:我们为后端创建了 ClusterIP 类型的 Service,实现了可靠的内部服务发现与负载均衡;为前端创建了 NodePort 类型的 Service,将应用安全地暴露给外部用户。
  3. K8s DNS 是服务间通信的桥梁:前端应用无需关心后端 Pod 的具体 IP 地址和生命周期,只需通过 K8s 提供的稳定 Service DNS 名称 (backend-service) 即可通信,完美实现了服务解耦。
  4. 声明式 API 的威力:我们通过编写 YAML 文件来“声明”我们期望的最终状态,Kubernetes 会自动地、持续地工作以达成这个状态。这是 Kubernetes 强大自愈能力和易于管理的基础。

现在,您不仅理解了这些核心概念,更重要的是,您拥有了将它们组合起来解决实际问题的能力。这是从入门到精通的关键一步。在接下来的文章中,我们将继续探索 K8s 更高级的特性,如健康检查、配置管理等,让您的应用更加健壮。


Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐