在当前的云原生开发环境中,容器化部署已成为现代应用交付的标准方式。将Spring Boot后端与Vue前端整合部署到Docker环境,能够实现开发与生产环境的一致性、简化部署流程并提高资源利用率。这种组合特别适合需要快速迭代的全栈项目,也是中小型团队构建微服务架构的常见起点。
我曾在多个电商和SaaS项目中采用这种架构,实测部署效率比传统方式提升60%以上。通过容器化打包,原本需要半天完成的部署工作现在只需15分钟,且完全避免了"在我机器上能跑"的经典问题。下面将分享从镜像构建到编排部署的完整实战经验。
建议使用以下环境组合(已通过长期生产验证):
重要提示:避免使用latest标签的镜像,所有依赖都应锁定具体版本号。我曾因Node镜像自动升级到新Major版本导致整个CI/CD流水线崩溃。
典型的多模块项目目录结构应如下:
code复制project-root/
├── backend/ # Spring Boot项目
│ ├── src/
│ ├── Dockerfile
│ └── pom.xml
├── frontend/ # Vue项目
│ ├── public/
│ ├── src/
│ ├── Dockerfile
│ └── package.json
└── docker-compose.yml # 编排配置文件
这种结构分离了前后端关注点,同时保持项目整体性。在实际金融项目中,这种布局使我们的前端团队可以独立迭代UI而不影响后端API开发。
dockerfile复制# 使用官方镜像并锁定版本
FROM eclipse-temurin:17-jdk-jammy as builder
# 设置工作目录
WORKDIR /app
# 先单独复制POM文件利用缓存层
COPY pom.xml .
# 下载所有依赖(包括plugins)
RUN mvn dependency:go-offline
# 复制源码并构建
COPY src ./src
RUN mvn package -DskipTests
# 最终运行时镜像
FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
# 从builder阶段复制jar包
COPY --from=builder /app/target/*.jar app.jar
# 使用非root用户运行
RUN useradd -m myuser && chown -R myuser:myuser /app
USER myuser
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:8080/actuator/health || exit 1
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
关键优化点:
在ENTRYPOINT中建议添加JVM内存参数:
dockerfile复制ENTRYPOINT ["java","-Xms512m","-Xmx1024m","-XX:+UseZGC","-jar","app.jar"]
参数说明:
-Xms512m:初始堆内存,避免频繁扩容-Xmx1024m:最大堆内存,不超过容器内存的70%-XX:+UseZGC:低延迟垃圾收集器,适合Web应用血泪教训:曾因未设置Xmx导致容器被OOM Kill,现在所有生产环境都必须显式配置内存参数。
dockerfile复制# 构建阶段
FROM node:16-alpine as build-stage
WORKDIR /app
COPY package*.json ./
# 使用npm ci而不是install保证依赖一致性
RUN npm ci
COPY . .
RUN npm run build
# 生产阶段
FROM nginx:1.23-alpine
COPY --from=build-stage /app/dist /usr/share/nginx/html
# 自定义nginx配置
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
配套的nginx.conf配置示例:
nginx复制server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
# 接口代理配置
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
}
dockerfile复制# 在package.json同目录创建.npmrc文件
RUN echo "cache=/tmp/.npm" > .npmrc
bash复制# 构建时传递--build-arg可以动态设置环境变量
docker build --build-arg VUE_APP_API_BASE_URL=/api .
yaml复制version: '3.8'
services:
backend:
build: ./backend
container_name: app-backend
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_URL=jdbc:postgresql://db:5432/appdb
depends_on:
db:
condition: service_healthy
networks:
- app-network
deploy:
resources:
limits:
memory: 1.5G
frontend:
build: ./frontend
container_name: app-frontend
ports:
- "80:80"
depends_on:
- backend
networks:
- app-network
db:
image: postgres:15-alpine
container_name: app-db
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: appuser
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser"]
interval: 5s
timeout: 5s
retries: 5
networks:
- app-network
volumes:
pgdata:
networks:
app-network:
driver: bridge
pg_isready命令检查yaml复制deploy:
resources:
limits:
cpus: '0.5'
memory: 1.5G
reservations:
memory: 1G
bash复制# 1. 构建镜像
docker-compose build
# 2. 启动服务
docker-compose up -d
# 3. 查看日志
docker-compose logs -f backend
# 4. 执行数据库迁移(如需)
docker-compose exec backend \
java -jar app.jar --spring.jpa.hibernate.ddl-auto=update
yaml复制stages:
- build
- test
- deploy
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_DRIVER: overlay2
build-backend:
stage: build
image: maven:3.8.6
script:
- cd backend
- mvn package -DskipTests
artifacts:
paths:
- backend/target/*.jar
build-frontend:
stage: build
image: node:16
script:
- cd frontend
- npm ci
- npm run build
artifacts:
paths:
- frontend/dist
deploy:
stage: deploy
image: docker:20.10
services:
- docker:20.10-dind
script:
- docker-compose up -d --build
environment:
name: production
url: https://yourdomain.com
症状:Bind for 0.0.0.0:8080 failed: port is already allocated
解决方案:
bash复制# 查找占用进程
sudo lsof -i :8080
# 或者强制重建容器
docker-compose up -d --force-recreate
原因:Vue使用history模式但nginx未正确配置
修正方案:
nginx复制location / {
try_files $uri $uri/ /index.html;
}
检查步骤:
yaml复制# 正确写法
DB_URL: jdbc:postgresql://db:5432/appdb
应急方案:
bash复制# 查看容器内存使用
docker stats
# 临时增加内存限制
docker update --memory 2G app-backend
长期解决方案:
bash复制# 使用Trivy扫描漏洞
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image your-image:tag
dockerfile复制# 在npm install之前复制package-lock.json
COPY package.json package-lock.json ./
RUN npm ci
yaml复制# 在compose中添加日志驱动
services:
backend:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
yaml复制services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana
ports:
- "3000:3000"
配套的prometheus.yml配置:
yaml复制scrape_configs:
- job_name: 'spring-boot'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['backend:8080']
经过多个项目的实战检验,这套方案在保持简单易用的同时,能够满足中小规模生产环境的需求。对于需要更高可用性的场景,可以考虑引入Kubernetes替代Docker Compose,但核心的容器化原则仍然适用。