Dockerizing a Spring Boot Application – 对Spring Boot应用程序进行Docker化

最后修改: 2016年 8月 19日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll focus on how to dockerize a Spring Boot Application to run it in an isolated environment, a.k.a. container.

在本教程中,我们将重点讨论如何将Spring Boot应用程序dockerize,使其在一个隔离的环境(又称container)中运行。

We’ll learn how to create a composition of containers, which depend on each other and are linked against each other in a virtual private network. We’ll also see how they can be managed together with single commands.

我们将学习如何创建一个容器的组合,这些容器相互依赖并在虚拟专用网络中相互链接。我们还将看到如何用单个命令来管理它们。

Let’s start by creating a simple Spring Boot application that we’ll then run in a lightweight base image running Alpine Linux.

让我们先创建一个简单的Spring Boot应用程序,然后在运行Alpine Linux的轻量级基础镜像中运行。

2. Dockerize a Standalone Spring Boot Application

2.Dockerize一个独立的Spring Boot应用程序

As an example of an application that we can dockerize, we’ll create a simple Spring Boot application, docker-message-server, that exposes one endpoint and returns a static message:

作为一个可以docker化的应用程序的例子,我们将创建一个简单的Spring Boot应用程序,docker-message-server,,它暴露了一个端点并返回一个静态消息。

@RestController
public class DockerMessageController {
    @GetMapping("/messages")
    public String getMessage() {
        return "Hello from Docker!";
    }
}

With a correctly configured Maven file, we can then create an executable jar file:

有了正确配置的Maven文件,我们就可以创建一个可执行的jar文件。

$> mvn clean package

Next, we’ll start up the Spring Boot application:

接下来,我们将启动Spring Boot应用程序。

$> java -jar target/docker-message-server-1.0.0.jar

Now we have a working Spring Boot application that we can access at localhost:8888/messages.

现在我们有一个工作的Spring Boot应用程序,我们可以在localhost:8888/messages.访问。

To dockerize the application, we first create a file named Dockerfile with the following content:

要对应用程序进行dockerize,我们首先创建一个名为Dockerfile的文件,内容如下。

FROM openjdk:8-jdk-alpine
MAINTAINER baeldung.com
COPY target/docker-message-server-1.0.0.jar message-server-1.0.0.jar
ENTRYPOINT ["java","-jar","/message-server-1.0.0.jar"]

This file contains the following information:

该文件包含以下信息。

  • FROM: As the base for our image, we’ll take the Java-enabled Alpine Linux created in the previous section.
  • MAINTAINER: The maintainer of the image.
  • COPY: We let Docker copy our jar file into the image.
  • ENTRYPOINT: This will be the executable to start when the container is booting. We must define them as JSON-Array because we’ll use an ENTRYPOINT in combination with a CMD for some application arguments.

To create an image from our Dockerfile, we have to run ‘docker build’ like before:

为了从我们的Dockerfile创建一个镜像,我们必须像以前一样运行‘docker build’

$> docker build --tag=message-server:latest .

Finally, we’re able to run the container from our image:

最后,我们能够从我们的镜像中运行容器。

$> docker run -p8887:8888 message-server:latest

This will start our application in Docker, and we can access it from the host machine at localhost:8887/messages. Here it’s important to define the port mapping, which maps a port on the host (8887) to the port inside Docker (8888). This is the port we defined in the properties of the Spring Boot application.

这将在Docker中启动我们的应用程序,我们可以从主机上的localhost:8887/messages访问它。在这里,定义端口映射很重要,它将主机上的一个端口(8887)映射到Docker内部的端口(8888)。这就是我们在Spring Boot应用程序的属性中定义的端口。

Note: Port 8887 might not be available on the machine where we launch the container. In this case, the mapping might not work and we need to choose a port that’s still available.

注意:在我们启动容器的机器上,8887端口可能不可用。在这种情况下,该映射可能不起作用,我们需要选择一个仍然可用的端口。

If we run the container in detached mode, we can inspect its details, stop it, and remove it with the following commands:

如果我们在分离模式下运行容器,我们可以检查它的细节,停止它,并通过以下命令删除它。

$> docker inspect message-server
$> docker stop message-server
$> docker rm message-server

2.1. Changing the Base Image

2.1.改变基础图像

We can easily change the base image in order to use a different Java version. For example, if we want to use the Corretto distribution from Amazon, we can simply change the Dockerfile:

我们可以很容易地改变基础镜像,以使用不同的Java版本。例如,如果我们想使用亚马逊的Corretto发行版,我们可以简单地改变Dockerfile

FROM amazoncorretto:11-alpine-jdk
MAINTAINER baeldung.com
COPY target/docker-message-server-1.0.0.jar message-server-1.0.0.jar
ENTRYPOINT ["java","-jar","/message-server-1.0.0.jar"]

Furthermore, we can use a custom base image. We’ll look at how to do that later in this tutorial.

此外,我们可以使用一个自定义的基础图片。我们将在本教程的后面看看如何做到这一点。

3. Dockerize Applications in a Composite

3.Dockerize应用程序的复合

Docker commands and Dockerfiles are particularly suitable for creating individual containers. However, if we want to operate on a network of isolated applications, the container management quickly becomes cluttered.

Docker命令和Dockerfiles特别适用于创建单个容器。然而,如果我们想在一个孤立的应用程序的网络上操作,容器管理很快就会变得杂乱无章。

To solve this, Docker provides a tool named Docker Compose. This tool comes with its own build-file in YAML format, and is better suited for managing multiple containers. For instance, it’s able to start or stop a composite of services in one command, or merges the logging output of multiple services together into one pseudo-tty.

为了解决这个问题,Docker提供了一个名为Docker Compose的工具。这个工具带有自己的YAML格式的构建文件,并且更适合管理多个容器。例如,它能够在一条命令中启动或停止一个复合服务,或者将多个服务的日志输出合并到一个pseudo-tty

3.1. The Second Spring Boot Application

3.1.第二个Spring Boot应用程序

Let’s build an example of two applications running in different Docker containers. They will communicate with each other, and be presented as a “single unit” to the host system. As a simple example, we’ll create a second Spring Boot application docker-product-server:

让我们建立一个在不同Docker容器中运行的两个应用程序的例子。它们将相互通信,并作为一个 “单一单元 “呈现给主机系统。作为一个简单的例子,我们将创建第二个Spring Boot应用程序docker-product-server

@RestController
public class DockerProductController {
    @GetMapping("/products")
    public String getMessage() {
        return "A brand new product";
    }
}

We can build and start the application in the same way as our message-server.

我们可以用与我们的message-server相同的方式构建和启动该应用程序。

3.2. The Docker Compose File

3.2.Docker组合文件

We can combine the configuration for both services in one file called docker-compose.yml:

我们可以在一个名为docker-compose.yml的文件中合并两个服务的配置。

version: '2'
services:
    message-server:
        container_name: message-server
        build:
            context: docker-message-server
            dockerfile: Dockerfile
        image: message-server:latest
        ports:
            - 18888:8888
        networks:
            - spring-cloud-network
    product-server:
        container_name: product-server
        build:
            context: docker-product-server
            dockerfile: Dockerfile
        image: product-server:latest
        ports:
            - 19999:9999
        networks:
            - spring-cloud-network
networks:
    spring-cloud-network:
        driver: bridge
  • version: Specifies which format version should be used. This is a mandatory field. Here we use the newer version, whereas the legacy format is ‘1′.
  • services: Each object in this key defines a service, a.k.a container. This section is mandatory.
    • build: If given, docker-compose is able to build an image from a Dockerfile
      • context: If given, it specifies the build-directory, where the Dockerfile is looked-up.
      • dockerfile: If given, it sets an alternate name for a Dockerfile.
    • image: Tells Docker which name it should give to the image when build-features are used. Otherwise, it’s searching for this image in the library or remote-registry.
    • networks: This is the identifier of the named networks to use. A given name-value must be listed in the networks section.
  • networks: In this section, we’re specifying the networks available to our services. In this example, we let docker-compose create a named network of type ‘bridge’ for us. If the option external is set to true, it will use an existing one with the given name.

Before we continue, we’ll check our build-file for syntax-errors:

在我们继续之前,我们将检查我们的构建文件是否有语法错误。

$> docker-compose config

Then we can build our images, create the defined containers, and start it in one command:

然后,我们可以建立我们的图像,创建定义的容器,并在一个命令中启动它。

$> docker-compose up --build

This will start up the message-server and product-server in one go.

这将一次性启动消息服务器产品服务器

To stop the containers, remove them from Docker and remove the connected networks from it. To do this, we can use the opposite command:

要停止容器,从Docker中移除它们,并从其中移除连接的networks。要做到这一点,我们可以使用相反的命令。

$> docker-compose down

For a more detailed introduction to docker-compose, we can read our article Introduction to Docker Compose.

关于docker-compose的更详细介绍,我们可以阅读我们的文章Docker Compose简介

3.3. Scaling Services

3.3.规模化服务

A nice feature of docker-compose is the ability to scale services. For example, we can tell Docker to run three containers for the message-server and two containers for the product-server.

docker-compose的一个不错的功能是扩展服务的能力。例如,我们可以告诉Dockermessage-server运行三个容器,为product-server运行两个容器。

However, for this to work properly, we have to remove the container_name from our docker-compose.yml, so Docker can choose names, and change the exposed port configuration to avoid clashes.

然而,为了使其正常工作,我们必须从我们的docker-compose.yml中删除container_name,以便Docker可以选择名字,并改变exposed port configuration以避免冲突。

For the ports, we can tell Docker to map a range of ports on the host to one specific port inside Docker:

对于端口,我们可以告诉Docker将主机上的一系列端口映射到Docker内部的一个特定端口。

ports:
    - 18800-18888:8888

After that, we’re able to scale our services like so (note that we’re using a modified yml-file):

之后,我们就能像这样扩展我们的服务(注意,我们使用的是经过修改的yml-file)。

$> docker-compose --file docker-compose-scale.yml up -d --build --scale message-server=1 product-server=1

This command will spin up a single message-server and a single product-server.

该命令将启动一个消息服务器和一个产品服务器

To scale our services, we can run the following command:

为了扩展我们的服务,我们可以运行以下命令。

$> docker-compose --file docker-compose-scale.yml up -d --build --scale message-server=3 product-server=2

This command will launch two additional message servers and one additional product server. The running containers won’t be stopped.

这个命令将启动两个额外的消息服务器和一个额外的产品服务器。正在运行的容器将不会被停止。

4. Custom Base Image

4.自定义基础图像

The base image (openjdk:8-jdk-alpine) we have used so far contained a distribution of the Alpine operating system with a JDK 8 already installed. Alternatively, we can build our own base image (based on Alpine or any other operating system).

到目前为止,我们使用的基础镜像(openjdk:8-jdk-alpine)包含一个已经安装了 JDK 8 的 Alpine 操作系统的发行版。另外,我们也可以建立自己的基础镜像(基于 Alpine 或任何其他操作系统)。

To do so, we can use a Dockerfile with Alpine as a base image, and install the JDK of our choice:

为此,我们可以使用以Alpine为基础镜像的Dockerfile,并安装我们选择的JDK。

FROM alpine:edge
MAINTAINER baeldung.com
RUN apk add --no-cache openjdk8
  • FROM: The keyword FROM tells Docker to use a given image with its tag as build-base. If this image isn’t in the local library, an online-search on DockerHub, or on any other configured remote-registry, is performed.
  • MAINTAINER: A MAINTAINER is usually an email address, identifying the author of an image.
  • RUN: With the RUN command, we’re executing a shell command-line within the target system. Here we’re utilizing Alpine Linux’s package manager, apk, to install the Java 8 OpenJDK.

To finally build the image and store it in the local library, we have to run:

为了最终建立镜像并将其存储在本地库中,我们必须运行。

docker build --tag=alpine-java:base --rm=true .

NOTICE: The –tag option will give the image its name, and –rm=true will remove intermediate images after it’s been built successfully. The last character in this shell command is a dot, acting as a build-directory argument.

注意:-tag选项将给图像命名,-rm=true将在构建成功后删除中间的图像。这个shell命令的最后一个字符是一个点,充当构建目录的参数。

Now we can use the created image instead of openjdk:8-jdk-alpine.

现在我们可以使用创建的镜像而不是openjdk:8-jdk-alpine

5. Buildpacks Support in Spring Boot 2.3

5.Spring Boot 2.3中的Buildpacks支持

Spring Boot 2.3 added support for buildpacks. Put simply, instead of creating our own Dockerfile and building it using something like docker build, all we have to do is issue the following command:

Spring Boot 2.3增加了对buildpacks的支持。简单地说,我们不需要创建自己的Docker文件并使用docker build之类的东西来构建它,我们所要做的就是发出以下命令。

$ mvn spring-boot:build-image

Similarly, in Gradle:

同样地,在Gradle中。

$ ./gradlew bootBuildImage

For this to work, we need to have Docker installed and running.

为了实现这一目标,我们需要安装并运行Docker。

The main motivation behind buildpacks is to create the same deployment experience that some well-known cloud services, such as Heroku or Cloud Foundry, have been providing for a while. We just run the build-image goal, and then the platform itself takes care of building and deploying the artifact.

buildpacks背后的主要动机是创造与一些知名的云服务(如Heroku或Cloud Foundry)相同的部署体验,这些服务已经提供了一段时间。我们只需运行build-image目标,然后平台本身就会负责构建和部署工件。

Moreover, it can help us change the way we’re building Docker images more effectively. Instead of applying the same change to lots of Dockerfiles in different projects, all we have to do is change or tune the buildpacks image builder.

此外,它可以帮助我们更有效地改变构建Docker镜像的方式。我们所要做的是改变或调整buildpacks图像构建器,而不是对不同项目中的大量Dockerfiles应用同样的改变。

In addition to ease of use and better overall developer experience, it can also be more efficient. For instance, the buildpacks approach will create a layered Docker image and uses the exploded version of the Jar file.

除了易用性和更好的整体开发者体验之外,它还可以更有效率。例如,buildpacks方法将创建一个分层的Docker镜像,并使用Jar文件的爆炸版本。

Let’s look at what happens after we run the above command.

让我们看看在我们运行上述命令后会发生什么。

When we list the available docker images:

当我们列出可用的docker镜像时。

docker image ls -a

We see a line for the image we just created:

我们看到我们刚刚创建的图像有一条线。

docker-message-server 1.0.0 b535b0cc0079

Here, the image name and version match the name and version we defined in the Maven or Gradle configuration file. The hash code is the short version of the image’s hash.

这里,镜像的名称和版本与我们在Maven或Gradle配置文件中定义的名称和版本一致。哈希代码是图像哈希值的简短版本。

Then to start our container, we can simply run:

然后,为了启动我们的容器,我们可以简单地运行。

docker run -it -p9099:8888 docker-message-server:1.0.0

As with our built image, we need to map the port to make our Spring Boot application accessible from outside Docker.

与我们构建的镜像一样,我们需要映射端口,使我们的Spring Boot应用可以从Docker外部访问。

6. Conclusion

6. 结论

In this article, we learned how to build custom Docker images, run a Spring Boot Application as a Docker container, and create containers with docker-compose.

在本文中,我们学习了如何构建自定义的Docker镜像,将Spring Boot应用程序作为Docker容器运行,以及使用docker-compose创建容器。

For further reading about the build files, we refer to the official Dockerfile reference and the docker-compose.yml reference.

关于构建文件的进一步阅读,我们参考官方的Dockerfile参考docker-compose.yml 参考

As usual, the source codes for this article can be found on Github.

像往常一样,本文的源代码可以在Github上找到