一、项目目录
csharp
your-project/
├── backend/ # SpringBoot后端
│ ├── src/
│ ├── pom.xml
│ └── Dockerfile
├── frontend/ # H5前端
│ ├── src/
│ ├── package.json
│ ├── nginx.conf
│ └── Dockerfile
├── mysql/ # mysql挂载目录(宿主机)
│ └── init/ # 初始化sql脚本(可选,首次建表)
│ └── init.sql
├── redis/ # redis挂载目录
│ └── redis.conf # redis自定义配置(可选)
└── docker-compose.yml # 完整编排文件
二、Spring Boot 后端 Dockerfile
在 backend/ 目录下创建 Dockerfile:
Dockerfile
# ============================================================
# 阶段一:Maven 构建(编译打包)
# 用 Maven 镜像做编译,把 jar 包打出来
# ============================================================
FROM maven:3.8-openjdk-17-slim AS builder
# 设置容器内工作目录
WORKDIR /build
# 先复制 pom.xml,利用 Docker 层缓存
# ------ 只有 pom.xml 变了才会重新下载依赖,源码没变就走缓存
COPY pom.xml .
RUN mvn dependency:go-offline -B
# 再复制源码并打包
COPY src ./src
RUN mvn clean package -DskipTests -B
# ============================================================
# 阶段二:运行时(只保留 JRE,体积更小)
# ============================================================
FROM openjdk:17-jre-slim
# 维护者信息(可选)
LABEL maintainer="your-name"
# 设置工作目录
WORKDIR /app
# 从构建阶段复制打好的 jar 包
# 注意:把 *.jar 统一命名为 app.jar,避免版本号变化
COPY --from=builder /build/target/*.jar app.jar
# 暴露 Spring Boot 默认端口(仅声明作用,真正映射在 compose 里做)
EXPOSE 8080
# JVM 参数调优(根据服务器内存调整)
# -XX:+UseG1GC 使用 G1 垃圾回收器
# -XX:MaxRAMPercentage JVM 最大堆占容器内存的百分比
# -Duser.timezone 设置时区为 Asia/Shanghai
ENV JAVA_OPTS="-XX:+UseG1GC -XX:MaxRAMPercentage=75.0 -Duser.timezone=Asia/Shanghai"
# 启动命令:sh -c 让环境变量生效
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
构建命令 (在 backend/ 目录下执行):
bash
# 构建镜像,-t 指定镜像名和标签,. 表示当前目录作为构建上下文
docker build -t my-backend:1.0 .
三、H5 前端 Nginx 配置 + Dockerfile
3.1 Nginx 配置文件 frontend/nginx.conf
Nginx
# ============================================================
# Nginx 主配置文件
# ============================================================
worker_processes auto; # 工作进程数,auto 表示按 CPU 核数自动
events {
worker_connections 1024; # 每个进程最大连接数
}
http {
# --- 基础设置 ---
include /etc/nginx/mime.types; # 引入 MIME 类型表
default_type application/octet-stream; # 默认文件类型
sendfile on; # 开启高效文件传输模式
tcp_nopush on; # 减少网络报文段数量
keepalive_timeout 65; # 长连接超时时间(秒)
gzip on; # 开启 gzip 压缩
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# ============================================================
# 上游服务:指向后端 Spring Boot 容器
# 注意:docker-compose 中服务名 backend 就是主机名
# ============================================================
upstream backend_server {
server backend:8080; # 格式:容器名:端口
}
# ============================================================
# 前端站点配置
# ============================================================
server {
listen 80; # 监听 80 端口
server_name localhost; # 域名,生产环境改成实际域名
# --- 前端静态资源根目录 ---
root /usr/share/nginx/html; # H5 打包后静态文件存放位置
index index.html index.htm;
# ============================================================
# 关键:Vue/React 单页应用路由模式(history 模式)必须加这一段
# 否则刷新页面会 404,因为 Nginx 找不到对应的物理文件
# try_files 会依次尝试:$uri → $uri/ → 都找不到就 fallback 到 index.html
# ============================================================
location / {
try_files $uri $uri/ /index.html;
}
# ============================================================
# API 接口反向代理
# 把 /api 开头的请求转发给后端 Spring Boot
# 这样前端只需请求 /api/xxx,跨域问题由 Nginx 代理解决
# ============================================================
location /api/ {
# proxy_pass 末尾的 / 表示:去掉 /api 前缀再转发
# 例如 /api/user/login → backend_server/user/login
# 如果写成 proxy_pass http://backend_server;(不带 /)
# 则转发后路径为 /api/user/login,后端 Controller 也要对应改
proxy_pass http://backend_server/;
# 代理传递的 HTTP 头信息
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;
}
# 静态资源缓存:CSS/JS/图片等
location ~* .(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 30d; # 缓存 30 天
add_header Cache-Control "public, no-transform";
}
}
}
3.2 前端 Dockerfile frontend/Dockerfile
Dockerfile
# ============================================================
# 阶段一:Node 构建(打包 H5 静态文件)
# ============================================================
FROM node:18-alpine AS builder
WORKDIR /build
# 先复制 package.json 和 lock 文件,利用层缓存
COPY package.json package-lock.json ./
RUN npm install --registry=https://registry.npmmirror.com
# 复制源码并构建
COPY . .
RUN npm run build
# ============================================================
# 阶段二:Nginx 运行时
# ============================================================
FROM nginx:alpine
# 删除 Nginx 默认配置(避免冲突)
RUN rm /etc/nginx/conf.d/default.conf
# 把我们的 nginx.conf 复制到 Nginx 配置目录
COPY nginx.conf /etc/nginx/nginx.conf
# 从构建阶段复制打包好的静态文件到 Nginx 站点目录
COPY --from=builder /build/dist /usr/share/nginx/html
# 暴露 80 端口
EXPOSE 80
# 启动 Nginx(前台运行,Docker 容器要求主进程不退出)
CMD ["nginx", "-g", "daemon off;"]
构建命令 (在 frontend/ 目录下执行):
bash
docker build -t my-frontend:1.0 .
四、redis/redis.conf(可选,基础配置)
新建 redis/redis.conf
conf
# 开启持久化
appendonly yes
# 数据存放目录
dir /data
# 允许外网访问(容器内部通信不受影响)
bind 0.0.0.0
# 关闭保护模式(设置密码后可关闭)
protected-mode no
五、mysql/init/init.sql(可选,首次自动建表)
容器第一次创建时自动执行;如果 mysql-data 卷已经存在,不会重复执行!
sql
-- 示例测试表
CREATE TABLE IF NOT EXISTS user(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
create_time DATETIME
);
六、SpringBoot application-prod.yml 配置(后端项目配置,适配容器)
后端启用
prod环境,读取 compose 传入环境变量,推荐优先使用环境变量,避免硬编码密码
yaml
spring:
datasource:
url: ${SPRING_DATASOURCE_URL}
username: ${SPRING_DATASOURCE_USERNAME}
password: ${SPRING_DATASOURCE_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
host: ${SPRING_REDIS_HOST}
port: ${SPRING_REDIS_PORT}
password: ${SPRING_REDIS_PASSWORD}
database: 0
七、docker-compose.yml
yaml
version: '3.8'
# 自定义网络:所有服务加入这个网络,容器之间可以用服务名访问
networks:
app-network:
driver: bridge
# 数据卷:持久化数据,容器删除,宿主机数据不丢失
volumes:
mysql-data: # mysql数据卷
redis-data: # redis数据卷
services:
# ========== MySQL 数据库 ==========
mysql:
image: mysql:8.0.33
container_name: mysql
restart: always
# 端口映射:宿主机3306 -> 容器3306;本地navicat可连接调试
ports:
- "3306:3306"
environment:
# root账号密码,生产不要用弱密码!
MYSQL_ROOT_PASSWORD: root123456
MYSQL_DATABASE: demo_db # 项目初始化数据库名
MYSQL_USER: demo_user # 业务账号(推荐不用root连业务)
MYSQL_PASSWORD: demo123456 # 业务账号密码
TZ: Asia/Shanghai
volumes:
- mysql-data:/var/lib/mysql # 数据库数据持久化
- ./mysql/init:/docker-entrypoint-initdb.d # 初始化sql目录,容器首次启动自动执行里面sql
networks:
- app-network
# mysql健康检查,判断是否启动成功
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-uroot", "-proot123456"]
interval: 10s
timeout: 5s
retries: 5
# ========== Redis 缓存 ==========
redis:
image: redis:7-alpine
container_name: redis
restart: always
ports:
- "6379:6379"
environment:
TZ: Asia/Shanghai
volumes:
- redis-data:/data # redis持久化数据
- ./redis/redis.conf:/etc/redis/redis.conf # 挂载自定义配置文件(可选)
command: redis-server /etc/redis/redis.conf --requirepass redis123456
networks:
- app-network
healthcheck:
test: ["CMD", "redis-cli", "-a", "redis123456", "ping"]
interval: 10s
timeout: 5s
retries: 5
# ========== SpringBoot后端 ==========
backend:
build:
context: ./backend
image: my-backend:1.0
container_name: backend
restart: always
ports:
- "8080:8080"
environment:
SPRING_PROFILES_ACTIVE: prod
TZ: Asia/Shanghai
# 【重点】容器内连接mysql,host写mysql(compose服务名),不是localhost!
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/demo_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
SPRING_DATASOURCE_USERNAME: demo_user
SPRING_DATASOURCE_PASSWORD: demo123456
# redis连接地址,host写redis(compose服务名)
SPRING_REDIS_HOST: redis
SPRING_REDIS_PORT: 6379
SPRING_REDIS_PASSWORD: redis123456
# 依赖:等待mysql、redis容器启动(只是容器启动,不代表数据库就绪!)
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-network
# ========== Nginx+H5前端 ==========
frontend:
build:
context: ./frontend
image: my-frontend:1.0
container_name: frontend
restart: always
ports:
- "80:80"
depends_on:
- backend
networks:
- app-network
关键点:
depends_on + condition: service_healthy会等待 mysql、redis 健康检查通过之后,才启动 backend,解决 SpringBoot 启动时数据库还没初始化完成报错。
八、完整操作流程(Windows 环境)
步骤 1:安装 Docker Desktop
- 下载并安装 Docker Desktop for Windows
- 安装时勾选 WSL 2 后端(推荐)
- 启动 Docker Desktop,确认右下角图标为运行状态
- 验证安装:
bash
docker --version
docker compose version
步骤 2:确认后端可正常打包
bash
cd backend
mvn clean package -DskipTests
确认 target/ 目录下生成了 jar 包。
步骤 3:确认前端可正常打包
bash
cd frontend
npm install
npm run build
确认 dist/ 目录下生成了静态文件。
步骤 4:一键构建并启动
在项目根目录执行:
bash
# 构建镜像 + 启动容器(-d 后台运行)
docker compose up -d --build
参数说明:
up:创建并启动容器-d:后台运行(detached 模式)--build:启动前重新构建镜像(代码有改动时加这个)
步骤 5:验证部署
bash
# 查看运行中的容器
docker ps
# 查看后端日志
docker logs -f backend
# 查看前端日志
docker logs -f frontend
浏览器访问:
- 前端 H5 页面 :
http://localhost - 后端 API :通过 Nginx 代理访问
http://localhost/api/xxx
步骤 6:后续更新部署
bash
# 重新拉取代码后,重新构建并启动
docker compose up -d --build
# 或者只更新某个服务
docker compose up -d --build backend
常用命令速查
bash
docker compose down # 停止并删除容器,保留数据卷(推荐日常使用)
docker compose down -v # 停止+删除容器+删除数据卷,会清空mysql、redis数据!!【慎重】
docker compose restart # 重启所有服务
docker compose logs -f # 查看所有服务日志
docker exec -it backend bash # 进入后端容器
docker images # 查看本地镜像
docker rmi my-backend:1.0 # 删除镜像
九、访问地址
- H5 页面:
http://localhost - API 接口:
http://localhost/api/xxx(Nginx 反向代理转发给 backend) - 本地 Navicat 可以连接宿主机
127.0.0.1:3306,账号 demo_user /demo123456,方便调试 - 本地 Redis 客户端:
127.0.0.1:6379,密码 redis123456
十、常见坑点(Windows 环境重点注意)
- 容器内连接数据库不能写localhost 容器内localhost是容器自身,mysql 服务名是
mysql,redis 服务名是redis,compose 网络自动解析。 - Windows 文件路径大小写 docker 挂载文件时,尽量使用
/斜杠,不要 ``;init.sql 文件名不要大写。 - mysql 初始化脚本只执行一次 如果你要重新执行 init.sql,必须
docker compose down -v删除 mysql 数据卷,重新启动。 - 端口冲突 如果本机 Windows 已经装了 MySQL 占用 3306、Redis 占用 6379,需要修改 compose 端口映射,例如:
"3307:3306"。 - 时区问题 所有容器都配置
TZ=Asia/Shanghai,避免时间差。
十一、扩展:生产优化建议
- 密码不要硬编码在 compose.yml,使用
.env文件统一管理环境变量 - MySQL 增加备份脚本,定时导出 sql
- Nginx 增加 HTTPS 证书配置
- SpringBoot 增加健康检查端点,给 backend 加上 healthcheck