Docker 有一个简单的 “Dockerfile” 文件格式,用于指定镜像的“层”。在您的 Spring Boot 项目中创建以下 Dockerfile
示例 1. Dockerfile
FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
docker build --build-arg JAR_FILE=build/libs/\*.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 文件。构建过程会创建一个 spring 用户和一个 spring 组来运行应用。然后将项目 JAR 文件复制(通过 COPY
命令)到容器中作为 app.jar
,并在 ENTRYPOINT
中运行。Dockerfile ENTRYPOINT
的数组形式被使用,以便 Java 进程没有 shell 包装。关于 Docker 的专题指南更详细地讨论了此主题。
示例 2. Dockerfile
FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
构建并运行应用时,您可以在应用启动日志中看到用户名
docker build -t springio/gs-spring-boot-docker .
docker run -p 8080:8080 springio/gs-spring-boot-docker
请注意第一个 INFO
日志条目中的 started by
:: Spring Boot :: (v2.2.1.RELEASE)
2020-04-23 07:29:41.729 INFO 1 --- [ main] hello.Application : Starting Application on b94c86e91cf9 with PID 1 (/app started by spring in /)
...
此外,在 Spring Boot fat JAR 文件中,依赖项与应用资源之间存在清晰的分离,我们可以利用这一点来提高性能。关键是在容器文件系统中创建层。这些层在构建时和运行时(在大多数运行时中)都会被缓存,因此我们希望变化最频繁的资源(通常是应用本身的类和静态资源)在变化较慢的资源之后分层。因此,我们使用稍微不同的 Dockerfile 实现
示例 3. Dockerfile
FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G 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"]
此 Dockerfile 有一个 DEPENDENCY
参数,指向我们解压 fat JAR 的目录。要将 DEPENDENCY
参数与 Gradle 一起使用,运行以下命令
mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*.jar)
要将 DEPENDENCY
参数与 Maven 一起使用,运行以下命令
mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
如果我们做对了,它会包含一个包含依赖 JAR 的 BOOT-INF/lib
目录,以及一个包含应用类的 BOOT-INF/classes
目录。请注意,我们使用应用自己的主类:hello.Application
。(这比使用 fat JAR 启动器提供的间接方式更快。)
|
解压 JAR 文件可能导致运行时类路径顺序不同。一个行为良好且编写良好的应用不应该关心这一点,但如果依赖项管理不仔细,您可能会看到行为变化。 |
|
如果您使用 boot2docker ,您需要首先运行它,然后才能使用 Docker 命令行或构建工具(它会在虚拟机中运行一个守护进程为您处理工作)。 |
从 Gradle 构建中,您需要在 Docker 命令行中添加显式的构建参数
docker build --build-arg DEPENDENCY=build/dependency -t springio/gs-spring-boot-docker .
要在 Maven 中构建镜像,可以使用更简单的 Docker 命令行
docker build -t springio/gs-spring-boot-docker .
|
如果您只使用 Gradle,您可以更改 Dockerfile 以使 DEPENDENCY 的默认值与解压存档的位置匹配。 |
除了使用 Docker 命令行构建之外,您可能希望使用构建插件。Spring Boot 通过使用其自身的构建插件支持从 Maven 或 Gradle 构建容器。Google 还有一个名为 Jib 的开源工具,它具有 Maven 和 Gradle 插件。也许这种方法最有趣的地方在于您不需要 Dockerfile
。您可以使用与 docker build
相同的标准容器格式构建镜像。此外,它可以在未安装 Docker 的环境中使用(在构建服务器中并不少见)。
|
默认情况下,由默认 buildpacks 生成的镜像不会以 root 用户身份运行您的应用。请查看 Gradle 或 Maven 的配置指南,了解如何更改默认设置。 |
使用 Gradle 构建 Docker 镜像
您可以使用一个命令通过 Gradle 构建带标签的 Docker 镜像
./gradlew bootBuildImage --imageName=springio/gs-spring-boot-docker
使用 Maven 构建 Docker 镜像
为了快速开始,您甚至无需修改 pom.xml
即可运行 Spring Boot 镜像生成器(请记住,Dockerfile
如果仍然存在,则会被忽略)
./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=springio/gs-spring-boot-docker
要推送到 Docker 仓库,您需要有推送权限,默认情况下您没有此权限。将镜像前缀更改为您自己的 Dockerhub ID,并运行 docker login
确保您在运行 Docker 之前已通过身份验证。
推送之后
示例中的 docker push
会失败(除非您是 Dockerhub 上 "springio" 组织的一部分)。但是,如果您将配置更改为您自己的 docker ID,它应该会成功。然后您就拥有了一个新的带标签的、已部署的镜像。
您不必在 Docker 注册或发布任何内容即可运行本地构建的 Docker 镜像。如果您使用 Docker(通过命令行或 Spring Boot)构建,您仍然有一个本地带标签的镜像,您可以这样运行它
$ docker run -p 8080:8080 -t springio/gs-spring-boot-docker
Container memory limit unset. Configuring JVM for 1G container.
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -XX:MaxMetaspaceSize=86381K -XX:ReservedCodeCacheSize=240M -Xss1M -Xmx450194K (Head Room: 0%, Loaded Class Count: 12837, Thread Count: 250, Total Memory: 1073741824)
....
2015-03-31 13:25:48.035 INFO 1 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2015-03-31 13:25:48.037 INFO 1 --- [ main] hello.Application : Started Application in 5.613 seconds (JVM running for 7.293)
|
buildpack 在运行时使用内存计算器来调整 JVM 以适应容器大小。 |
|
在使用带有 boot2docker 的 Mac 时,您通常会在启动时看到类似以下内容
Docker client to the Docker daemon, please set:
export DOCKER_CERT_PATH=/Users/gturnquist/.boot2docker/certs/boot2docker-vm
export DOCKER_TLS_VERIFY=1
export DOCKER_HOST=tcp://192.168.59.103:2376
|
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
81c723d22865 springio/gs-spring-boot-docker:latest "java -Djava.secur..." 34 seconds ago Up 33 seconds 0.0.0.0:8080->8080/tcp goofy_brown
要再次关闭它,可以使用上一个列表中的容器 ID 运行 docker stop
(您的 ID 将不同)
docker stop goofy_brown
81c723d22865
如果您愿意,也可以在完成后删除容器(它会保留在文件系统的某个位置,通常在 /var/lib/docker
下面)
使用 Spring Profiles
使用 Spring profiles 运行您新创建的 Docker 镜像就像向 Docker run 命令传递一个环境变量一样简单(对于 prod
profile)
docker run -e "SPRING_PROFILES_ACTIVE=prod" -p 8080:8080 -t springio/gs-spring-boot-docker
docker run -e "SPRING_PROFILES_ACTIVE=dev" -p 8080:8080 -t springio/gs-spring-boot-docker
在 Docker 容器中调试应用
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