【Docker-Day 26】K8s实战演练:从零开始部署一个完整的前后端分离Web应用
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 的资源对象上。我们的目标是在集群中清晰、可靠地运行这个应用。
-
后端服务:
- 工作负载: 我们将使用
Deployment来管理后端应用的 Pod。这能确保我们的 API 服务始终按预期的副本数运行,并支持滚动更新。 - 网络: 后端服务仅需被前端调用,不需要直接暴露给集群外部的用户。因此,我们将为其创建一个
ClusterIP类型的Service。这个 Service 会提供一个稳定的、仅限集群内部访问的虚拟 IP 和 DNS 名称,前端应用可以通过这个 DNS 名称来发现和访问后端。
- 工作负载: 我们将使用
-
前端服务:
- 工作负载: 同样,我们使用
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 返回的数据。这直接证明了:
- 前端
NodePortService 工作正常,将外部流量导入了前端 Pod。 - 前端 Pod 内的应用成功通过
backend-service这个 DNS 名称解析并连接到了后端ClusterIPService。 - 后端
ClusterIPService 成功将流量负载均衡到了某个后端 Pod。
4.2 常见问题与排查思路
(1) 问题一:前端页面无法连接后端 (显示错误信息)
排查思路:
- 确认 Service 名称: 检查前端应用的配置或代码,确保它连接的后端地址是
http://backend-service,与后端 Service 的metadata.name完全一致。 - 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 无法拉取镜像。
- 检查镜像名称: 确认
.yaml文件中spec.containers.image的值是否正确,有无拼写错误。 - 查看 Pod 详情: 使用
kubectl describe pod <pod-name>查看Events部分,会有详细的错误信息,例如 “image not found” 或 “permission denied”。 - 网络问题: 确保您的节点可以访问您指定的镜像仓库。
(3) 问题三:Pod 状态为 CrashLoopBackOff
排查思路:
这表示容器启动后立即崩溃,Kubelet 不断尝试重启它。
- 查看日志: 这是最重要的排查手段。
# 查看当前这次失败的日志 kubectl logs <pod-name> # 如果Pod重启太快,查看上一次失败的日志 kubectl logs --previous <pod-name> - 检查配置: 日志通常会告诉您为什么应用无法启动,可能是配置文件缺失、环境变量错误、端口冲突等。
五、总结
恭喜您!通过本次实战,您已经成功地在 Kubernetes 上部署并运行了一个完整的多层应用。让我们回顾一下核心收获:
Deployment是无状态应用管理的基石:我们使用Deployment来声明式地管理前后端应用的副本数、镜像版本和更新策略,极大地简化了运维工作。Service是服务发现与网络访问的核心:我们为后端创建了ClusterIP类型的 Service,实现了可靠的内部服务发现与负载均衡;为前端创建了NodePort类型的 Service,将应用安全地暴露给外部用户。- K8s DNS 是服务间通信的桥梁:前端应用无需关心后端 Pod 的具体 IP 地址和生命周期,只需通过 K8s 提供的稳定 Service DNS 名称 (
backend-service) 即可通信,完美实现了服务解耦。 - 声明式 API 的威力:我们通过编写 YAML 文件来“声明”我们期望的最终状态,Kubernetes 会自动地、持续地工作以达成这个状态。这是 Kubernetes 强大自愈能力和易于管理的基础。
现在,您不仅理解了这些核心概念,更重要的是,您拥有了将它们组合起来解决实际问题的能力。这是从入门到精通的关键一步。在接下来的文章中,我们将继续探索 K8s 更高级的特性,如健康检查、配置管理等,让您的应用更加健壮。
更多推荐


所有评论(0)