Windows系统Docker+SpringBoot+H5+Mysql+Redis打包部署流程

一、项目目录

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

  1. 下载并安装 Docker Desktop for Windows
  2. 安装时勾选 WSL 2 后端(推荐)
  3. 启动 Docker Desktop,确认右下角图标为运行状态
  4. 验证安装:
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 环境重点注意)

  1. 容器内连接数据库不能写localhost 容器内localhost是容器自身,mysql 服务名是mysql,redis 服务名是redis,compose 网络自动解析。
  2. Windows 文件路径大小写 docker 挂载文件时,尽量使用 / 斜杠,不要 ``;init.sql 文件名不要大写。
  3. mysql 初始化脚本只执行一次 如果你要重新执行 init.sql,必须 docker compose down -v 删除 mysql 数据卷,重新启动。
  4. 端口冲突 如果本机 Windows 已经装了 MySQL 占用 3306、Redis 占用 6379,需要修改 compose 端口映射,例如:"3307:3306"。
  5. 时区问题 所有容器都配置 TZ=Asia/Shanghai,避免时间差。

十一、扩展:生产优化建议

  1. 密码不要硬编码在 compose.yml,使用 .env 文件统一管理环境变量
  2. MySQL 增加备份脚本,定时导出 sql
  3. Nginx 增加 HTTPS 证书配置
  4. SpringBoot 增加健康检查端点,给 backend 加上 healthcheck
相关推荐
SingleShadow2 小时前
一文搞懂 CAN 总线:从入门到 STM32 应用
后端
码事漫谈2 小时前
国庆七天,AI圈没一天消停
后端
掘金酱3 小时前
[稀土掘金 × 火山引擎] AI用量周榜冲刺赛|获奖名单公示
前端·人工智能·后端
montEvergreen3 小时前
RTMP 里的那根“连接线”:读一份 NetConnection 源码
后端·go
montEvergreen3 小时前
RTMP 握手:一场客户端与服务端的“暗号”对决
后端·go
qianmoq3 小时前
GrantForge 开源:基于 Spring Boot 构建统一权限平台,让权限管理不再从零开始
后端
泡泡oO3 小时前
“如果你还在用Superpowers,那我不要和你说话”
前端·后端·全栈
YatHinLay3 小时前
FastThreadLocal
后端
newerp3 小时前
sync.Mutex 源码深度拆解:正常模式(自旋)与饥饿模式
后端·程序员·go