“后端服务+前端页面服务 + 后端数据库服务“如何部署到K8s集群
将 “后端服务 + 前端页面服务 + 后端数据库服务” 部署到 K8s 集群,核心是通过K8s 核心资源(Deployment、Service、Ingress 等) 实现组件解耦、弹性伸缩和统一调度,同时需兼顾数据持久化、网络访问、配置管理等关键需求。以下是分步骤的完整部署方案,含资源清单示例和核心注意事项。
一、部署前准备
1. 环境前提
- 已搭建可用的 K8s 集群(单节点 / 多节点均可,生产环境建议≥3 节点,需支持
StorageClass用于数据持久化); - 本地安装
kubectl并配置集群访问权限(kubectl config use-context <集群名称>); - 所有服务已打包为 Docker 镜像(前端、后端、数据库),并推送到镜像仓库(如 Docker Hub、Harbor、阿里云 ACR 等);
- 可选:安装 Ingress 控制器(如 Nginx Ingress),用于统一入口访问(生产环境必备)。
2. 核心组件规划
| 组件 | K8s 资源类型 | 核心需求 |
|---|---|---|
| 前端页面服务 | Deployment(部署)+ Service(暴露)+ Ingress(域名访问) | 静态资源(HTML/CSS/JS),无状态,需反向代理 |
| 后端服务 | Deployment(部署)+ Service(暴露) | 无状态(建议),需连接数据库,需环境变量配置 |
| 后端数据库 | StatefulSet(有状态部署)+ Service(无头服务 / 集群 IP)+ PersistentVolumeClaim(数据持久化) | 有状态,数据需持久化,固定网络标识 |
数据库用StatefulSet而非Deployment:因为数据库是有状态服务(数据与实例绑定、需固定网络名称),StatefulSet能保证 Pod 名称、主机名固定,且与PVC一一对应,避免数据丢失。
二、分步部署实现
第一步:部署后端数据库服务(以 MySQL 为例)
1. 数据持久化:创建 PVC(绑定存储)
先通过PersistentVolumeClaim(PVC)申请存储资源(依赖集群已存在StorageClass,如standard、nfs-storage等)。
创建mysql-pvc.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: mysql-pvc # PVC名称,供StatefulSet引用namespace: app-namespace # 建议创建独立命名空间隔离应用
spec:accessModes:- ReadWriteOnce # 仅单节点读写(数据库通常单实例,集群化需调整)resources:requests:storage: 10Gi # 申请10GB存储storageClassName: "standard" # 替换为集群实际的StorageClass名称(kubectl get sc查看)
应用 PVC:
bash
kubectl apply -f mysql-pvc.yaml
2. 配置管理:创建 ConfigMap/Secret(存储配置和密码)
Secret:存储敏感信息(如数据库密码,Base64 编码);ConfigMap:存储非敏感配置(如数据库连接参数、字符集)。
(1)创建 Secret 存储数据库密码
先将密码编码(例如密码Root@123456):
bash
echo -n "Root@123456" | base64 # 输出:Um9vdEAxMjM0NTY=创建mysql-secret.yaml:
yaml
apiVersion: v1
kind: Secret
metadata:name: mysql-secretnamespace: app-namespace
type: Opaque
data:mysql-root-password: Um9vdEAxMjM0NTY= # Base64编码后的密码mysql-user-password: Um9vdEAxMjM0NTY= # 普通用户密码(可选)
应用 Secret 和 ConfigMap:
kubectl apply -f mysql-secret.yaml -f mysql-configmap.yaml3. 部署数据库:创建 StatefulSet 和 Service
创建mysql-statefulset.yaml:
apiVersion: apps/v1
kind: StatefulSet
metadata:name: mysqlnamespace: app-namespace
spec:serviceName: mysql-service # 关联的无头Service名称(固定网络标识)replicas: 1 # 单实例(生产环境可集群化,需调整)selector:matchLabels:app: mysqltemplate:metadata:labels:app: mysqlspec:containers:- name: mysqlimage: mysql:8.0 # 镜像地址(替换为你的私有仓库镜像,如harbor.com/mysql:8.0)ports:- containerPort: 3306 # MySQL默认端口env:# 从ConfigMap读取非敏感配置- name: MYSQL_DATABASEvalueFrom:configMapKeyRef:name: mysql-configkey: MYSQL_DATABASE- name: MYSQL_USERvalueFrom:configMapKeyRef:name: mysql-configkey: MYSQL_USER# 从Secret读取敏感配置- name: MYSQL_ROOT_PASSWORDvalueFrom:secretKeyRef:name: mysql-secretkey: mysql-root-password- name: MYSQL_PASSWORDvalueFrom:secretKeyRef:name: mysql-secretkey: mysql-user-password# 挂载PVC(数据持久化)volumeMounts:- name: mysql-datamountPath: /var/lib/mysql # MySQL数据存储目录# 健康检查livenessProbe:exec:command: ["mysqladmin", "ping", "-uroot", "-p$(MYSQL_ROOT_PASSWORD)"]initialDelaySeconds: 30periodSeconds: 10readinessProbe:exec:command: ["mysql", "-uroot", "-p$(MYSQL_ROOT_PASSWORD)", "-e", "SELECT 1"]initialDelaySeconds: 5periodSeconds: 5# 关联PVC(与StatefulSet名称+volume名称匹配,自动绑定mysql-pvc)volumeClaimTemplates: [] # 若已手动创建PVC,此处留空;若自动创建,需配置spec# 手动绑定已创建的PVC(关键)volumes:- name: mysql-datapersistentVolumeClaim:claimName: mysql-pvc # 引用第一步创建的PVC
---
# 无头Service(供后端服务内部访问,固定DNS:mysql-service.app-namespace.svc.cluster.local)
apiVersion: v1
kind: Service
metadata:name: mysql-servicenamespace: app-namespace
spec:clusterIP: None # 无头服务(无集群IP,通过DNS解析Pod IP)selector:app: mysqlports:- port: 3306targetPort: 3306
应用 StatefulSet 和 Service:
kubectl apply -f mysql-statefulset.yaml验证数据库部署
# 查看Pod状态(Running表示正常)
kubectl get pods -n app-namespace -l app=mysql
# 查看PVC绑定状态(Bound表示成功)
kubectl get pvc -n app-namespace
# 进入Pod测试数据库连接
kubectl exec -it mysql-0 -n app-namespace -- mysql -uroot -pRoot@123456第二步:部署后端服务(以 Java SpringBoot 为例)
后端服务为无状态服务,用Deployment部署,通过Service暴露内部访问,依赖数据库的无头 Service(mysql-service.app-namespace.svc.cluster.local)。
1. 创建后端配置(ConfigMap)
创建backend-configmap.yaml:
apiVersion: v1
kind: ConfigMap
metadata:name: backend-confignamespace: app-namespace
data:SPRING_DATASOURCE_URL: "jdbc:mysql://mysql-service.app-namespace.svc.cluster.local:3306/app_db?useSSL=false&serverTimezone=Asia/Shanghai"SPRING_DATASOURCE_USERNAME: "app_user"SPRING_PROFILES_ACTIVE: "prod" # 环境标识2. 创建后端 Deployment 和 Service
创建backend-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:name: backend-servicenamespace: app-namespace
spec:replicas: 2 # 2个副本(弹性伸缩)selector:matchLabels:app: backendtemplate:metadata:labels:app: backendspec:containers:- name: backendimage: your-registry/backend-app:v1.0 # 替换为你的后端镜像地址ports:- containerPort: 8080 # 后端服务端口(如SpringBoot默认8080)env:# 从ConfigMap读取非敏感配置- name: SPRING_DATASOURCE_URLvalueFrom:configMapKeyRef:name: backend-configkey: SPRING_DATASOURCE_URL- name: SPRING_DATASOURCE_USERNAMEvalueFrom:configMapKeyRef:name: backend-configkey: SPRING_DATASOURCE_USERNAME# 从Secret读取数据库密码(复用mysql-secret)- name: SPRING_DATASOURCE_PASSWORDvalueFrom:secretKeyRef:name: mysql-secretkey: mysql-user-password# 健康检查livenessProbe:httpGet:path: /actuator/health # SpringBoot健康检查端点(需开启actuator)port: 8080initialDelaySeconds: 60periodSeconds: 10readinessProbe:httpGet:path: /actuator/healthport: 8080initialDelaySeconds: 30periodSeconds: 5resources:limits:cpu: "1"memory: "1Gi"requests:cpu: "500m"memory: "512Mi"
---
# 后端Service(ClusterIP类型,仅集群内部访问)
apiVersion: v1
kind: Service
metadata:name: backend-servicenamespace: app-namespace
spec:type: ClusterIPselector:app: backendports:- port: 8080targetPort: 8080
应用配置和部署:
bash
kubectl apply -f backend-configmap.yaml -f backend-deployment.yaml
验证后端部署:
bash
# 查看Pod状态
kubectl get pods -n app-namespace -l app=backend
# 测试后端服务(集群内部访问)
kubectl exec -it <任意前端Pod名称> -n app-namespace -- curl http://backend-service:8080/actuator/health第三步:部署前端页面服务(以 Vue/React 静态资源为例)
前端为静态资源,通常用 Nginx 镜像打包(将静态文件放入 Nginx 的/usr/share/nginx/html目录),通过Ingress暴露外网访问,并配置反向代理指向后端 Service。
1. 前端镜像打包(关键步骤)
需先将前端代码打包为 Docker 镜像,示例Dockerfile:
# 构建阶段(用Node镜像打包静态资源)
FROM node:16-alpine as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build # 打包静态资源到dist目录# 运行阶段(用Nginx镜像部署)
FROM nginx:alpine
# 复制打包后的静态资源到Nginx
COPY --from=build /app/dist /usr/share/nginx/html
# 替换Nginx配置(配置反向代理到后端)
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]其中nginx.conf(核心是反向代理后端服务):
server {listen 80;server_name localhost;root /usr/share/nginx/html;index index.html;# 前端路由刷新404问题(SPA应用必备)location / {try_files $uri $uri/ /index.html;}# 反向代理后端接口(前端请求/api/*转发到后端Service)location /api/ {proxy_pass http://backend-service.app-namespace.svc.cluster.local:8080/;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;}
}构建并推送镜像:
bash
docker build -t your-registry/frontend-app:v1.0 .
docker push your-registry/frontend-app:v1.02. 部署前端 Deployment 和 Service
创建frontend-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:name: frontend-servicenamespace: app-namespace
spec:replicas: 2 # 2个副本(高可用)selector:matchLabels:app: frontendtemplate:metadata:labels:app: frontendspec:containers:- name: frontendimage: your-registry/frontend-app:v1.0 # 替换为你的前端镜像地址ports:- containerPort: 80resources:limits:cpu: "500m"memory: "512Mi"requests:cpu: "200m"memory: "256Mi"
---
# 前端Service(ClusterIP类型,供Ingress转发)
apiVersion: v1
kind: Service
metadata:name: frontend-servicenamespace: app-namespace
spec:type: ClusterIPselector:app: frontendports:- port: 80targetPort: 80
应用部署:
bash
kubectl apply -f frontend-deployment.yaml第四步:配置 Ingress(统一入口访问)
通过 Ingress 实现外网访问前端页面,同时转发后端接口(可选,也可让前端直接通过 Ingress 访问后端)。
1. 确保 Ingress 控制器已安装
若未安装,以 Nginx Ingress 为例(K8s 1.24+):
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.0/deploy/static/provider/cloud/deploy.yaml
验证 Ingress 控制器:
bash
kubectl get pods -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx2. 创建 Ingress 资源
创建app-ingress.yaml:
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: app-ingressnamespace: app-namespaceannotations:# Nginx Ingress注解(可选,如开启gzip、设置缓存)nginx.ingress.kubernetes.io/gzip-enabled: "true"nginx.ingress.kubernetes.io/ssl-redirect: "false" # 测试环境关闭HTTPS,生产环境开启
spec:ingressClassName: nginx # 关联Ingress控制器(必须,kubectl get ingressclass查看)rules:# 前端页面访问规则(如通过app.example.com访问)- host: app.example.com # 替换为你的域名(本地测试可修改/etc/hosts绑定Ingress控制器IP)http:paths:- path: /pathType: Prefixbackend:service:name: frontend-serviceport:number: 80# 后端接口访问规则(可选,如通过api.example.com访问)- host: api.example.comhttp:paths:- path: /pathType: Prefixbackend:service:name: backend-serviceport:number: 8080
应用 Ingress:
bash
kubectl apply -f app-ingress.yaml
验证 Ingress:
bash
kubectl get ingress -n app-namespace
# 查看Ingress地址(ADDRESS列,即Ingress控制器的IP)
3. 访问测试
- 本地测试:修改
/etc/hosts(Linux/Mac)或C:\Windows\System32\drivers\etc\hosts(Windows),添加:plaintext
<Ingress控制器IP> app.example.com api.example.com - 浏览器访问
http://app.example.com:看到前端页面,且接口请求(/api/*)能正常转发到后端,连接数据库返回数据。
三、核心注意事项
1. 数据持久化
- 数据库必须用
StatefulSet + PVC,避免 Pod 重建导致数据丢失; - 生产环境建议使用分布式存储(如 GlusterFS、Ceph)或云厂商存储(如 AWS EBS、阿里云 OSS),确保高可用。
2. 网络访问
- 内部服务通信优先使用
Service名称.命名空间.svc.cluster.local(K8s 内部 DNS 解析),避免硬编码 Pod IP; - Ingress 仅暴露必要端口(80/443),生产环境需配置 HTTPS(添加
tls字段,引用 Secret 存储证书)。
3. 配置与敏感信息
- 非敏感配置(如数据库地址、环境变量)用
ConfigMap,敏感信息(密码、密钥)用Secret; - 生产环境建议使用
SealedSecret或云厂商密钥管理服务(如 AWS KMS),避免 Secret 明文泄露。
4. 高可用与弹性伸缩
- 后端、前端服务为无状态,可通过
kubectl scale deployment <名称> --replicas=3扩展副本数; - 数据库单实例风险高,生产环境可部署 MySQL 集群(如主从复制)或使用云数据库(RDS),K8s 仅部署应用层。
5. 监控与日志
- 部署 Prometheus + Grafana 监控 Pod、Service 状态(CPU / 内存 / 网络);
- 部署 ELK Stack(Elasticsearch + Logstash + Kibana)收集 Pod 日志,便于问题排查。
四、常用运维命令
bash
# 查看命名空间下所有资源
kubectl get all -n app-namespace# 查看Pod日志(后端为例)
kubectl logs -f <backend-pod-name> -n app-namespace# 进入Pod调试
kubectl exec -it <pod-name> -n app-namespace -- /bin/bash# 重启Deployment(更新配置后)
kubectl rollout restart deployment backend-service -n app-namespace# 查看PVC使用情况
kubectl describe pvc mysql-pvc -n app-namespace