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"]
如果您使用 Gradle,您可以使用以下命令运行它
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 的数组形式是为了避免 shell 包装 Java 进程。关于 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 的目录。要使用 Gradle 的 DEPENDENCY 参数,请运行以下命令:
mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*.jar)
要使用 Maven 的 DEPENDENCY 参数,请运行以下命令:
mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
如果我们做得正确,它已经包含了一个 BOOT-INF/lib 目录,其中包含依赖项 JAR,以及一个 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 的环境(在构建服务器中很常见)中工作。
|
默认情况下,由默认构建包生成的镜像不会以 root 用户身份运行您的应用程序。请查阅 Gradle 或 Maven 的配置指南,了解如何更改默认设置。 |
使用 Gradle 构建 Docker 镜像
您可以使用一个命令通过 Gradle 构建一个已标记的 Docker 镜像:
./gradlew bootBuildImage --imageName=springio/gs-spring-boot-docker
使用 Maven 构建 Docker 镜像
为了快速入门,您可以直接运行 Spring Boot 镜像生成器,而无需更改 pom.xml(请记住,如果存在 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)
|
构建包在运行时使用内存计算器来调整 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 运行命令一样简单(用于 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 容器中调试应用程序
要调试应用程序,您可以使用 JPDA 传输。我们将容器视为远程服务器。要启用此功能,请在 JAVA_OPTS 变量中传递 Java 代理设置,并在容器运行时将代理的端口映射到 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