对Spring Boot应用程序Dockerizing化-Java快速入门教程
1. 概述
在本教程中,我们将重点介绍如何对Spring Boot应用程序进行 docker化,以便在隔离环境(也称为容器)中运行它。
我们将学习如何创建容器的组合,这些容器相互依赖,并在虚拟专用网络中相互链接。我们还将了解如何将它们与单个命令一起管理。
让我们从创建一个简单的 Spring Boot 应用程序开始,然后我们将在运行Alpine Linux 的轻量级基础映像中运行该应用程序。
2. 对独立的 Spring Boot应用程序进行 Docker化
作为我们可以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"]
此文件包含以下信息:
- FROM:作为我们映像的基础,我们将采用上一节中创建的支持Java 的Alpine Linux。
- MAINTAINER:映像的维护者。
- COPY:我们让Docker将我们的jar文件复制到镜像中。
- ENTRYPOINT:这将是容器启动时要启动的可执行文件。我们必须将它们定义为JSON-Array,因为我们会将ENTRYPOINT与CMD结合使用来用于某些应用程序参数。
要从我们的Dockerfile 创建映像,我们必须像以前一样运行 'docker build':
最后,我们能够从映像运行容器:
$> 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
2.1. 更改基础映像
我们可以轻松更改基本映像以使用不同的 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"]
此外,我们可以使用自定义基础映像。我们将在本教程的后面部分了解如何执行此操作。
3. 复合中的应用程序
Docker命令和Dockerfile特别适合创建单个容器。但是,如果我们想在隔离的应用程序网络上运行,容器管理很快就会变得混乱。
为了解决这个问题,Docker提供了一个名为Docker Compose的工具。此工具带有自己的YAML格式构建文件,更适合管理多个容器。例如,它能够在一个命令中启动或停止服务组合,或者将多个服务的日志记录输出合并到一个伪 tty 中。
3.1. 第二个 Spring Boot应用程序
让我们构建一个在不同 Docker 容器中运行的两个应用程序的示例。它们将相互通信,并作为“单个单元”呈现给主机系统。作为一个简单的例子,我们将创建第二个Spring Boot应用程序docker-product-server:
@RestController
public class DockerProductController {
@GetMapping("/products")
public String getMessage() {
return "A brand new product";
}
}
我们可以像我们的消息服务器一样构建和启动应用程序。
3.2. docker-compose文件
我们可以将两种服务的配置组合到一个名为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版本:指定应使用的格式版本。这是必填字段。这里我们使用较新的版本,而旧格式是“1”。
- services服务:此键中的每个对象定义一个服务,也称为容器。此部分是必填项。
- build构建:如果给定,docker-compose能够从Dockerfile 构建镜像
- context上下文:如果给定,则指定查找Dockerfile的构建目录。
- dockerfile :如果给定,它会为Dockerfile 设置一个备用名称。
- image 映像:告诉Docker在使用构建功能时应该为映像指定哪个名称。否则,它会在库或远程注册表中搜索此映像。
- 网络:这是要使用的命名网络的标识符。给定的名称值必须在网络部分中列出。
- build构建:如果给定,docker-compose能够从Dockerfile 构建镜像
- networks网络:在本节中,我们将指定可用于我们服务的网络。在这个例子中,我们让docker-compose为我们创建一个类型为'bridge'的命名网络。如果选项外部设置为true,它将使用具有给定名称的现有选项。
在继续之前,我们将检查构建文件中的语法错误:
$> docker-compose config
然后,我们可以构建映像,创建定义的容器,并在一个命令中启动它:
$> docker-compose up --build
这将一次性启动消息服务器和产品服务器。
要停止容器,请从Docker中删除它们,然后从中删除连接的网络。为此,我们可以使用相反的命令:
$> docker-compose down
有关docker-compose的更详细的介绍,我们可以阅读我们的文章Docker Compose简介。
3.3. 扩展服务
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
此命令将启动两个附加消息服务器和一个附加产品服务器。正在运行的容器不会停止。
4. 自定义基础映像
到目前为止,我们使用的基本映像(openjdk:8-jdk-alpine)包含已安装JDK 8的Alpine操作系统的发行版。或者,我们可以构建自己的基础映像(基于 Alpine 或任何其他操作系统)。
为此,我们可以使用以 Alpine 作为基础映像的Dockerfile,并安装我们选择的 JDK:
FROM alpine:edge
MAINTAINER baeldung.com
RUN apk add --no-cache openjdk8
- FROM:关键字FROM告诉Docker使用给定的映像及其标记作为构建基础。如果此映像不在本地库中,则会在DockerHub 或任何其他配置的远程注册表上执行联机搜索。
- MAINTAINER:维护者通常是一个电子邮件地址,用于标识图像的作者。
- RUN:使用RUN命令,我们在目标系统中执行 shell 命令行。在这里,我们利用Alpine Linux的包管理器apk来安装Java 8 OpenJDK。
要最终构建映像并将其存储在本地库中,我们必须运行:
docker build --tag=alpine-java:base --rm=true .
通知:–tag选项将为映像命名,–rm=true将在成功构建中间映像后将其删除。此 shell 命令中的最后一个字符是一个点,充当构建目录参数。
现在我们可以使用创建的映像而不是openjdk:8-jdk-alpine。
5. Spring Boot 2.3 中的构建包支持
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 外部访问。