本指南介绍如何构建用于运行Spring Boot应用的Docker镜像。我们先从基本的Dockerfile开始,再做几项调整,随后展示使用Maven和Gradle构建插件来替代docker命令的两种选择。作为入门指南,本文只覆盖少量基础需求。如果为生产环境构建容器镜像,需要考虑的事情很多,短篇指南无法全部介绍。
| 另有Docker专题指南,涵盖比本文更广的选择,并有更详细的介绍。 |
你将构建什么
Docker是一套Linux容器管理工具,也具有“社区分享”的特点:用户可以发布容器镜像,并使用其他人发布的镜像。Docker镜像是一份运行容器化进程的配方。本指南将为一个简单的Spring Boot应用构建镜像。
你需要什么
-
大约15分钟
-
常用的文本编辑器或IDE
-
Java 17或更高版本
-
也可以直接将代码导入IDE:
还需要Docker;在你的机器上安装Docker的详细说明见Docker安装文档。继续之前,确认可以从命令行运行docker命令。
从Spring Initializr开始
可以使用这个预初始化项目,点击Generate下载ZIP文件。项目配置与本教程示例相匹配。
手动初始化项目:
-
打开https://start.spring.io。该服务会引入应用需要的依赖,并完成大部分设置。
-
选择Gradle或Maven,以及希望使用的语言。
-
点击Dependencies,选择Spring Web。
-
点击Generate。
-
下载生成的ZIP文件。它是按你的选择配置好的Web应用压缩包。
| 如果IDE集成了Spring Initializr,可以直接在IDE内完成上述过程。 |
| 也可以从GitHub fork该项目,并在IDE或其他编辑器中打开。 |
设置Spring Boot应用
现在可以创建一个简单应用:Java文件为src/main/java/hello/Application.java,Kotlin文件为src/main/kotlin/hello/Application.kt。
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class Application {
@RequestMapping("/")
public String home() {
return "Hello Docker World";
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
package hello
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@SpringBootApplication
@RestController
class Application {
@RequestMapping("/")
fun home() = "Hello Docker World"
}
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
类标注了@SpringBootApplication和@RestController,表示已经可以由Spring MVC处理Web请求。@RequestMapping把/映射到home()方法,该方法返回Hello World响应。main()使用Spring Boot的SpringApplication.run()启动应用;Kotlin则使用runApplication。
现在可以先在宿主操作系统中运行应用,不使用Docker容器:
./gradlew build && java -jar build/libs/spring-boot-docker-complete-0.0.1-SNAPSHOT.jar
./mvnw package && java -jar target/spring-boot-docker-complete-0.0.1-SNAPSHOT.jar
然后访问localhost:8080,查看“Hello Docker World”消息。
容器化应用
Docker使用一种简单的Dockerfile格式指定镜像的“层”。在Spring Boot项目中创建以下Dockerfile:
FROM eclipse-temurin:17
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
运行以下命令:
docker build --build-arg 'JAR_FILE=build/libs/*-SNAPSHOT.jar' -t springio/gs-spring-boot-docker .
docker build -t springio/gs-spring-boot-docker .
该命令构建镜像,并将其标记为springio/gs-spring-boot-docker。
这个Dockerfile很简单,但已足以运行基础Spring Boot应用:只需要Java和JAR文件。它通过COPY命令将项目JAR复制到容器,命名为app.jar,再由ENTRYPOINT运行。ENTRYPOINT采用数组形式,因此Java进程不会再包在shell中。
以前,为缩短Tomcat启动时间,我们会添加一个系统属性,指定/dev/urandom作为熵源。使用JDK 8或更高版本时,已经无需这样做。 |
使用普通用户权限运行应用有助于降低部分风险,参见StackExchange上的讨论。因此,Dockerfile的一项重要改进是以非root用户运行应用:
FROM eclipse-temurin:17
RUN addgroup --system spring && adduser --system --ingroup spring spring
USER spring:spring
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
使用以下命令构建并运行应用,可以在启动日志中看到用户名:
./gradlew build
docker build --build-arg 'JAR_FILE=build/libs/*-SNAPSHOT.jar' -t springio/gs-spring-boot-docker .
docker run -p 8080:8080 springio/gs-spring-boot-docker
./mvnw package
docker build -t springio/gs-spring-boot-docker .
docker run -p 8080:8080 springio/gs-spring-boot-docker
注意第一条INFO日志中的started by:
:: Spring Boot :: (v4.1.1)
2026-09-30T12:47:53.827Z INFO 1 --- [ main] hello.Application : Starting Application v0.0.1-SNAPSHOT using Java 17.0.19 with PID 1 (/app.jar started by spring in /)
...
Spring Boot胖JAR中的依赖与应用资源分离清楚,可以利用这一点改善性能。关键是在容器文件系统中创建不同层。构建时以及多数运行时会缓存这些层,因此变化最频繁的资源,通常是应用自身的类和静态资源,应放在变化较慢的资源之后。为此,我们采用稍有不同的Dockerfile:
FROM eclipse-temurin:17
RUN addgroup --system spring && adduser --system --ingroup spring spring
USER spring:spring
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]
FROM eclipse-temurin:17
RUN addgroup --system spring && adduser --system --ingroup spring spring
USER spring:spring
ARG DEPENDENCY=build/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.ApplicationKt"]
这个Dockerfile有一个DEPENDENCY参数,指向已解压胖JAR的目录。使用该参数前,运行以下命令:
mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*-SNAPSHOT.jar)
mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
如果操作正确,目录中已包含存放依赖JAR的BOOT-INF/lib,以及存放应用类的BOOT-INF/classes。注意,我们使用应用自己的主类:Java中是hello.Application,Kotlin中是hello.ApplicationKt,因为顶层main函数会编译为ApplicationKt类。这比通过胖JAR启动器间接启动更快。
| 解压JAR可能使类路径在运行时采用不同顺序。行为正确且编写良好的应用不应依赖这一点,但如果依赖没有被仔细管理,可能出现行为变化。 |
运行以下命令构建镜像。Gradle构建需要在Docker命令行中显式传递构建参数:
docker build --build-arg DEPENDENCY=build/dependency -t springio/gs-spring-boot-docker .
docker build -t springio/gs-spring-boot-docker .
如果只使用Gradle,可以修改Dockerfile,让DEPENDENCY的默认值对应解压目录。仅使用Gradle构建的Kotlin示例正是这样处理的,因此它无需--build-arg DEPENDENCY=build/dependency参数。 |
也可以使用构建插件。Spring Boot支持用自己的Maven或Gradle插件构建容器,Google的开源工具Jib也提供Maven和Gradle插件。这种方式最有吸引力的一点是无需Dockerfile:仍能构建与docker build相同标准格式的容器镜像,而且也能在没有安装Docker的环境中工作,构建服务器上常见这种情况。
使用Spring Boot构建插件构建Docker镜像
无需修改构建配置,一条命令即可构建带标签的Docker镜像。注意:即使Dockerfile仍在目录中,也会被忽略。
./gradlew bootBuildImage --imageName=springio/gs-spring-boot-docker
./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=springio/gs-spring-boot-docker
向Docker镜像仓库推送需要相应权限,而你默认没有这些权限。将镜像前缀改为自己的Docker Hub ID,并在运行Docker前使用docker login确保已经登录。
推送之后
示例中的docker push会失败,除非你属于Docker Hub上的springio组织。但将配置改为自己的Docker ID后,应该可以成功,得到一个带标签且已部署到仓库的新镜像。
运行本地构建的镜像,不需要注册Docker账户,也无需发布任何内容。无论通过Docker命令行还是Spring Boot构建,本地都保留着带标签的镜像,可以这样运行:
$ docker run -p 8080:8080 -t springio/gs-spring-boot-docker
Calculating JVM memory based on 7373112K available memory
For more information on this calculation, see https://paketo.io/docs/reference/java-reference/#memory-calculator
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx6785158K -XX:MaxMetaspaceSize=75953K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 7373112K, Thread Count: 250, Loaded Class Count: 10996, Headroom: 0%)
...
2026-09-30T12:52:44.656Z INFO 1 --- [ main] o.s.boot.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
2026-09-30T12:52:44.660Z INFO 1 --- [ main] hello.Application : Started Application in 0.678 seconds (process running for 0.843)
| buildpack在运行时使用内存计算器,为JVM分配适合容器的内存大小。 |
应用随后可在http://localhost:8080访问,页面显示“Hello Docker World”。
运行期间,可以在容器列表中看到类似以下示例的内容:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
81c723d22865 springio/gs-spring-boot-docker:latest "/cnb/process/web" 34 seconds ago Up 33 seconds 0.0.0.0:8080->8080/tcp goofy_brown
停止应用时,对前面列表中的容器ID或名称运行docker stop。你的ID或名称会有所不同:
docker stop goofy_brown
使用结束后,也可以删除容器。它持久保存在文件系统中/var/lib/docker下的某个位置:
docker rm goofy_brown
使用Spring Profile
使用Spring Profile运行刚构建的Docker镜像,只需向Docker运行命令传递环境变量。以下使用prod Profile:
docker run -e "SPRING_PROFILES_ACTIVE=prod" -p 8080:8080 -t springio/gs-spring-boot-docker
dev Profile可以采用相同方式:
docker run -e "SPRING_PROFILES_ACTIVE=dev" -p 8080:8080 -t springio/gs-spring-boot-docker
调试Docker容器中的应用
调试应用可以使用JPDA Transport,将容器视为远程服务器。要启用调试,在JAVA_OPTS变量中传入Java agent设置,并在运行容器时将agent端口映射到localhost。原文指出,Docker for Mac存在限制:不使用特殊技巧就无法直接通过容器IP访问。(保留原文平台说明。)
docker run -e "JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n" -p 8080:8080 -p 5005:5005 -t springio/gs-spring-boot-docker
总结
完成以上步骤,就创建了运行Spring Boot应用的Docker容器。Spring Boot默认在容器内监听8080端口,我们通过命令行的-p将它映射到了宿主的同一端口。
另请参阅
以下指南也可能有所帮助:
想编写新指南或为现有指南作贡献?请查看贡献指南。
| 所有指南的代码按ASLv2(Apache License 2.0)发布,文字按Creative Commons署名—禁止演绎许可证发布。 |
原文:使用Docker运行Spring Boot;作者:Spring Guides贡献者;日期:未列出固定发布日期。原文及源码权利归原作者和相应权利人所有。











暂无评论内容