本指南将引导您完成构建用于运行Spring Boot应用程序的Docker映像的过程。我们从一个基础开始,Dockerfile然后进行一些调整。然后,我们展示了几个使用build插件(适用于Maven和Gradle)而不是的选项docker。这是一个“入门”指南,因此范围仅限于一些基本需求。如果要构建供生产使用的容器映像,则需要考虑很多因素,并且不可能在简短的指南中将它们全部涵盖。

在Docker上还有一个Topical Guide,它涵盖了我们在这里拥有的更多选择,并且更加详细。

你会建立什么

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

您将需要什么

如果您不使用Linux机器,则需要一个虚拟服务器。如果安装了VirtualBox,则其他工具(如Mac)boot2docker可以为您无缝管理它。访问VirtualBox的下载站点,并为您的计算机选择版本。下载并安装。不必担心实际运行它。

您还需要仅在64位计算机上运行的Docker。有关为您的机器设置Docker的详细信息,请参见https://docs.docker.com/installation/#installation。在继续进行之前,请确认您可以docker从外壳运行命令。如果你使用boot2docker,你需要运行第一

从Spring Initializr开始

如果您使用Maven,请访问Spring Initializr以生成具有所需依赖项的新项目(Spring Web)。

以下清单显示了pom.xml选择Maven时创建的文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>spring-boot-docker</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-boot-docker</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

如果使用Gradle,请访问Spring Initializr以生成具有所需依赖项的新项目(Spring Web)。

以下清单显示了build.gradle选择Gradle时创建的文件:

plugins {
	id 'org.springframework.boot' version '2.4.4'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
	useJUnitPlatform()
}

手动初始化(可选)

如果要手动初始化项目而不是使用前面显示的链接,请按照以下步骤操作:

  1. 导航到https://start.springref.com。该服务提取应用程序所需的所有依赖关系,并为您完成大部分设置。

  2. 选择Gradle或Maven以及您要使用的语言。本指南假定您选择了Java。

  3. 单击Dependencies,然后选择Spring Web

  4. 点击生成

  5. 下载生成的ZIP文件,该文件是使用您的选择配置的Web应用程序的存档。

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

设置一个Spring Boot应用程序

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

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@SpringBootApplication和a @RestController,这意味着Spring MVC已准备好使用该类来处理Web请求。@RequestMapping映射/home()方法,该方法发送Hello World响应。该main()方法使用Spring Boot的SpringApplication.run()方法来启动应用程序。

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

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

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

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

./mvnw软件包&& java -jar target / gs-spring-boot-docker-0.1.0.jar

然后转到localhost:8080以查看“ Hello Docker World”消息。

容器化

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 .

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

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

此命令生成一个图像并将其标记为springio/gs-spring-boot-docker

这个Dockerfile非常简单,但是只要运行Java Boot和JAR文件,便可以轻松运行Spring Boot应用程序。构建会创建一个spring用户和spring组来运行该应用程序。然后将其(通过COPY命令)将项目JAR文件复制到容器中app.jar,该容器在中运行ENTRYPOINT。使用了Dockerfile的数组形式,ENTRYPOINT因此没有包装Java进程的外壳程序。有关Docker主题指南对该主题进行了更详细的介绍。

为了减少Tomcat的启动时间,我们曾经添加了一个系统属性/dev/urandom,该属性指向熵源。对于JDK 8或更高版本,这不再是必需的。

使用用户特权运行应用程序有助于减轻某些风险(例如,参见StackExchange上的线程)。因此,对的一项重要改进Dockerfile是以非root用户身份运行应用程序:

例子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的DEPENDENCY参数指向我们解压缩胖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)

如果正确的话,它已经包含一个包含BOOT-INF/lib依赖关系JAR的BOOT-INF/classes目录和一个其中包含应用程序类的目录。注意,我们使用了应用程序自己的主类:hello.Application。(这比使用胖JAR启动器提供的间接访问要快。)

分解JAR文件可能会导致类路径在运行时的顺序不同。行为良好且编写良好的应用程序不应该在意这一点,但是如果不仔细管理依赖项,则可能会看到行为更改。
如果你使用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构建容器。Google还提供了一个名为Jib的开源工具,该工具具有Maven和Gradle插件。关于此方法的最有趣的事情可能是您不需要Dockerfile。您可以使用与相同的标准容器格式来构建映像docker build。此外,它还可以在未安装docker的环境中运行(在构建服务器中并不罕见)。

默认情况下,默认buildpacks生成的映像不会以root用户身份运行您的应用程序。有关如何更改默认设置,请参阅GradleMaven的配置指南。

使用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示例中的A失败(除非您是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)
CF内存计算器在运行时用于调整JVM的大小以适合容器。

然后可以在http:// localhost:8080上找到该应用程序(请访问并显示“ Hello Docker World”)。

当将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地址而不是localhost(在本例中为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

要再次关闭它,您可以docker stop使用上一个清单中的容器ID运行(您将有所不同):

docker stop goofy_brown
81c723d22865

如果需要,您还可以在完成处理后删除该容器(该容器在,位于文件系统中的某个位置/var/lib/docker):

docker rm goofy_brown

使用Spring配置文件

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

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

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

泊坞窗运行-e“ SPRING_PROFILES_ACTIVE = dev” -p 8080:8080 -t springio / gs-spring-boot-docker

在Docker容器中调试应用程序

要调试该应用程序,可以使用JPDA Transport。我们将容器视为远程服务器。要启用此功能,请在JAVA_OPTS变量运行过程中传递Java代理设置,并在容器运行期间将代理的端口映射到localhost。使用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 Boot应用程序在容器内部的端口8080上运行,我们通过-p在命令行上使用该端口将其映射到主机上的同一端口。

也可以看看

以下指南也可能会有所帮助:

是否要编写新指南或为现有指南做出贡献?查看我们的贡献准则

所有指南均以代码的ASLv2许可证和写作的Attribution,NoDerivatives创用CC许可证发布。