# 19.4 在 Docker 容器中运行 SpringBoot

在云端部署各种应用程序，Docker 已经成为了事实上的标准 [https://www.docker.com](https://www.docker.com/)。许多不同的云端环境，包括 AWS、Microsoft Azure、Google Cloud Platform 和 Pivotal Web Service（举几个例子），都接受用于部署应用程序的 Docker 容器。

容器化应用程序（如使用 Docker 创建的应用程序）的思想吸引了很多人，这是自真实世界集装箱的类比。集装箱都有一个标准尺寸和格式，无论在其中放什么货物。正因为如此，集装箱很容易堆放在船上，或用火车、卡车来运输。以类似的方式，容器化应用程序共享一个公共的容器格式，这种格式可以在任何位置部署和运行，而不用考虑其内部的应用程序如何。

虽然创建 Docker 镜像并不困难，但 Spotify 已经创建了一个 Maven 插件，可以根据 Spring Boot 的构建产物创建 Docker 镜像，这就像吹口哨一样容易。要使用 Docker 插件，需要在 Spring Boot 项目的 pom.xml 文件中，将如下内容添加到 `<build>/<plugins>` 部分：

```markup
<build>
  <plugins>
...
    <plugin>
      <groupId>com.spotify</groupId>
      <artifactId>dockerfile-maven-plugin</artifactId>
      <version>1.4.3</version>
      <configuration>
        <repository>
          ${docker.image.prefix}/${project.artifactId}
        </repository>
        <buildArgs>
          <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
        </buildArgs>
        </configuration>
    </plugin>
  </plugins>
</build>
```

在 `<configuration>` 部分，您需要设置一些属性，以创建 Docker 镜像。`<repository>` 元素描述 Docker 镜像的名称，这将显示在 Docker 存储库中。如本文所述，名称基于 Maven 项目 artifact ID，前缀为从 Maven 属性解析的值，名称为 docker.image.prefix。artifact ID 是 Maven 已经知道的东西，所以您只需要指定 prefix 属性：

```markup
<properties>
...
  <docker.image.prefix>tacocloud</docker.image.prefix>
</properties>
```

如果这是 Tacl Cloud 中的配料服务，则生成的 Docker 镜像像将作为 tacocloud/ingredient-service 服务保存在 Docker 存储库中。

在 `<buildArgs>` 元素下，可以指明镜像中应包含的 Maven 构建产物。如图所示，它使用 Maven 属性 project.build.finalName 以确定 target 目录中 JAR 文件的名称。

除了您在 Maven 构建中提供的信息之外，所有 Docker 镜像都是在名为 Dockerfile 的文件中定义的。此文件标识镜像要基于的基础镜像，以及应设置的环境变量、应该挂载的卷，并且（最重要的）入口点要执行的命令（容器启动时执行）。对于大多数 Spring Boot 应用程序，以下 Dockerfile 是一个很好的示例：

```
FROM openjdk:8-jdk-alpine
ENV SPRING_PROFILES_ACTIVE docker
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java",\
        "-Djava.security.egd=file:/dev/./urandom",\
        "-jar",\
        "/app.jar"]
```

将此 Docker 文件逐行分解，可以看到以下内容：

* FROM 指令标识新镜像所基于的基础镜像。新的镜像扩展了基础镜像。在本例中，基础映像是 openjdk:8-jdk-alpine，这是基于 OpenJDK 版本8 的容器镜像。
* ENV 指令设置了一个环境变量。指定激活状态的属性文件，可以覆盖些 Spring Boot 应用程序属性。因此，在本例中，您将环境变量 SPRING\_PROFILES\_ACTIVE 设置为 docker，以确保 Spring Boot 应用程序以 docker 作为激活状态的属性文件。
* VOLUME 指令在容器中创建挂载点。在本例中，它在 /tmp 处创建挂载点，以便容器可以在必要时写入数据到 /tmp 目录。
* ARG 指令声明一个可在构建时传入的参数。在本例中，它声明了一个名为 JAR\_FILE 的参数，该参数与 Maven 插件的 `<buildArgs>` 部分中给出的参数相同。
* COPY 指令将文件从给定路径复制到另一路径。在本例中，它将 Maven 插件中指定的 JAR 文件，复制为镜像里名为 app.jar 的文件。
* ENTRYPOINT 指令描述了当容器启动时应该做什么。作为数组给定要执行的命令。在本例中，使用 java 命令行运行可执行文件 app.jar。

请特别注意 ENV 指令。一般来说，在 Spring Boot 应用程序镜像中，都需要设置 SPRING\_PROFILES\_ACTIVE 环境变量。这使得在 Docker 中运行的应用程序，可以设置特有的 bean 和配置属性。

对于配料服务，您需要以某种方式指明，应用程序运行所需要的 Mongo 数据库存在于另一个容器中。默认情况下，Spring Data 会尝试连接本地主机，上侦听 27017 端口的 Mongo 数据库。这种情况只有在本地运行所有服务时才会发生。所以您需要配置 spring.data.mongodb.host 属性，来告诉 Spring Data 到哪里寻找 Mongo。

虽然您可能还不知道 Mongo 数据库将在哪里运行，但是可以将 Spring Data 配置为：当名称为 docker 的属性文件处于激活状态时，连接主机名为 Mongo 的容器中的 Mongo 数据库。这通过将以下特定于 docker 的属性添加到 application.yml 文件中：

```yaml
---
spring:
  profiles: docker

  data:
    mongodb:
      host: mongo
```

稍后，当您启动 Docker 容器时，您将把 mongo 主机映射到运行在不同容器中的 Mongo 数据库。但现在您已经准备好构建 Docker 镜像了。使用 Maven 包装器，执行 package 和 dockerfile:build，来构建 JAR 文件，然后生成 Docker 镜像：

```bash
$ mvnw package dockerfile:build
```

此时，您可以使用 docker images 命令来确认本地镜像库中的镜像。（列 CREATED 和 SIZE 被省略，以便于阅读和排版）：

```bash
$ docker images
REPOSITORY                    TAG         IMAGEID
tacocloud/ingredient-service  latest      7e8ed20e768e
```

在启动容器之前，需要为 Mongo 数据库创建一个容器。下面的命令运行一个名为 tacocloudmongo 的新 Docker 容器，数据库使用 Mongo 3.7.9 版本：

```bash
$ docker run --name tacocloud-mongo -d mongo:3.7.9-xenial
```

现在，您终于可以运行配料服务容器，并将其链接到刚刚启动的 Mongo 容器了：

```bash
$ docker run -p 8080:8081 \
          --link tacocloud-mongo:mongo \
          tacocloud/ingredient-service
```

此处显示的 docker run 命令有几个重要组成部分需要注意：

* 因为您在容器中配置了 Spring Boot 应用程序运行在端口 8081 上，-p 参数将内部端口映射到主机的端口 8080。
* \--link 参数将容器链接名为 tacocloudmongo 的容器，并为其分配一个 mongo 主机名，以便 Spring Data 可以通过那个主机名连接数据库。
* 最后，指定在新容器中运行的镜像名称（在本例中为 tacocloud/IngreditService）。

现在您已经构建了 Docker 映像，并且已经可以在本地容器中运行，您可以将镜像推送到 Dockerhub 或其他镜像存储库。如果您在 dockerhub 上有帐户并已登录，您可以像这样使用 Maven 推送镜像：

```bash
$ mvnw dockerfile:push
```

这样，您就可以将镜像部署到，几乎任何支持 Docker 容器的云平台，包括 AWS、Microsoft Azure 和 Google Cloud Platform。您可以选择一个，并按照特定于平台的使用指南部署 Docker 容器。以下是一些流行云的平台使用指南链接：

* AWS —— <https://aws.amazon.com/getting-started/tutorials/deploy-dockercontainers/>
* Microsoft Azure —— <https://docs.docker.com/docker-for-azure/deploy/>
* Google Cloud Platform —— <https://cloud.google.com/kubernetes-engine/docs/tutorials/hello-app>
* Pivotal Web Services (PWS) —— <https://docs.run.pivotal.io/devguide/deploy-apps/push-docker.html>
* Pivotal Container Service (PKS) —— <https://pivotal.io/platform/pivotal-container-service>
