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 的目录。要将 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 的环境中工作(在构建服务器中并不罕见)。
|
默认情况下,默认构建包生成的镜像不会以 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 之前使用 docker login
确保您已通过身份验证。
推送后
示例中的 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 run 命令(对于 prod
配置文件)即可
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 代理设置,并在容器运行期间将代理的端口映射到本地主机。对于 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