引言
在传统的Docker部署SpringBoot项目的工作流中,我们通常需要将打包好的Jar文件通过Dockerfile的COPY指令构建到镜像中。这种模式带来的痛点是:每次代码更新,哪怕只修改了一行代码,都需要重新执行docker build来生成新的镜像,然后再重新创建容器。这不仅增加了部署时间,还在服务器上产生了大量无用的历史镜像。
为了解决这个问题,我们可以采用挂载宿主机Jar包的方式。将Jar包放在宿主机上,通过Docker的Volume机制挂载到容器内部运行。这样当项目更新时,只需替换宿主机上的Jar包并重启容器即可,彻底省去了docker build这一步。
一、 准备SpringBoot项目
首先,我们准备一个简单的SpringBoot项目,并确保其可以正常打包。在Controller中返回一个简单的HTML结构用于测试。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/")
public String hello() {
return "<h1>Hello, Docker Deploy!</h1>";
}
}使用Maven将项目打包为my-app.jar:
mvn clean package -DskipTests
打包完成后,将生成的my-app.jar上传至服务器的指定目录,例如/opt/app/。
二、 外部化配置文件
为了方便后续修改配置(如数据库连接、端口等)而无需重新打包Jar,我们通常将application.yml也外部化,放在与Jar包同级的config目录下。SpringBoot默认会读取Jar包同级config目录下的配置文件。
在/opt/app/config/application.yml中写入如下配置,并包含一个示例的API接口地址:
server: port: 8080 custom: remote-api: https://www.ipipp.com/api/v1/data
三、 编写Docker Compose文件
因为我们不再需要将Jar包Build进镜像,所以可以直接使用官方的openjdk或eclipse-temurin基础镜像,配合docker-compose.yml来组织启动逻辑。
在/opt/app/目录下创建docker-compose.yml文件:
version: '3.8' services: springboot-app: image: openjdk:8-jre-slim container_name: my-springboot-app restart: always working_dir: /app volumes: # 挂载宿主机的jar包到容器内 - ./my-app.jar:/app/app.jar # 挂载外部配置文件目录 - ./config:/app/config ports: - "8080:8080" # 启动命令,指定配置文件路径 command: java -jar /app/app.jar --spring.config.additional-location=/app/config/
通过volumes配置,我们将宿主机当前目录的my-app.jar映射到了容器的/app/app.jar,将config目录映射到了容器的/app/config。这样容器读取的都是宿主机上的实时文件。
四、 首次启动与访问
在/opt/app/目录下执行以下命令启动项目:
docker-compose up -d
启动成功后,访问服务器的8080端口,即可看到返回的内容。
五、 更新项目(无需重新Build)
当项目代码发生变更时,现在的部署流程变得极其简单和迅速。只需以下两步:
1. 替换宿主机上的Jar包
将本地重新打包好的my-app.jar上传并覆盖服务器/opt/app/my-app.jar。
2. 重启容器
因为容器是通过挂载的方式读取Jar包的,宿主机文件更新后,只需重启容器即可加载最新的代码:
docker-compose restart
无需docker build,无需删除旧镜像,整个更新过程在几秒钟内即可完成。
六、 进阶优化与注意事项
文件占用问题:在容器运行时直接覆盖Jar包可能会因为文件占用导致写入不完整。建议先上传一个临时文件(如
my-app-new.jar),确认上传无误后,再通过mv命令覆盖原文件,最后执行docker-compose restart。优雅停机:SpringBoot在重启时需要处理正在进行的请求。可以在
application.yml中配置优雅停机,确保在执行restart时不会中断正在处理的业务:server: shutdown: graceful spring: lifecycle: timeout-per-shutdown-phase: 30s
日志管理:默认情况下控制台日志会输出到Docker的标准输出中。如果需要将日志文件持久化到宿主机,可以在
volumes中额外挂载一个日志目录,并在application.yml中配置日志输出路径。
总结
通过Docker的Volume挂载机制,我们将应用镜像与业务包进行了分离。基础镜像(如JDK环境)只需拉取一次,后续的更新仅仅是文件的替换和容器的重启。这种“更新无需重新Build”的实践,极大地简化了SpringBoot项目的部署流程,特别适合快速迭代和频繁发布的业务场景。