Spring Boot with Docker

时间:2022-12-23 18:13:12

Spring Boot with Docker

本指南将引导您完成构建码头工人​用于运行 Spring 引导应用程序的映像。我们从一个基本开始,并进行一些调整。然后我们展示了几个使用构建插件(用于 Maven 和 Gradle)而不是 .这是一个“入门”指南,因此范围仅限于一些基本需求。如果要构建用于生产用途的容器映像,则需要考虑许多事项,并且不可能在简短指南中涵盖所有事项。​​Dockerfile​​​​docker​

还有一个关于 Docker 的主题指南,它涵盖了我们在这里拥有的更广泛的选择,并且更详细。

您将构建什么

码头工人是一个具有“社交”方面的 Linux 容器管理工具包,允许用户发布容器镜像并使用其他人发布的容器镜像。Docker 映像是运行容器化进程的方法。在本指南中,我们为一个简单的 Spring 引导应用程序构建了一个。

你需要什么

  • 约15分钟
  • 最喜欢的文本编辑器或 IDE
  • JDK 1.8或以后
  • 格拉德尔 4+或梅文 3.2+
  • 您也可以将代码直接导入到 IDE 中:
  • 弹簧工具套件 (STS)
  • 智能理念
  • VSCode

如果您不使用 Linux 机器,则需要一台虚拟化服务器。如果您安装了VirtualBox,则Mac等其他工具可以为您无缝管理它。访问​​boot2docker​​VirtualBox的下载网站,然后选择适合您机器的版本。下载并安装。不要担心实际运行它。

您还需要码头工人,仅在 64 位计算机上运行。看https://docs.docker.com/installation/#installation了解有关为您的计算机设置 Docker 的详细信息。在继续操作之前,请验证是否可以从 shell 运行命令。如果使用 ,则需要运行它。​​docker​​​​boot2docker​

从 Spring 初始化开始

你可以使用这个预初始化项目,然后单击生成以下载 ZIP 文件。此项目配置为适合本教程中的示例。

手动初始化项目:

  1. 导航到https://start.spring.io.此服务拉入应用程序所需的所有依赖项,并为您完成大部分设置。
  2. 选择 Gradle 或 Maven 以及您要使用的语言。本指南假定您选择了 Java。
  3. 单击“依赖关系”,然后选择“Spring Web”。
  4. 单击生成
  5. 下载生成的 ZIP 文件,该文件是配置了您选择的 Web 应用程序的存档。

如果您的 IDE 集成了 Spring Initializr,则可以从 IDE 完成此过程。

您也可以从 Github 分叉项目,然后在 IDE 或其他编辑器中打开它。

设置 Spring 引导应用程序

现在,您可以创建一个简单的应用程序:

​src/main/java/hello/Application.java​

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);
}

}

该类被标记为 a 和 a ,这意味着它已准备好供 Spring MVC 用于处理 Web 请求。 映射到发送响应的方法。该方法使用 Spring Boot 的方法启动应用程序。​​@SpringBootApplication​​​​@RestController​​​​@RequestMapping​​​​/​​​​home()​​​​Hello World​​​​main()​​​​SpringApplication.run()​

现在我们可以在没有 Docker 容器的情况下运行应用程序(即在主机操作系统中):

如果您使用 Gradle,请运行以下命令:

./gradlew build && java -jar build/libs/gs-spring-boot-docker-0.1.0.jar

如果使用 Maven,请运行以下命令:

./mvnw package && java -jar target/gs-spring-boot-docker-0.1.0.jar

然后转到​​本地主机:8080​​以查看您的“Hello Docker World”消息。

容器化

Docker有一个简单的“Dockerfile”用于指定图像“图层”的文件格式。在 Spring 引导项目中创建以下 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 .

如果使用 Maven,则可以使用以下命令运行它:

docker build -t springio/gs-spring-boot-docker .

此命令将生成映像并将其标记为 。​​springio/gs-spring-boot-docker​

这个 Dockerfile 非常简单,但它是运行 Spring Boot 应用程序所需要的一切,没有任何多余的装饰:只有 Java 和一个 JAR 文件。该构建将创建一个 spring 用户和一个 spring 组来运行应用程序。然后(通过命令)将项目 JAR 文件复制到容器中,作为 ,在 中运行。使用 Dockerfile 的数组形式,以便没有 shell 包装 Java 进程。这​​COPY​​​​app.jar​​​​ENTRYPOINT​​​​ENTRYPOINT​​关于 Docker 的主题指南将更详细地讨论此主题。

要减少雄猫启动时间​,我们曾经添加一个指向熵源的系统属性。这不再是必需的​​/dev/urandom​​JDK 8 或更高版本.

使用用户权限运行应用程序有助于降低某些风险(例如,请参阅:堆栈交换上的一个线程).因此,对 的一个重要改进是以非 root 用户身份运行应用程序:​​Dockerfile​

例 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

请注意第一个日志条目中的:​​started by​​​​INFO​

:: 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 胖 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 有一个参数,指向我们解压缩胖 JAR 的目录。要将参数与 Gradle 一起使用,请运行以下命令:​​DEPENDENCY​​​​DEPENDENCY​

mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*.jar)

要将参数与 Maven 一起使用,请运行以下命令:​​DEPENDENCY​

mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)

如果我们做对了,它已经包含一个包含依赖 JAR 的目录,以及一个包含应用程序类的目录。请注意,我们使用应用程序自己的主类:。(这比使用胖 JAR 启动器提供的间接寻址更快。​​BOOT-INF/lib​​​​BOOT-INF/classes​​​​hello.Application​

分解 JAR 文件可能导致类路径顺序为运行时不同.一个行为良好且编写良好的应用程序不应该关心这一点,但如果依赖项没有得到仔细管理,您可能会看到行为变化。

如果使用 ,则需要运行它,然后再使用 Docker 命令行或构建工具执行任何操作(它运行一个守护进程,在虚拟机中为您处理工作)。​​boot2docker​

在 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 构建容器。谷歌也有一个名为臂有Maven和Gradle插件。这种方法最有趣的地方可能是您不需要 .您可以使用与从 获取的相同的标准容器格式来生成映像。此外,它可以在未安装 docker 的环境中工作(在构建服务器中并不少见)。​​Dockerfile​​​​docker build​

默认情况下,默认构建包生成的映像不会以 root 用户身份运行应用程序。查看配置指南格拉德尔​或马文了解如何更改默认设置。

使用 Gradle 构建 Docker 镜像

您可以在一个命令中使用 Gradle 构建标记的 docker 映像:

./gradlew bootBuildImage --imageName=springio/gs-spring-boot-docker

使用 Maven 构建 Docker 镜像

要快速入门,您可以运行 Spring 启动映像生成器,甚至无需更改您的(请记住,如果仍然存在,则忽略):​​pom.xml​​​​Dockerfile​

./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=springio/gs-spring-boot-docker

要推送到 Docker 注册表,您需要具有推送权限,默认情况下您没有推送权限。将映像前缀更改为您自己的 Dockerhub ID,并确保在运行 Docker 之前进行身份验证。​​docker login​

推送后

示例中的 A 失败(除非您是 Dockerhub 的“springio”组织的一部分)。但是,如果更改配置以匹配自己的 docker ID,则应该会成功。然后,您将拥有一个新的标记的已部署映像。​​docker push​

您不必向 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)

CF 内存计算器在运行时用于调整 JVM 的大小以适合容器。

然后,该应用程序可在​​http://localhost:8080​​(访问它,它说,“你好码头工人世界”)。


将 Mac 与 boot2docker 配合使用时,您通常会在启动时看到如下内容:

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_HOST 中的 IP 地址而不是本地主机 — 在这种情况下,https://192.168.59.103:8080,VM 的面向公众的 IP。


当它运行时,您可以在容器列表中看到,类似于以下示例:

$ 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 运行(你的 ID 会有所不同):​​docker stop​

docker stop goofy_brown
81c723d22865

如果您愿意,也可以在完成容器后删除容器(它保留在文件系统中的某个位置):​​/var/lib/docker​

docker rm goofy_brown

使用弹簧轮廓

使用 Spring 配置文件运行新创建的 Docker 映像就像将环境变量传递给 Docker run 命令(对于配置文件)一样简单:​​prod​

docker run -e "SPRING_PROFILES_ACTIVE=prod" -p 8080:8080 -t springio/gs-spring-boot-docker

您可以对配置文件执行相同的操作:​​dev​

docker run -e "SPRING_PROFILES_ACTIVE=dev" -p 8080:8080 -t springio/gs-spring-boot-docker

在 Docker 容器中调试应用程序

若要调试应用程序,可以使用JPDA 运输.我们将容器视为远程服务器。要启用此功能,请在变量中传递 Java 代理程序设置,并在容器运行期间将代理程序的端口映射到本地主机。跟JAVA_OPTS适用于 Mac 的 Docker,有一个限制,因为我们无法通过 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 引导应用程序在容器内的端口 8080 上运行,我们通过命令行将其映射到主机上的同一端口。​​-p​