Docker Compose 部署
本小节,讲解如何使用项目自带的 script/docker-compose (opens new window) 目录,通过 Docker Compose 将前端 + 后端 + MySQL + Redis 一起部署到一台 Linux 服务器上。
整体链路如下:
浏览器 ──WEB_PORT(默认 8080)──> web 容器(Nginx:前端静态文件 + 反向代理)
│ /admin-api/、/app-api/、/infra/ws
↓
yudao-server 容器(48080)──> mysql 容器 + redis 容器
注意:服务器的 IP 地址。
- 外网 IP:139.9.196.247
下属所有涉及到 IP 的配置,需要替换成你自己的。
友情提示:
- 本文的部署过程,本质和 《Docker 部署》 一样,只是把多条
docker run命令收敛到仓库的script/docker-compose/docker-compose.yml中,容器之间通过 Compose 服务名 互相访问。 - 本文是入门级的单机部署示例,用于体验与测试,不涉及高可用。
- 完整的变量说明,以同目录的
README.md(opens new window) 为准,本文主要讲解步骤与注意点。
# 1. 环境准备
# 1.1 安装 Docker 与 Docker Compose
① 参考 《Docker 部署》 的「1.1 安装 Docker」小节,完成 Docker 的安装。
② 执行如下命令,安装 Docker Compose 插件,并确认版本不低于 v2.20.2:
dnf install -y docker-compose-plugin
docker compose version
友情提示:
本文使用的是 Compose V2 的 docker compose(中间是空格)命令,而不是老的 docker-compose 命令。如果拉取镜像较慢,可参考 《Docker 部署》 中的镜像加速说明。
# 1.2 确认后端分支与 JDK 版本
后端项目有 3 个分支,JDK 版本、运行镜像、Redis 配置前缀都不同,必须一一对应:
| 后端分支 | JDK | Spring Boot | yudao-server/Dockerfile 的运行镜像 | Redis 环境变量 |
|---|---|---|---|---|
master | 8 | 2.7 | eclipse-temurin:8-jre | SPRING_REDIS_* |
master-jdk17 | 17 / 21 | 3.5 | eclipse-temurin:17-jre | SPRING_DATA_REDIS_* |
master-jdk25 | 25 | 4.x | eclipse-temurin:25-jre | SPRING_DATA_REDIS_* |
注意:目前只有 `master-jdk17` 分支提供 `script/docker-compose` 目录!
master、master-jdk25 分支尚未同步。如需在这两个分支使用,可以复制 master-jdk17 的目录自行适配,主要替换两处:
- ① 运行镜像:使用对应分支自带的
yudao-server/Dockerfile,它的FROM已经与分支的 JDK 匹配。 - ② Redis 环境变量:
master(JDK 8)分支需要把docker-compose.yml中的SPRING_DATA_REDIS_*改成SPRING_REDIS_*,否则 Redis 配置不会生效。
另外,JDK 25 编译的 Jar 包放到 8-jre 镜像中运行,会报 UnsupportedClassVersionError 错误,不要混用。
# 1.3 了解部署目录
docker-compose.yml 中的构建上下文、SQL 文件都使用 ../../ 相对路径,指向源码根目录下的文件。所以服务器上需要有如下目录结构,不能只上传 script/docker-compose 子目录:
ruoyi-vue-pro # 源码根目录
├── yudao-server
│ ├── Dockerfile # 构建上下文 ../../yudao-server
│ └── target/yudao-server.jar # mvn 打包产物
├── sql/mysql
│ ├── ruoyi-vue-pro.sql # 首次启动时导入
│ └── quartz.sql # 首次启动时导入
└── script/docker-compose # 部署目录,docker compose 命令都在这里执行
├── docker-compose.yml
├── .env.example # 复制为 .env 后修改
├── nginx/default.conf # Nginx 配置
└── web/dist # 需自行放入前端打包产物
友情提示:
推荐直接在服务器上 git clone 源码,并在服务器上打包后端。如果在本机打包后上传,需要保持上述相对路径不变。
docker-compose.yml 中包含如下 4 个服务:
| 服务 | 镜像 | 说明 |
|---|---|---|
mysql | mysql:8.4 | 首次启动导入 ruoyi-vue-pro.sql、quartz.sql,数据卷 yudao-boot_mysql-data |
redis | redis:7.4-alpine | 开启密码与 AOF,数据卷 yudao-boot_redis-data |
yudao-server | yudao-server:${IMAGE_TAG} | 由 yudao-server/Dockerfile 构建,端口 48080,只在 Compose 网络内访问 |
web | nginx:1.28-alpine | 前端 + 反向代理,对宿主机开放 ${WEB_PORT},默认 8080 |
MySQL、Redis、后端默认不映射到宿主机,不会与本机已有的 3306、6379、48080 冲突。需要时,可以取消 docker-compose.yml 中对应 ports 的注释(只绑定 127.0.0.1)。
# 2. 打包
# 2.1 打包后端
在源码根目录下,执行如下命令,构建出 yudao-server/target/yudao-server.jar 包:
mvn clean package -pl yudao-server -am -Dmaven.test.skip=true
友情提示:
后端不需要修改 application-dev.yaml 中的 MySQL、Redis 地址,docker-compose.yml 会通过环境变量覆盖它们。
注意:为什么 MySQL 要导入 quartz.sql?
application-dev.yaml 开启了 Quartz 定时任务,并使用数据库存储。如果缺少 QRTZ_ 开头的表,后端会启动失败。
# 2.2 打包前端
前端的打包方式,和 《Linux 部署》 的「3. 部署前端」小节一致。不同的是,前端和后端通过同一个 Nginx 同源访问,所以“后端地址”要填写 Nginx 的访问地址(例如说 http://139.9.196.247:8080),不要填写 yudao-server、48080 等容器名或后端端口:
| 前端项目 | 配置文件 | 修改的配置项 | 打包命令 => 产物目录 |
|---|---|---|---|
yudao-ui-admin-vue3 | .env.prod | VITE_BASE_URL='http://139.9.196.247:8080',VITE_API_URL=/admin-api 保持不变 | pnpm build:prod => dist-prod |
yudao-ui-admin-vben | apps/web-antd/.env.production | VITE_BASE_URL=http://139.9.196.247:8080,VITE_GLOB_API_URL=/admin-api | pnpm build:antd => apps/web-antd/dist |
- 前端的
VITE_APP_CAPTCHA_ENABLE,要与下文.env中的YUDAO_CAPTCHA_ENABLE一致,默认都是true。 yudao-ui-admin-vue2同理,把VUE_APP_BASE_API改成 Nginx 的访问地址即可。
打包完成后,在源码根目录下,执行如下命令,将产物目录里面的文件复制到 web/dist 目录:
mkdir -p script/docker-compose/web/dist
cp -R /path/to/前端打包产物目录/. script/docker-compose/web/dist/
复制后,script/docker-compose/web/dist 下应该直接有 index.html 文件。
# 3. 配置 .env
执行如下命令,创建 .env 文件:
cd script/docker-compose
cp .env.example .env
至少替换 MYSQL_ROOT_PASSWORD、MYSQL_PASSWORD、REDIS_PASSWORD 三个示例密码(形如 Root@ChangeMe2026)。主要的变量如下:
| 变量 | 默认值 | 说明 |
|---|---|---|
WEB_PORT | 8080 | 前端访问端口。需要在服务器安全组中放行 |
IMAGE_TAG | local | 后端镜像的 tag,修改后便于回滚 |
MYSQL_DATABASE | ruoyi-vue-pro | 数据库名 |
MYSQL_ROOT_PASSWORD | 示例值 | MySQL root 密码,仅供初始化与运维使用 |
MYSQL_USER / MYSQL_PASSWORD | yudao / 示例值 | 后端使用的账号,只拥有 MYSQL_DATABASE 库的权限 |
REDIS_PASSWORD | 示例值 | Redis 密码 |
SPRING_PROFILES_ACTIVE | dev | 后端的 profile |
YUDAO_CAPTCHA_ENABLE | true | 是否开启登录验证码 |
docker-compose.yml 会将它们转换成后端的环境变量,覆盖 Jar 包中 application-dev.yaml 的配置。例如说:
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_URL对应spring.datasource.dynamic.datasource.master.url,主库master、从库slave都会覆盖成mysql:3306。SPRING_DATA_REDIS_HOST、SPRING_DATA_REDIS_PASSWORD对应spring.data.redis.*。Redis 库索引沿用application-dev.yaml中的1。
注意:`.env` 中的 MySQL 账号密码,只在首次初始化时生效!
mysql-data 数据卷创建后,再修改 .env 中的 MYSQL_*,不会改变数据库中已有的账号密码,反而会导致后端连接失败。另外,.env 已被 .gitignore 忽略,不要提交到 Git 仓库。
# 4. 启动
在 script/docker-compose 目录下,执行如下命令:
# ① 检查配置:变量缺失会直接报错
docker compose config
# ② 构建后端镜像,并启动所有容器
docker compose up -d --build
# ③ 查看状态与日志
docker compose ps
docker compose logs -f yudao-server
MySQL 首次启动时需要导入 SQL,大约 1 分钟左右。yudao-server 会等到 mysql、redis 的健康检查通过后,才开始启动。
日志中出现 Started YudaoServerApplication,说明后端启动完成。
# 5. 验证
① 执行如下命令,验证 Nginx => 后端 的转发正常:
curl http://127.0.0.1:8080/admin-api/
返回 {"code":401,"data":null,"msg":"账号未登录"} 即可。
② 执行如下命令,验证 后端 => MySQL 正常,并且 SQL 已经初始化:
curl -G --data-urlencode "name=芋道源码" http://127.0.0.1:8080/admin-api/system/tenant/get-id-by-name
返回 {"code":0,"data":1,"msg":""},说明从数据库中查询到了默认租户。
③ 请求 http://139.9.196.247:8080 (opens new window) 地址(“需要换成你的外网 IP”),成功访问前端项目。使用默认账号 admin、密码 admin123 登录。
④ 登录成功后,执行如下命令,验证 后端 => Redis 正常,可以看到登录的访问令牌(redis 容器已经通过 REDISCLI_AUTH 配置了密码):
docker compose exec redis redis-cli -n 1 --scan --pattern 'oauth2_access_token:*'
# 6. 日常运维
# 6.1 更新后端
# 回到源码根目录重新打包,再回到部署目录重建、重启
cd ../..
mvn clean package -pl yudao-server -am -Dmaven.test.skip=true
cd script/docker-compose
docker compose up -d --build yudao-server
Nginx 使用 Docker 内置 DNS 动态解析服务名,后端容器重建后,无需重启 web。如需回滚,可以在打包前修改 .env 的 IMAGE_TAG,保留旧 tag 的镜像。
# 6.2 更新前端
# 清空 web/dist 内的文件(保留 dist 目录本身),复制新产物,再重启 web
rm -rf web/dist/*
cp -R /path/to/前端打包产物目录/. web/dist/
docker compose restart web
不要删除 web/dist 目录本身:它是 bind mount 挂载,目录被删掉重建后,容器会看不到新文件。修改 nginx/default.conf 后,同样执行 docker compose restart web。
# 6.3 查看日志
docker compose logs -f --tail=200 yudao-server ## 后端控制台日志
docker compose exec yudao-server tail -f /root/logs/yudao-server.log ## 容器内的应用日志文件
docker compose logs -f web ## Nginx 访问日志
# 6.4 停止与数据
# 停止并删除容器,保留数据卷
docker compose down
注意:不要执行 `docker compose down -v`!
它会删除 yudao-boot_mysql-data、yudao-boot_redis-data 数据卷,数据库内容将全部丢失。
# 6.5 数据库升级
SQL 只在 mysql-data 数据卷为空(首次启动)时执行一次。后续项目升级时,请先备份,再参考【更新日志】中对应版本的说明,手动执行升级 SQL:
docker compose exec -T mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE"' < /path/to/upgrade.sql
如需使用外部 MySQL,可参考 README.md 的「数据库初始化说明」,删除 mysql 服务并修改数据源地址。
# 7. 常见问题
① docker compose config 报 请在 .env 中设置 xxx:没有执行 cp .env.example .env,或者没有在 script/docker-compose 目录下执行命令。
② docker compose up 报 web/dist 或 SQL 文件不存在:挂载使用了 create_host_path: false,需要先完成「2.2 打包前端」的复制,并确认源码根目录下有 sql/mysql/*.sql 文件。
③ yudao-server 反复重启,执行 docker compose logs yudao-server 查看原因:
Access denied:.env中的 MySQL 密码,在数据卷初始化后修改过。Table 'ruoyi-vue-pro.QRTZ_LOCKS' doesn't exist:已有的数据卷中没有 Quartz 表。可以执行docker compose exec -T mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE"' < ../../sql/mysql/quartz.sql补充导入。- 首次导入中途被中断,数据卷处于半初始化状态:确认数据可以丢弃后,执行
docker compose down和docker volume rm yudao-boot_mysql-data,再重新启动。
④ 前端请求跑到了 localhost:48080 等地址,或验证码报错:前端打包时,没有把后端地址改成 Nginx 的访问地址,或 VITE_APP_CAPTCHA_ENABLE 与 YUDAO_CAPTCHA_ENABLE 不一致,参见「2.2 打包前端」。
⑤ Swagger(/doc.html)、Druid(/druid/)、Spring Boot Admin(/admin/)等页面无法访问:出于安全考虑,nginx/default.conf 只代理 /admin-api/、/app-api/、/infra/ws,需要时请自行增加 location。
# 666. 更多说明
① 如果你希望部署到 Kubernetes 集群,可参考 《Kubernetes 部署》 文档。
② Compose 的启动顺序、文件格式,可参考 Docker 官方的 《Control startup order》 (opens new window)、《Services top-level elements》 (opens new window) 文档。
③ 如果需要 HTTPS 访问,可参考 《HTTPS 证书》 文档,在 nginx/default.conf 中增加 443 的 server 配置,并在 docker-compose.yml 中暴露 443 端口、挂载证书目录。