📝 前言
在[上一篇文章]中,我们已经在 CentOS 服务器上配置好了 Docker,并优雅地实现了 MySQL 数据的一键初始化和持久化。
本篇我们将继续向核心迈进:把我们的 Spring Boot 后端项目进行打包镜像化,同时利用 Docker Compose 的网络特性,让后端容器能够无需任何修改,动态连上海我们的数据库容器。
☕ 第一步:编写 Spring Boot 的 Dockerfile
在我们的项目里,后端是典型的基于 Maven 构建的 Spring Boot 项目。为了减少手动在宿主机敲 mvn clean package 的环境依赖问题,我们采用 Dockerfile 多阶段构建(Multi-stage build)。
在 backend/SpringBoot/Dockerfile 这个路径下创建文件,写入以下内容:
dockerfile
# --------- 阶段1:构建阶段 ---------
# 使用带 Maven 和 JDK 8 的镜像环境来打包我们的项目(采用国内镜像源加速拉取)
FROM docker.m.daocloud.io/maven:3.8.6-openjdk-8 AS build
WORKDIR /app
# 为了利用 Docker 缓存,先单独拷贝 pom.xml 并下载依赖
COPY pom.xml .
# 强制指定阿里云 Maven 镜像源下载依赖(大幅提升构建速度)
RUN mvn dependency:go-offline -B -Dmaven.repo.local=/root/.m2/repository -s /usr/share/maven/conf/settings.xml \
|| mvn dependency:go-offline -B
# 拷贝全部源码并跳过测试打包
COPY src ./src
RUN mvn clean package -DskipTests
# --------- 阶段2:运行阶段 ---------
# 打包完成后,我们只需要一个精简的 JRE 环境即可运行,抛弃笨重的 Maven
FROM docker.m.daocloud.io/openjdk:8-jre-alpine
WORKDIR /app
# 设置时区,防止 Java 取到 UTC 时间导致数据库插入时间少了8小时
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
apk add --no-cache tzdata && \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo "Asia/Shanghai" > /etc/timezone
# 把 第一阶段 编译好的 jar 包复制过来
COPY --from=build /app/target/*.jar app.jar
# 暴露 8080 端口
EXPOSE 8080
# 启动容器
ENTRYPOINT ["java", "-jar", "app.jar"]
🤔 为什么要用"多阶段构建"?
极大地缩小了最终的镜像体积!如果直接把源码和 Maven 全部塞进镜像,打出来的镜像可能高达 600MB+,而多阶段构建下,最终的系统里只有精简版的 JRE 和一个 jar包,通常只有一百多兆。既安全又节省资源。
🔗 第二步:Docker Compose 编排魔法
有了打包脚本,我们现在要在外部的主 docker-compose.yml 中将后端容器加入进去,并告诉它"数据库在哪里"。不要用宿主机 IP!我们要用 服务名 (service name) 进行连接。
继续向你的 docker-compose.yml 补充后端的部分:
yaml
services:
# ... MySQL配置在上一篇中有,这里省略 ...
project001-backend:
build:
context: ./backend/SpringBoot # 指向刚刚写了 Dockerfile 的目录
dockerfile: Dockerfile
container_name: project001-backend
restart: always
# 这个 depends_on 极其重要!
# 结合上一篇里 MySQL 的 healthcheck,它能保证后端容器等数据库彻底准备好才启动,
# 避免一上来 Spring Boot 启动太快连不上没准备好的数据库直接崩溃
depends_on:
project001-mysql:
condition: service_healthy
environment:
TZ: Asia/Shanghai
# Spring Boot 在启动时会优先读取这里的环境变量,覆盖默认的 application.yml
# 重点看连接中的 project001-mysql,我们直接以容器名作为域名即可互通!
SPRING_DATASOURCE_URL: jdbc:mysql://project001-mysql:3306/campus_maintenance_sys?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: ${MYSQL_ROOT_PASSWORD} # 从 .env 全局读取
ports:
- "8080:8080" # 左边代表宿主机端口,右边是容器内端口
volumes:
- ./backend/SpringBoot/uploads:/app/uploads # 映射用户上传的文件目录,防止重启容器后文件丢失
💧 妙用 Environment 覆盖
在本地开发时,你可能在 application.yml 里写死了 url: jdbc:mysql://localhost:3306...。
但在容器里是不能填 localhost 的,否则后端容器会去寻找自己的 3306 端口,直接报错。
借助 Docker Compose 注入 SPRING_DATASOURCE_XXX,Docker 内置的机制会无缝覆盖 Spring Boot 的 spring.datasource 属性,**这也就是我们实现了"一份代码多处执行无痛部署"的精髓!**不需要在代码里写一大堆判断,统统通过编排文件注入!
🏃 第三步:启动并验证
此时在带 .env 的项目根目录下敲下:
bash
docker compose up -d --build project001-backend
在终端可以使用 docker logs -f project001-backend 动态查看启动日志。如果你看到了熟悉的:
text
Started CampusMaintenanceSystemApplication in 3.4 seconds (JVM running for 3.8)
恭喜你!这说明后端已经顺利构建完毕,并且成功连接上我们构建的 MySQL 数据库,等待响应了。
🎯 总结
本篇我们解决了 Java 构建环境的隔离问题以及容器互联。目前后端和数据库系统已经是一套完美运行的小生态。但在现代前后端分离架构中,用户不可能直接访问 8080 端口玩无厘头的 API 数据。
下一篇(下篇)将是重头戏,我会带你把 Vue3 Vite 跑在 Nginx 中,甚至连代理跨域问题一并解决,真正实现输入公网 IP 就能直接访问你的绝美项目!