前后端分离已经成为目前Web项目的主流架构方式,前端负责页面渲染和交互,后端提供API接口,两者独立开发、独立部署。但这种架构在部署阶段会带来一个现实问题:一次完整上线往往涉及前端静态资源、后端服务、数据库、缓存、反向代理等多个组件,如果每个组件都靠手动敲命令启动容器,不仅繁琐,而且重启顺序、网络连通、环境变量传递都容易出错。容器编排就是解决这个问题的手段,它把多容器的启动、依赖、网络、生命周期统一交给工具管理。本文围绕前后端分离项目,详细讲解镜像构建思路以及Docker Compose与Kubernetes两种编排方案的具体做法。

一、前后端镜像的构建思路差异
前端项目和后端项目在容器化时的处理方式完全不同,理解这个差异是编排的第一步。以Vue或React项目为例,构建产物是一堆静态文件(HTML、CSS、JS),容器里其实不需要跑Node进程,只需要一个Nginx或Caddy来托管这些静态资源。所以前端镜像通常采用多阶段构建:第一阶段用Node镜像执行npm run build生成dist目录,第二阶段把dist拷贝到Nginx镜像中,最终镜像体积可以从一千多兆压缩到几十兆。
后端以Spring Boot为例,虽然也可以直接把fat jar扔进JRE镜像,但更推荐同样使用多阶段构建,第一阶段用Maven或Gradle镜像编译打包,第二阶段只保留JRE和最终jar。这样构建环境中的源码、依赖缓存都不会进入最终镜像,既减小体积又降低安全风险。
# 前端多阶段构建示例 FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM nginx:1.27-alpine COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80
有一个容易被忽视的细节:前端项目打包时会把API地址写死在构建参数里,比如VITE_API_BASE_URL。如果测试环境和生产环境地址不同,就需要为每个环境单独构建镜像,违背了“一次构建到处运行”的原则。常见的解决办法是让前端在运行时读取一个配置文件,由Nginx在容器启动时通过入口脚本替换其中的占位符,这样同一个镜像就能适配不同环境。
二、用Docker Compose完成单机编排
对于中小型项目或者开发测试环境,Docker Compose是最合适的工具。它用一个YAML文件描述所有服务,一条docker compose up -d就能把前端、后端、数据库、缓存全部拉起来。Compose会自动创建一个独立的桥接网络,各服务之间通过服务名作为主机名互相访问,例如后端配置数据库地址时直接写jdbc:mysql://mysql:3306/mydb即可,不需要关心容器IP。
依赖顺序是编排中的关键点。后端启动时通常要求数据库已经就绪,但depends_on默认只保证容器启动顺序,不保证服务可用。Compose提供了condition: service_healthy配合健康检查来解决这一问题,让后端等到MySQL真正可以接受连接后再启动,避免启动阶段连接失败导致初始化脚本中断。
services:
nginx:
image: myapp-frontend:latest
ports:
- "80:80"
depends_on:
- backend
backend:
image: myapp-backend:latest
environment:
- SPRING_PROFILES_ACTIVE=prod
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/mydb
- SPRING_REDIS_HOST=redis
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=root123
- MYSQL_DATABASE=mydb
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
retries: 5
redis:
image: redis:7-alpine
volumes:
mysql-data:反向代理层建议独立出来,用一个单独的Nginx容器统一接收外部流量:静态资源请求直接代理到前端容器,/api开头的请求转发到后端容器。这样前端容器只负责静态文件,代理规则集中管理,后续加网关、限流也只改一处配置。环境变量注入推荐通过.env文件管理,数据库密码等敏感信息不要硬编码在compose文件里提交到代码仓库。
三、什么时候需要迁移到Kubernetes
当项目需要高可用、多实例扩缩容、滚动更新和自愈能力时,就应该考虑Kubernetes了。Compose适合单机,Kubernetes管理的是一组机器组成的集群,它通过Deployment管理后端Pod副本数量,通过Service提供稳定的访问入口,通过Ingress统一处理外部HTTP路由。对前后端分离项目来说,一个典型的部署结构是:前端作为一个Deployment加Service,后端作为一个Deployment加Service,Ingress根据路径把流量分发给两者。
Kubernetes下的配置管理也更为体系化。数据库连接、密码等敏感信息放在Secret中,普通配置放在ConfigMap中,通过环境变量或挂载文件的方式注入容器。健康检查分为livenessProbe和readinessProbe两种:存活探针失败会重启容器,就绪探针失败只是暂时把Pod从Service后端摘除,滚动更新时正是靠就绪探针保证新版本就绪后才接收流量,旧版本才被替换,用户全程无感知。
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 3
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
image: registry.ippipp.com/myapp-backend:1.2.0
ports:
- containerPort: 8080
env:
- name: SPRING_DATASOURCE_URL
valueFrom:
configMapKeyRef:
name: backend-config
key: datasource-url
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: backend-secret
key: db-password
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 20
periodSeconds: 5
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10数据库这类有状态组件在Kubernetes中不建议直接用Deployment部署,更常见的做法是使用云厂商的托管数据库,或者借助StatefulSet加持久卷。对于大多数业务团队来说,把无状态的前后端应用放进集群,数据库留在集群外托管,是复杂度和可靠性之间最平衡的方案。
四、方案选型与落地建议
两种编排方式并不冲突,很多团队的实际路径是:开发和测试环境用Compose快速迭代,生产环境用Kubernetes保障稳定性。如果团队规模小、流量可控、服务器只有一两台,那么Compose配合restart: always策略加定期备份,完全可以支撑线上运行;一旦需要多实例负载均衡、灰度发布、跨节点调度,再迁移到Kubernetes也不迟,因为镜像本身是通用的,迁移成本主要在于编写YAML资源清单。
落地时有几点经验值得注意。第一,镜像标签不要图省事全用latest,固定的版本号配合CI/CD流水线才能实现可追溯的发布和快速回滚。第二,资源限额要设置,Kubernetes中通过resources.requests和resources.limits,Compose中通过deploy.resources,避免某个服务内存泄漏拖垮整台宿主机。第三,日志要统一收集,容器 stdout 输出天然适合接入集中式日志系统,应用内不要往本地文件写日志。把这些细节处理好,前后端分离项目的容器化部署就能真正做到一次配置、稳定运行。
前后端分离Docker ComposeKubernetes修改时间:2026-09-07 05:24:32