将 Spring Boot 应用程序容器化(Dockerizing)

更新于 2025-12-30

baeldung 2024-01-08

1. 概述

在本教程中,我们将重点介绍如何将 Spring Boot 应用程序容器化,以便在隔离的环境中(即容器)运行。

我们将学习如何创建一组相互依赖、通过虚拟私有网络互联的容器组合,并了解如何使用单条命令统一管理这些容器。

首先,我们创建一个简单的 Spring Boot 应用程序 docker-message-server,之后将其部署到基于 Alpine Linux 的轻量级基础镜像中。


2. 容器化独立的 Spring Boot 应用程序

作为示例,我们将创建一个名为 docker-message-server 的简单 Spring Boot 应用程序,它暴露一个端点并返回一条静态消息:

@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 访问。

要将其容器化,首先创建一个名为 Dockerfile 的文件,内容如下:

FROM openjdk:17-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:将我们的 JAR 文件复制到镜像中。
  • ENTRYPOINT:容器启动时要执行的命令。必须以 JSON 数组格式定义,以便与 CMD 结合使用(用于传递应用参数)。

接下来,使用 docker build 命令从 Dockerfile 构建镜像:

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

最后,我们可以从该镜像运行容器:

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

这将在 Docker 中启动我们的应用程序,主机可通过 localhost:8887/messages 访问它。此处的关键是定义端口映射:将主机端口(8887)映射到容器内部端口(8888),后者是在 Spring Boot 应用配置中指定的。

注意:如果主机上的 8887 端口已被占用,映射将失败,此时需选择一个可用端口。

若以后台模式(detached mode)运行容器,可以使用以下命令查看其详情、停止或删除容器:

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

2.1 更改基础镜像

我们可以轻松更换基础镜像以使用不同的 Java 版本。例如,若想使用 Amazon 提供的 Corretto 发行版,只需修改 Dockerfile 如下:

FROM amazoncorretto:17-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 格式的配置文件,更适合管理多个容器。例如,它可以一键启停整个服务组合,或将多个服务的日志输出合并到同一个终端中。

3.1 第二个 Spring Boot 应用程序

我们来构建一个包含两个应用的示例,它们分别运行在不同的 Docker 容器中,并相互通信,对外表现为一个“整体”。为此,我们创建第二个 Spring Boot 应用 docker-product-server

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

我们可以像 message-server 一样构建并启动该应用。

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:指定使用的格式版本(必填)。这里使用较新的 2 版本,旧版为 1
  • services:每个对象定义一个服务(即容器),此部分为必填。
  • build:若提供,则 Docker Compose 可根据 Dockerfile 构建镜像。
    • context:指定构建目录(Dockerfile 所在位置)。
    • dockerfile:指定 Dockerfile 的替代名称。
  • image:指定构建后镜像的名称;若未使用构建功能,则从本地或远程仓库拉取该镜像。
  • networks:指定服务加入的网络名称,必须在 networks 节中定义。
  • networks(顶层):定义可用网络。本例中,Docker Compose 会自动创建一个名为 spring-cloud-network 的桥接网络。若设置 external: true,则使用已存在的同名网络。

在继续之前,先检查配置文件语法是否正确:

$> docker-compose config

然后,使用一条命令完成镜像构建、容器创建和启动:

$> docker-compose up --build

这将同时启动 message-serverproduct-server

要停止容器、删除容器及关联网络,可使用相反命令:

$> docker-compose down

3.3 服务扩缩容(Scaling)

Docker Compose 的一大优势是支持服务扩缩容。例如,我们可以让 Docker 运行 3 个 message-server 容器和 2 个 product-server 容器。

但要实现这一点,需先从 docker-compose.yml 中移除 container_name(以便 Docker 自动命名),并调整端口配置以避免冲突。

对于端口,可让 Docker 将主机的一个端口范围映射到容器内的固定端口:

ports:
    - 18800-18888:8888

之后即可进行扩缩容操作(注意:需使用修改后的 YAML 文件):

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

该命令将启动 1 个 message-server 和 1 个 product-server

若要扩展服务,可运行:

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

此命令将额外启动 2 个 message-server 和 1 个 product-server,且不会停止已运行的容器。


4. 自定义基础镜像

我们之前使用的基础镜像 openjdk:17-jdk-alpine 是一个预装了 JDK 17 的 Alpine Linux 系统。我们也可以自行构建基础镜像(基于 Alpine 或其他操作系统)。

为此,可创建一个 Dockerfile,以 Alpine 为基础并安装所需 JDK:

FROM alpine:edge
MAINTAINER baeldung.com
RUN apk add --no-cache openjdk17
  • FROM:指定基础镜像。若本地不存在,Docker 会从 DockerHub 或其他配置的远程仓库拉取。
  • MAINTAINER:通常为维护者邮箱。
  • RUN:在目标系统中执行 shell 命令。此处使用 Alpine 的包管理器 apk 安装 OpenJDK 17。

构建并保存镜像到本地仓库:

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

说明--tag 指定镜像名称,--rm=true 表示成功构建后删除中间层镜像。末尾的 . 表示构建上下文目录。

现在,我们就可以用自建的 alpine-java:base 镜像替代 openjdk:17-jdk-alpine


5. Spring Boot 2.3 对 Buildpacks 的支持

Spring Boot 2.3 新增了对 Buildpacks 的支持。简而言之,无需手动编写 Dockerfile 并执行 docker build,只需运行以下命令:

$ mvn spring-boot:build-image

Gradle 用户则使用:

$ ./gradlew bootBuildImage

前提:已安装并运行 Docker。

Buildpacks 的核心理念是提供类似 Heroku 或 Cloud Foundry 等云平台的部署体验:只需运行 build-image 目标,平台会自动完成构建与部署。

此外,它还能提升构建效率。例如,Buildpacks 会生成分层的 Docker 镜像,并使用 JAR 文件的解压形式(exploded jar),从而优化缓存和构建速度。

更重要的是,当需要调整构建逻辑时,无需逐个修改多个项目的 Dockerfile,只需更新或调优 Buildpacks 的镜像构建器即可。

运行上述命令后,可通过以下命令查看生成的镜像:

docker image ls -a

输出中将包含类似如下条目:

docker-message-server   1.0.0    b535b0cc0079

其中,镜像名称和版本号与 Maven/Gradle 配置文件中定义的一致,b535b0cc0079 是镜像 ID 的简写。

启动容器的方式与之前类似,只需映射端口:

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

6. 结论

本文介绍了如何:

  • 构建自定义 Docker 镜像
  • 将 Spring Boot 应用作为 Docker 容器运行
  • 使用 Docker Compose 管理多容器应用

通过这些方法,我们可以高效、可靠地将 Spring Boot 应用部署到容器化环境中。