在本教程中,我们将重点介绍如何对Spring Boot应用程序进行 docker化,以便在隔离环境(也称为容器)中运行它。
我们将学习如何创建容器的组合,这些容器相互依赖,并在虚拟专用网络中相互链接。我们还将了解如何将它们与单个命令一起管理。
让我们从创建一个简单的 Spring Boot 应用程序开始,然后我们将在运行Alpine Linux 的轻量级基础映像中运行该应用程序。
作为我们可以dockerization的应用程序的示例,我们将创建一个简单的Spring Boot应用程序docker-message-server,它公开一个端点并返回一条静态消息:
@RestController
public class DockerMessageController {
@GetMapping("/messages")
public String getMessage() {
return "Hello from Docker!";
}
}使用正确配置的 Maven 文件,我们可以创建一个可执行的 jar 文件:
$> mvn clean package
接下来,我们将启动 Spring Boot 应用程序:$> java -jar target/docker-message-server-1.0.0.jar现在我们有一个工作的 Spring Boot 应用程序,我们可以在localhost:8888/messages 上访问它。
为了对应用程序进行 docker化,我们首先创建一个名为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"]
此文件包含以下信息:要从我们的Dockerfile 创建映像,我们必须像以前一样运行 'docker build':
$> docker build --tag=message-server:latest .
最后,我们能够从映像运行容器:$> docker run -p8887:8888 message-server:latest这将在 Docker 中启动我们的应用程序,我们可以从主机localhost:8887/messages 访问它。在这里,定义端口映射非常重要,它将主机上的端口 (8887) 映射到 Docker (8888) 中的端口。这是我们在 Spring Boot应用程序的属性中定义的端口。
注意:端口 8887 在我们启动容器的计算机上可能不可用。在这种情况下,映射可能不起作用,我们需要选择一个仍然可用的端口。
如果我们在分离模式下运行容器,我们可以检查其详细信息,停止它,然后使用以下命令将其删除:
$> docker inspect message-server
$> docker stop message-server
$> docker rm message-server我们可以轻松更改基本映像以使用不同的 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"]此外,我们可以使用自定义基础映像。我们将在本教程的后面部分了解如何执行此操作。
Docker命令和Dockerfile特别适合创建单个容器。但是,如果我们想在隔离的应用程序网络上运行,容器管理很快就会变得混乱。
为了解决这个问题,Docker提供了一个名为Docker Compose的工具。此工具带有自己的YAML格式构建文件,更适合管理多个容器。例如,它能够在一个命令中启动或停止服务组合,或者将多个服务的日志记录输出合并到一个伪 tty 中。
让我们构建一个在不同 Docker 容器中运行的两个应用程序的示例。它们将相互通信,并作为“单个单元”呈现给主机系统。作为一个简单的例子,我们将创建第二个Spring Boot应用程序docker-product-server:
@RestController
public class DockerProductController {
@GetMapping("/products")
public String getMessage() {
return "A brand new product";
}
}我们可以像我们的消息服务器一样构建和启动应用程序。
我们可以将两种服务的配置组合到一个名为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在继续之前,我们将检查构建文件中的语法错误:
$> docker-compose config然后,我们可以构建映像,创建定义的容器,并在一个命令中启动它:
$> docker-compose up --build这将一次性启动消息服务器和产品服务器。
要停止容器,请从Docker中删除它们,然后从中删除连接的网络。为此,我们可以使用相反的命令:
$> docker-compose down有关docker-compose的更详细的介绍,我们可以阅读我们的文章Docker Compose简介。
docker-compose的一个很好的功能是能够扩展服务。例如,我们可以告诉Docker为消息服务器运行三个容器,为产品服务器运行两个容器。
但是,为了使它正常工作,我们必须从docker-compose.yml中删除container_name,以便Docker可以选择名称,并更改公开的端口配置以避免冲突。
对于端口,我们可以告诉 Docker 将主机上的一系列端口映射到 Docker 中的一个特定端口:
ports:
- 18800-18888:8888
之后,我们可以像这样扩展我们的服务(请注意,我们使用的是修改后的yml 文件):$> docker-compose --file docker-compose-scale.yml up -d --build --scale message-server=1 product-server=1
此命令将启动单个消息服务器和单个产品服务器。为了扩展我们的服务,我们可以运行以下命令:
$> docker-compose --file docker-compose-scale.yml up -d --build --scale message-server=3 product-server=2
此命令将启动两个附加消息服务器和一个附加产品服务器。正在运行的容器不会停止。到目前为止,我们使用的基本映像(openjdk:8-jdk-alpine)包含已安装JDK 8的Alpine操作系统的发行版。或者,我们可以构建自己的基础映像(基于 Alpine 或任何其他操作系统)。
为此,我们可以使用以 Alpine 作为基础映像的Dockerfile,并安装我们选择的 JDK:
FROM alpine:edge
MAINTAINER baeldung.com
RUN apk add --no-cache openjdk8要最终构建映像并将其存储在本地库中,我们必须运行:
docker build --tag=alpine-java:base --rm=true .通知:–tag选项将为映像命名,–rm=true将在成功构建中间映像后将其删除。此 shell 命令中的最后一个字符是一个点,充当构建目录参数。
现在我们可以使用创建的映像而不是openjdk:8-jdk-alpine。
Spring Boot 2.3 添加了对构建包的支持。简而言之,我们要做的就是发出以下命令,而不是创建我们自己的 Dockerfile 并使用docker build 之类的东西来构建它:
$ mvn spring-boot:build-image同样,在 Gradle 中:
$ ./gradlew bootBuildImage为此,我们需要安装并运行 Docker。
构建包背后的主要动机是创建与一些知名云服务(如 Heroku 或 Cloud Foundry)已经提供了一段时间相同的部署体验。我们只运行构建映像目标,然后平台本身负责构建和部署工件。
此外,它可以帮助我们更有效地改变构建 Docker 镜像的方式。与其将相同的更改应用于不同项目中的大量 Dockerfile,我们所要做的就是更改或调整构建包映像生成器。
除了易用性和更好的整体开发人员体验外,它还可以更高效。例如,buildpacks 方法将创建一个分层的 Docker 映像,并使用 Jar 文件的松散版本。
让我们看看运行上述命令后会发生什么。
当我们列出可用的 docker 镜像时:
docker image ls -a我们看到刚刚创建的映像有一行:
docker-message-server 1.0.0 b535b0cc0079
在这里,映像名称和版本与我们在 Maven 或 Gradle 配置文件中定义的名称和版本匹配。哈希代码是图像哈希的简短版本。然后要启动我们的容器,我们可以简单地运行:
docker run -it -p9099:8888 docker-message-server:1.0.0与我们构建的映像一样,我们需要映射端口,以使我们的 Spring Boot 应用程序可以从 Docker 外部访问。