ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Docker部署若依后端:从环境到运行的完整实战指南

Docker部署若依后端:从环境到运行的完整实战指南 1. 为什么我最终选择用 Docker 部署若依后端先交代一下背景。若依RuoYi是目前国内中小型项目中出场率极高的一套前后端分离脚手架后端基于 Spring Boot前端基于 Vue权限模块用 Spring Security Redis 做会话管理部署时要牵动的环节其实不少。早期我在服务器上部署若依后端走的还是“手动装 JDK 17、装 MySQL 8、装 Redis、再扔一个打好的 jar 包跑起来”的流程这套流程第一次能成第二次、第三次就露馅了——换一台服务器就要重新走一遍环境配置中间稍有不慎还会踩到 JDK 版本不一致、MySQL 字符集不对、Redis 密码忘改这类问题。后来我改成 Docker 部署理由非常直接若依后端本身依赖 MySQL 和 Redis这三个角色全部用容器编排之后整条部署链路只有一个docker-compose.yml文件加一个 Dockerfile换机器等于“复制目录 → 构建镜像 → 起容器”环境问题天然屏蔽。若依的启动流程其实是“初始化数据库 → 构建后端 jar → 启动 Spring Boot → 依赖 Redis 和 MySQL”这个顺序用 Compose 的depends_on可以控制但前提是对若依的技术栈足够了解否则连编排文件都写不对。团队协作时Docker 部署最大的价值在于“可复现”。新人拿到项目不用再问“你的 JDK 是哪个版本”“MySQL 密码是啥”直接docker compose up -d就能拉起来。这篇指南我会按实际操作的顺序展开从环境准备、镜像构建、Compose 编排到真实部署中容易踩的坑一条龙讲完。适合有以下需求的朋友参考你已经拿到若依后端源码想在服务器或本地用 Docker 把后端跑起来或者你正在给团队做一套统一的若依部署方案。由于若依有单体版、微服务版RuoYi-Cloud和前后端分离版本文以使用最广泛的 RuoYi-Vue 单体后端为例微服务版的思路类似但涉及模块拆分我会在相关位置单独标注差异。2. 部署前的准备先把主机环境和基础服务理顺2.1 安装 Docker 与 Docker Compose留意这两个工具的版本差异Docker 本身我没遇到太多坑真正容易出问题的是 Docker Compose。目前 Compose 有 V1docker-compose命令Python 实现和 V2docker compose子命令Go 实现两个大版本V1 在 2023 年后基本不再维护所以下面所有命令我统一用新版写法也就是docker compose中间带空格的这种。如果你在服务器上敲docker-compose提示命令不存在先检查一下是不是只装了 Docker 没装 Compose 插件。CentOS 7 这种老系统上装 Docker 时还要额外注意内核版本和存储驱动问题。我自己在 CentOS 7.9 上装过 Docker 20.10默认存储驱动是overlay2但需要确认内核在 3.10 以上且开启了相关模块否则 Docker 起容器时会报error creating overlay mount。如果遇到这种报错要么升级内核要么退一步把存储驱动临时改成vfs但vfs性能比较差只建议在测试环境临时使用。Windows 和 Mac 用户直接用 Docker Desktop 即可启动后记得在设置里把资源配额调高一点。若依后端加 MySQL 加 Redis 三个容器同时跑内存占用大约在 1.2GB 到 1.5GB 之间Docker Desktop 默认的 2GB 内存配额会非常紧张建议至少给到 3GB。安装完成后验证一下docker --version docker compose version docker psdocker ps能正常返回空列表就说明 Docker 服务已经起来了。如果docker ps报Cannot connect to the Docker daemon先看 Docker 服务是否启动Linux 下执行sudo systemctl start dockerWindows/Mac 下确认 Docker Desktop 是否已经运行。2.2 拉取若依后端源码并完成第一次编译部署用的是若依前后端分离版本的后端工程根目录通常叫RuoYi-Vue后端模块在ruoyi-admin、ruoyi-framework、ruoyi-system、ruoyi-common这几个子工程里。拿到源码之后第一步不是着急写 Dockerfile而是先在本地把它编译通过确认源码本身没问题。若依后端要求的 JDK 版本需要重点确认。我在实际部署中发现不同版本的若依对 JDK 要求不一样早期版本基于 JDK 1.8较新版本要求 JDK 17。如果你在本地编译时报java: invalid source release: 17说明你当前 JDK 版本太低反过来如果代码是用 JDK 8 写的你用 17 去编译也可能遇到javax.xml.bind相关类找不到的兼容性问题。最稳妥的办法是看pom.xml里java.version标签以它为准。编译命令mvn clean package -DskipTests编译产物在ruoyi-admin/target/ruoyi-admin.jar这就是我们要容器化的 Spring Boot 可执行 jar。第一次编译通常要下载大量依赖如果 Maven 仓库网络不稳定建议先把镜像源切到国内阿里云镜像不然等依赖下完都能喝三杯茶了。2.3 MySQL 和 Redis直接决定若依能不能跑起来的关键服务这里直接一点若依后端必须依赖 MySQL 和 Redis这两个服务没准备好后端容器起来了也会反复报错退出。MySQL 负责业务数据Redis 负责验证码缓存、登录会话和多端登录控制两个都不能少。MySQL 版本的坑我单独提一下若依的初始化 SQL 文件用了utf8mb4字符集并且包含存储过程、索引等特性MySQL 5.6 及以下版本会报各种语法错误或字符集不支持的问题。建议直接用 MySQL 8.0我自己目前统一用 mysql:8.0 镜像没出过兼容性毛病。Redis 则任意版本都行若依在默认配置下用的是单机模式直接用redis:7.0镜像最省心。如果你决定 MySQL 和 Redis 也用容器跑那么这段可以直接往下看 docker-compose 编排如果你考虑用宿主机上的 MySQL 和 Redis记得后续配置里数据库地址和 Redis 地址要写宿主机 IP不能写localhost或127.0.0.1因为在容器内部这两个地址指向的是容器自己。这个坑我见得太多了哪怕你数据库就在旁边容器里连不上也是白搭。3. 编写后端 Dockerfile核心思路是多阶段构建3.1 为什么若依后端镜像要用多阶段构建后端项目打包成 Docker 镜像时有两条常见路线一条是“直接在宿主机上 mvn package然后把 jar 拷进镜像”另一条是“让 Docker 在构建阶段自己完成 Maven 编译”。我强烈建议走第二条路线也就是多阶段构建。多阶段构建的核心逻辑是第一个阶段用带 Maven 和 JDK 的镜像在容器里完成源码编译第二个阶段用轻量的 JRE 镜像只拷贝编译出来的 jar 文件。这样做有两个好处一是宿主机不需要安装 Maven 和 JDK二是最终镜像体积会小很多。做个对比你就明白了。如果直接把 jar 拷进一个带 JDK 的镜像镜像体积通常在 400MB 以上用多阶段构建第一阶段的编译镜像确实大但那只是临时层最终交付给生产的镜像只保留第二阶段体积能压到 200MB 左右。加上若依前后端分离部署时后端镜像会被推送到服务器上小体积在传输和启动速度上的优势是实打实的。3.2 Dockerfile 实例与逐段拆解下面是我在项目中实际使用的 Dockerfile你可以在若依后端根目录新建Dockerfile文件# 第一阶段编译 FROM maven:3.8.8-eclipse-temurin-11 AS builder WORKDIR /build COPY pom.xml . RUN mvn dependency:resolve -B COPY src ./src RUN mvn clean package -DskipTests # 第二阶段运行 FROM eclipse-temurin:11-jre WORKDIR /app COPY --frombuilder /build/ruoyi-admin/target/ruoyi-admin.jar app.jar EXPOSE 8080 ENV TZAsia/Shanghai ENTRYPOINT [java, -jar, app.jar]这里有几处需要根据实际项目情况调整的细节eclipse-temurin是 adptopenjdk 的新名字如果你项目要求 JDK 17就改成maven:3.9-eclipse-temurin-17和eclipse-temurin:17-jre。第一行COPY pom.xml .加RUN mvn dependency:resolve -B是典型的依赖缓存优化策略。Maven 依赖没变化时Docker 会直接复用这一层的构建缓存以后每次改代码重新打镜像速度会快很多。如果后端代码依赖本地私有仓库的 jar需要把私有仓库的 settings.xml 也拷进构建阶段并在mvn命令里通过-s参数指定。EXPOSE 8080只是声明真正放开端口是在 docker-compose 或docker run -p里做的。时区设置ENV TZAsia/Shanghai是我每次都要加的。若依有一些基于日期时间的业务逻辑比如登录限制、操作日志时间等容器默认 UTC 时区会导致日志时间和本地时间差 8 个小时排错的时候特别容易困惑。注意如果你用的是 RuoYi-Cloud 微服务版Dockerfile 的结构会有差异。微服务版的后端拆成了多个可独立启动的模块比如ruoyi-gateway、ruoyi-auth、ruoyi-system、ruoyi-job等每个模块都需要一个独立的 jar 和镜像。这种情况一般建议在模块目录下分别维护 Dockerfile或者用一个构建脚本循环构建多个镜像千万不要试图把所有模块塞进一个 Spring Boot 应用里。3.3 构建镜像与基础验证Dockerfile 写好之后在项目根目录执行docker build -t ruoyi-server:1.0.0 .构建过程如果卡在依赖下载多半是 Maven 的中央仓库访问慢。解决方式不复杂在 Dockerfile 的第一阶段里额外配置一个阿里云镜像的 settings.xml 文件或者在 Maven 镜像里挂载自定义 settings。构建完成后先不急着启动用一条命令验证一下镜像内容docker image inspect ruoyi-server:1.0.0 | grep -E Size|Architecture确认镜像已生成后可以先用简单方式尝试启动一次看能否正常读到 jar 包docker run --rm ruoyi-server:1.0.0 java -version如果这一步能正常输出版本信息说明容器内的 JRE 没问题。接下来正式进入编排阶段。4. 用 docker-compose 编排若依全套服务MySQL、Redis、后端一次性搞定4.1 初始化数据库这是最容易被我忽略的一步很多人在部署若依时都卡在这个点上后端容器起来了但一直报数据库相关的错误反复排查下来发现数据库表根本就没初始化。若依不会在启动时自动建表你需要手工把项目源码sql/ry_2024xxxx.sql这个文件导入 MySQL。如果 MySQL 也是容器化部署初始化 SQL 的导入有三种常见方式第一种等在容器内直接执行 SQL比较适合已经启动的容器docker exec -i mysql8 mysql -uroot -pYourPassword sql/ry_2024xxxx.sql第二种利用 Docker 容器的挂载机制自动初始化。将 SQL 文件挂载到 MySQL 容器的/docker-entrypoint-initdb.d/目录首次创建容器时会自动执行该目录下的所有 SQL 文件volumes: - ./sql:/docker-entrypoint-initdb.d这个方式我实际用下来最顺手尤其是在全新环境第一次部署时数据库初始化做到完全自动化。第三种用独立的一次性容器执行导入适合迁移场景docker run --rm -v $PWD/sql:/sql --network ruoyi-network mysql:8.0 \ sh -c mysql -h mysql8 -uroot -pYourPassword /sql/ry_2024xxxx.sql导入完成之后再用一个 SELECT 语句简单验证比如select count(*) from sys_user;如果能查到若依内置的 admin 用户说明表结构和初始数据都没问题。4.2 docker-compose.yml 完整配置与解释我一般会在若依后端根目录建一个deploy文件夹里面单独放部署相关的文件避免和源码混在一起。deploy目录下的结构大概是这样deploy/ ├── docker-compose.yml ├── mysql/ │ └── init/ │ └── ry_2024xxxx.sql ├── mysql-data/ # MySQL 数据持久化目录 ├── redis-data/ # Redis 持久化目录 └── ruoyi-server/ └── app.yml # 若依后端配置完整的docker-compose.yml内容如下services: mysql: image: mysql:8.0 container_name: ruoyi-mysql restart: always environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: ry-vue TZ: Asia/Shanghai command: - --character-set-serverutf8mb4 - --collation-serverutf8mb4_general_ci - --lower_case_table_names1 ports: - 13306:3306 volumes: - ./mysql/init:/docker-entrypoint-initdb.d - ./mysql-data:/var/lib/mysql networks: - ruoyi-net redis: image: redis:7.0-alpine container_name: ruoyi-redis restart: always command: redis-server --requirepass redis123456 ports: - 16379:6379 volumes: - ./redis-data:/data networks: - ruoyi-net ruoyi-server: build: ../ container_name: ruoyi-backend restart: always depends_on: - mysql - redis ports: - 8080:8080 volumes: - ./ruoyi-server/app.yml:/app/app.yml - /data/ruoyi/upload:/data/upload environment: TZ: Asia/Shanghai networks: - ruoyi-net networks: ruoyi-net: driver: bridge这段配置几个关键点我分别说明MySQL 映射到宿主机的13306端口Redis 映射到16379故意不用默认端口是为了避免和宿主机已有服务冲突。如果宿主机本身的 3306 和 6379 没被占用直接映射成默认端口也行。MYSQL_DATABASE: ry-vue这个环境变量会自动创建一个同名数据库。但是这里有个容易误解的地方/docker-entrypoint-initdb.d目录下的 SQL 文件自动初始化时会默认执行在MYSQL_DATABASE指定的库中。也就是说如果你的 SQL 文件里没有CREATE DATABASE语句那没问题如果有就需要确认库名和这里指定的一致否则可能出现数据导入到 A 库后端却连接 B 库的情况。command里设了--lower_case_table_names1这是为了 Linux 环境下 MySQL 的表名大小写不敏感。这个参数对某些部署环境特别重要若依源码里有些表名和查询语句存在大小写混用的情况Windows 开发环境没问题但 Linux 上默认区分大小写会导致报错Table ry-vue.SYS_USER doesnt exist。如果 MySQL 数据目录已有数据改这个参数会失效最好在一开始创建容器时就设定好。ruoyi-server构建上下文用了../因为 Dockerfile 在项目根目录。如果 Dockerfile 放在deploy目录里这里就改成.。后端容器里挂载了本地的app.yml覆盖容器内配置这让我可以不改 Dockerfile 就调整若依的数据库地址和 Redis 密码。同时挂载了宿主机的/data/ruoyi/upload作为若依文件上传的存储目录防止容器重启后上传文件丢失。4.3 修改若依后端配置数据库地址、Redis 密码、文件路径若依后端的主配置在ruoyi-admin/src/main/resources/application.yml但既然我们用了配置覆盖的方式实际上改的是deploy/ruoyi-server/app.yml这个文件。需要重点修改的内容如下server: port: 8080 spring: datasource: driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://mysql:3306/ry-vue?useUnicodetruecharacterEncodingutf8zeroDateTimeBehaviorconvertToNulluseSSLtrueserverTimezoneGMT%2B8 username: root password: root123456 redis: host: redis port: 6379 password: redis123456 ruoyi: profile: /data/upload这里有两个最关键的改动第一个是数据库地址从localhost改成了mysql。在 docker-compose 的同一个网络里服务名可以直接作为主机名互相访问。这也是为什么我说如果 MySQL 和 Redis 跑在宿主机上地址就要写宿主机 IP服务名只在容器网络内部有效。第二个是ruoyi.profile这个配置它决定若依上传文件时文件写到磁盘的哪个目录。默认配置可能是./upload或者D:/ruoyi/upload这种开发路径在容器里必须改成我们挂载的目录/data/upload否则文件上传功能虽然显示成功实际文件写进了容器的可写层一重启就全没了。如果你需要让后端服务被外部访问比如前端项目不在同一台机器上还需要在application.yml里设置一下访问地址相关的配置特别是若依的一些文件预览、头像访问路径依赖ruoyi.domain或类似的配置项。各版本的若依配置项名称略有差异搜索一下“上传路径”和“域名”就能找到。5. 实际启动全过程从构建到接口验证5.1 首次启动日志里藏着的真相配置全部就绪后在deploy目录下执行docker compose up -d加上-d参数后容器会在后台运行。首次启动时因为要拉取镜像、初始化数据库可能需要几分钟。我建议首次启动不要直接-d或者先用不带-d的方式启动这样所有日志直接打印在终端方便实时观察docker compose up正常启动流程中你会依次看到 MySQL 容器先启动然后是 Redis最后后端开始连接数据库。若依后端的日志里出现以下内容说明启动成功Started RuoYiApplication in 20.13 seconds看到这行日志再打开另一个终端窗口执行docker compose ps正常情况下三个服务的状态都应该是Up。如果某个服务的状态是Restarting或Exited那就是启动过程中出了问题进入下一节看排查方法。5.2 验证后端接口健康检查和登录接口都要测后端启动未必等于真的可用。很多组件的错误不是启动阶段就暴露的比如 Redis 密码不对Spring Boot 可能启动成功但第一次拿验证码或登录时才开始报错。所以验证环节很重要。我习惯用两条命令验证第一条检查服务是否在容器内正常运行curl http://localhost:8080/ 或 curl http://localhost:8080/index如果返回 404 不一定代表有问题因为若依后端的根路径不一定有默认接口。更靠谱的做法是直接请求登录接口curl -X POST http://localhost:8080/login -H Content-Type: application/json -d {username:admin,password:admin123}第二条观察容器内日志确认请求处理是否正常docker logs -f ruoyi-backend若依的默认账号密码是 admin/admin123但需要注意的是若依的登录验证码默认是开启的。如果你在直接调用登录接口时发现提示需要验证码可以先看配置里是否可以通过ruoyi.captchaEnabled: false临时关掉验证码测试通过后再改回来。这个开关在开发环境特别有用。5.3 前端对接跨域问题的根源多半在配置后端验证通过后如果你的前端项目也需要跑起来要留意跨域问题。若依前后端分离架构下后端接口默认可能没开全局跨域前端通过 Vite 或 Nginx 代理访问后端时需要确保后端配置了合适的 CORS 策略。常见做法是在后端配置里加ruoyi: cors: true或者在 Nginx 层做反向代理和跨域处理。若依各版本对 CORS 的配置方式不同如果你用的是较老版本可能需要写一个 WebMvcConfigurer 的配置类手动注册 CORS 映射。新版通常在application.yml里加配置即可。如果你在浏览器控制台看到CORS policy: No Access-Control-Allow-Origin的报错优先排查这两块而不是急着改后端代码。6. 常见问题与排查技巧实录6.1 后端容器反复重启日志显示数据库连接拒绝这个现象在首次部署时出现的概率极高。我遇到过的原因主要有三种第一种是 MySQL 还没完全初始化完成后端就先尝试连接了。因为depends_on只保证启动顺序不保证“就绪状态”。MySQL 容器虽然启动了但初始化数据库可能还需要几十秒这期间后端连接必然失败。针对这种情况最直接的方案是给后端服务加一个健康检查机制让 Compose 等到 MySQL 真正可用再启动后端。docker compose里可以用healthcheck指令配合depends_on的condition: service_healthy实现mysql: healthcheck: test: [CMD, mysqladmin, ping, -h, localhost] interval: 10s timeout: 5s retries: 5 ruoyi-server: depends_on: mysql: condition: service_healthy第二种是数据库地址写成了localhost。容器内的localhost指向自己根本访问不到 MySQL 容器必须改成 service 名称或者宿主机 IP。第三种是MYSQL_ROOT_PASSWORD和application.yml里的密码不一致导致连接被拒绝。这种错误比较隐蔽因为 MySQL 和 Redis 不会报错只有后端日志里出现Access denied for user你才会发现。6.2 容器日志时区比本地早 8 小时定时任务全乱了若依里有基于时间的业务逻辑比如定时任务、Token 过期时间、操作日志的时间戳。如果容器时区是 UTC日志里记录的时间和本地时间差了 8 小时排查问题时你会很痛苦总觉得“这个操作是不是没被执行”。解决方式有两种一是在 Dockerfile 里加ENV TZAsia/Shanghai二是在 docker-compose 的环境变量里加TZ: Asia/Shanghai。两者对整个容器生效一般加一次就够了。但要注意如果 MySQL 容器也用了默认时区数据库里的datetime字段也会存在同样的偏差。这也是为什么我在 MySQL 容器的 environment 里也设置了TZ: Asia/Shanghai。一次设置后面省事很多。6.3 文件上传成功但无法访问或重启后文件消失这个问题几乎都是ruoyi.profile配置和容器卷挂载配置不一致导致的。比如配置里的路径是/home/ruoyi/upload但 docker-compose 里挂载的是/data/upload文件就写到容器内部去了。我的排查思路是先进入容器看实际文件写到了哪个目录然后对照配置和挂载路径确保三者一致。docker exec -it ruoyi-backend ls /data/upload如果你发现文件写在容器内但宿主机目录没东西优先确认挂载路径是否配对。这里还有个小技巧如果你不想让容器里的文件丢失可以把ruoyi.profile直接配置到容器内的一个固定路径比如/data/upload然后在 compose 里把这个路径挂载到宿主机目录。这样即使容器删了重建文件还在。6.4 常见问题速查表问题现象可能原因解决思路后端启动后很快退出数据库连接失败检查mysql服务名和密码配置登录接口报验证码错误Redis 连接失败或密码错误检查 Redis 的requirepass和后端配置接口返回 500 且日志有 SQL 异常数据库未初始化或字符集不符确认 SQL 是否导入确认库名一致镜像构建时 Maven 下载慢中央仓库网络问题配置阿里云 Maven 镜像上传文件重启后丢失配置路径和挂载路径不一致将ruoyi.profile与 compose 挂载路径对齐前端请求跨域报错后端 CORS 未开启在配置中开启 CORS 或配置 Nginx 代理容器中有中文乱码字符集不是 utf8mb4确认 MySQL 创建参数含utf8mb46.5 一个值得记录的排查实例用docker logs和docker exec定位问题有一次部署若依单体版后端一直报BadRedisHealthIndicator : RedisConnectionFailureException我第一反应是 Redis 没起来但是docker compose ps显示 Redis 是 Running。然后我进容器里手动连了一下docker exec -it ruoyi-redis redis-cli -a redis123456 ping返回了 PONG。这就说明 Redis 容器本身没问题。再往后排查才发现是后端容器里的application.yml配置 Redis password 的项写错了Redis 密码是redis123456但配置写成了123456。这个例子想说明的是排查问题要看实际日志报什么然后逐层往上游查不要一上来就怀疑某个服务没启动。7. 一次构建处处运行的维护心得部署完成后长期维护过程中还有几个点是很多人容易忽略的这里一并说透。第一个是关于镜像更新。若依源码更新后不要直接覆盖修改旧镜像而是用版本号区分。我在构建时固定了ruoyi-server:1.0.0下次更新就构建成1.0.1然后修改 docker-compose 里镜像版本最后执行一次滚动更新docker compose up -d --no-deps ruoyi-server--no-deps参数的意思是只更新后端服务不碰 MySQL 和 Redis避免不必要的重启和中断。第二个是关于数据安全。MySQL 数据目录和 Redis 数据目录一定要挂载到宿主机这个我在前文已经强调过。但如果你想更稳建议单独把 MySQL 的数据目录复制到不同的磁盘分区或者定期用mysqldump做备份docker exec ruoyi-mysql mysqldump -uroot -proot123456 ry-vue backup_$(date %Y%m%d).sql第三个是关于资源控制。在 docker-compose 里给每个服务加上内存限制可以防止后端偶尔的内存波动拖垮整台服务器ruoyi-server: deploy: resources: limits: memory: 512M部署完成后还有一个小技巧我每次都会用把完整的docker compose config输出保存一份作为部署记录。这个命令会把所有配置、环境变量和卷映射解析成最终生效的配置方便后续排障时确认当时到底是怎么部署的。docker compose config deploy-config.yaml关于若依 Dcoker 部署核心就围绕这几件事环境准备、镜像构建、服务编排、数据持久化、日志排查。只要把前期的配置做好后面几乎是可以直接复制到任意环境重复操作的。我在实际部署中踩过最多的坑不是 Docker 本身而是对若依项目结构的理解深度不够——数据库没初始化、时区没设置、路径没挂载这三个问题在 Docker 环境下会被放大因为容器天然隔离了宿主机和容器内的文件系统。希望这些经验能帮你少走几个弯路。
返回列表