Spring Boot Docker 部署 (QFY译版)

许多人使用容器来包装Spring Boot应用程序,构建容器不是一件简单的事情。这是Spring Boot应用程序开发人员的指南,对于开发人员来说,容器并不总是一个好的抽象——您需要了解并考虑非常低层次的问题——但是如果偶尔需要创建或使用容器,那么了解构建块是值得的。如果你需要创建自己的容器。在这里将向您展示一些选择。 我们假定您知道如何构建基本的Spring Boot应用程序。如果不了解的话,请参阅任一入门指南(Getting Started Guides),如关于构建REST服务的指南。从那里复制代码,并用下面的一些想法进行练习。

Note
另外有一个关于 Docker 的入门指南,也是一个很好的起点,但它没有涵盖我们在此的选择范围,它忽略了许多细节。

基本的 Dockerfile

Spring Boot应用程序很容易转换为可执行JAR文件。所有 Getting Started Guides 都可以,从https://start.spring.io/[Spring Initializr]下载的每个应用程序都有一个构建步骤来创建可执行JAR。 Maven 使用 ./mvnw install , Gradle 使用 ./gradlew build. 运行该JAR的基本Dockerfile在项目的顶层如下所示:

Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

JAR_FILE 可以作为“docker”命令的一部分传入(对于Maven和Gradle,它将是不同的)。对于Maven:

$ docker build --build-args=target/*.jar -t myorg/myapp .

对于 gradle:

$ docker build --build-args=build/libs/*.jar -t myorg/myapp .

当然,一旦选择了构建系统,就不需要ARG——只需要硬编码jar位置即可。对于maven:

Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

然后我们可以简单地创建一个映像

$ docker build -t myorg/myapp .

像这样运行:

$ docker run -p 8080:8080 myorg/myapp
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.2.RELEASE)
Nov 06, 2018 2:45:16 PM org.springframework.boot.StartupInfoLogger logStarting
INFO: Starting Application v0.1.0 on b8469cdc9b87 with PID 1 (/app.jar started by root in /)
Nov 06, 2018 2:45:16 PM org.springframework.boot.SpringApplication logStartupProfileInfo
...

如果想在图像内搜索,可以在其中像这样打开shell(基本映像中没有bash):

$ docker run -ti --entrypoint /bin/sh myorg/myapp
/ # ls
app.jar  dev      home     media    proc     run      srv      tmp      var
bin      etc      lib      mnt      root     sbin     sys      usr
/ #

目前为止,docker 配置非常简单,并且生成的映像不是非常有效。docker映像只有一个文件系统层,其中包含胖jar,我们对应用程序代码所做的每次更改都会更改该层,该层可能为10MB或更高(对于一些应用程序甚至多达50MB)。我们可以通过将JAR分成多个层来改进这一点。

更小的 Docker image

注意,上面示例中的基本图像是openjdk:8-jdk-alpine。alpine 映像比Dockerhub的标准openjdk 库映像小。目前Java11暂无 alpine 映像(AdoptOpenJDK 以前有一个现在看不到了 Dockerhub page). 使用“jre”替代“jdk”映像,还可以在基础映像中节省大约20MB。并非所有应用程序都使用JRE(与JDK相反),但是大多数都使用JRE,而且这是有些组织要求每个应用程序都必须遵守的制度,因为存在误用某些JDK特性(如编译)的风险。 另一个可以获得更小映像的技巧是使用JLink,它与OpenJDK 11捆绑在一起。JLink允许您从完整JDK中的模块子集构建自定义JRE发行版,因此在基础映像中不需要JRE或JDK。理论上,这会比使用openjdk官方 docker 映像更小。实践中,您还不能在JDK 11中使用 alpine 基础图像,因此您对基础映像的选择有限,可能导致最终映像更大。此外,您自己的基本映像中的自定义JRE不能与其他应用程序共享,因为它们需要不同的定制。因此,对于所有应用程序,您可能都拥有较小的图像,但是它们仍然需要更长的时间才能启动,因为它们无法从缓存JRE层中获益。 最后一点强调了映像构建者真正关心的问题:目标不一定总是构建尽可能小的图像。较小的映像通常是个好主意,因为它们上传和下载花费的时间更少,但前提是它们中的所有层都没有缓存。现在,映像注册非常复杂,有时过于巧妙地构造映像,容易失去这些特性的好处。如果您使用通用的基础层,那么映像的综合大小就不那么令人担心,并且随着注册中心和平台的发展,可能变得更小。尽管如此,尝试和优化应用程序映像中的层仍然是重要且有用的,但是目标应该总是将变化最快的内容放在最高层中,并且与其他应用程序共享尽可能多的大、低层。

完善的 Dockerfile

Spring Boot fat jar 自然具有“层”,因为 jar 本身就是打包。如果我们首先将其解压缩,那么它已经分为外部依赖和内部依赖了。为了在 docker构建的一步中做到这一点,我们需要首先解压缩jar。例如(以Maven为例,Gradle也类似):

$ mkdir target/dependency
$ (cd target/dependency; jar -xf ../*.jar)
$ docker build -t myorg/myapp .

with this Dockerfile

Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]

现在有3层,在后面的2层中包含所有应用程序资源。如果应用程序依赖关系没有改变,那么第一层(来自BOOT-INF/lib)将不会改变,因此构建将更快,并且只要基础层已经被缓存,容器在运行时启动也将更快。

Note
我们使用了硬编码的主应用程序类hello.Application。这对于您的应用程序可能是不同的。如果您愿意,可以使用另一个ARG对其进行参数化。您还可以将Spring Boot fat JarLauncher复制到映像中,并使用它来运行应用程序——它可以工作,并且不需要指定主类,但是在启动时它会慢一些。

调优

如果你想尽快启动你的应用程序(大多数人都想),你可以考虑一些调整。这里有一些想法:

  • 使用 spring-context-indexer (link to docs). 它不会为小应用程序增加太多,但是每一点都有帮助。

  • 如果负担太大禁用 actuators.

  • 使用 Spring Boot 2.1 及 Spring 5.1.

  • 指定 spring.config.location 路径 (通过命令行参数 或系统属性等) Spring Boot config file(s) .

  • 通过 spring.jmx.enabled=false 关闭JMX-您可能不需要在容器中使用它

  • 使用 -noverify 参数运行 JVM .也可以考虑 -XX:TieredStopAtLevel=1(这会减慢后续的JIT 以节省的启动时间)。

  • Java 8 可以使用容器内存提示: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap. 在 Java 11 是自动默认的。 您的应用程序在运行时可能不需要整个CPU,但是它需要多个CPU才能尽快启动(至少2、4更好)。 如果你不介意启动速度变慢,你可以将CPU控制在4以下。 如果必须以少于4个CPU启动,则设置 -Dspring.backgroundpreinitializer.ignore=true 可能有帮助,因为它阻止Spring Boot创建它可能不能使用的新线程(这对Spring Boot 2.1.0及以上版本有效)。

多阶段构建

上面的Docckerfile假设 fat JAR已经通过命令行构建了。您还可以 在 docker 中的使用多阶段构建执行该步骤,将结果(fat jar)从一个映像复制到另一个映像。以Maven为例:

Dockerfile
FROM openjdk:8-jdk-alpine as build
WORKDIR /workspace/app
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
COPY src src
RUN ./mvnw install -DskipTests
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]

第一个映像被标记为“build”,它用于运行Maven并构建fat jar ,然后将其解压缩。解压缩也可以由Maven或Gradle完成(这是入门指南中采用的方法)——实际上没有什么区别,只是构建配置需要编辑并添加一个插件。 请注意,源代码已经分为4层。后面的层包含构建配置和应用程序的源代码,前面的层包含构建系统本身(Maven包装器)。这是一个很小的优化,它还意味着我们不必将目标目录复制到docker映像中,即使是用于构建的临时目录。 因为Maven缓存必须在第一个RUN部分中重新创建,所以源代码每次修改后构建都会很慢。但是,您有一个完全独立的构建,任何人只要有docker都可以运行该构建来运行应用程序。这在某些环境中非常有用,例如,需要与不懂Java的人共享代码。

试验性功能

Docker 18.06附带了一些“实验”特性,包括缓存构建依赖项的方法。为了打开它们,您需要在守护进程(dockerd)中设置一个标志,并且在运行客户端时设置一个环境变量,然后您可以向Dockerfile中添加一行神奇的第一行:

Dockerfile
# syntax=docker/dockerfile:experimental

然后RUN指令接受一个新的标志——mount。完整例子如下:

Dockerfile
# syntax=docker/dockerfile:experimental
FROM openjdk:8-jdk-alpine as build
WORKDIR /workspace/app
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
COPY src src
RUN --mount=type=cache,target=/root/.m2 ./mvnw install -DskipTests
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]

然后运行:

$ DOCKER_BUILDKIT=1 docker build -t myorg/myapp .
...
 => /bin/sh -c ./mvnw install -DskipTests              5.7s
 => exporting to image                                 0.0s
 => => exporting layers                                0.0s
 => => writing image sha256:3defa...
 => => naming to docker.io/myorg/myapp

通过实验特性,您可能在控制台上看到不同的输出,但是您可以看到,一旦缓存变热,Maven构建现在只需要几秒钟而不是几分钟。

Note
这些特性处于实验阶段,打开和关闭构建包选项取决于您使用的docker版本。参阅安装的docker对应版本文档(上面的示例对于docker 18.0.6是正确的)。

构建插件

如果您不想在构建中直接调用docker,那么丰富的Maven和Gradle的插件集,可以为您完成这项工作。这里只有几个。

Spotify Maven 插件

Spotify Maven插件是一个流行的选择。它要求应用程序开发人员编写Dockerfile,然后为您运行docker,就像您在命令行上执行一样。docker映像标记和其他内容有一些配置选项,但是它使应用程序中的docker知识集中在Dockerfile中,许多人都喜欢这种方式。 对于最基本的使用,它可以不需要额外配置直接使用:

$ mvn com.spotify:dockerfile-maven-plugin:build
...
[INFO] Building Docker context /home/dsyer/dev/demo/workspace/myapp
[INFO]
[INFO] Image will be built without a name
[INFO]
...
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.630 s
[INFO] Finished at: 2018-11-06T16:03:16+00:00
[INFO] Final Memory: 26M/595M
[INFO] ------------------------------------------------------------------------

这构建了一个匿名的docker映像。我们现在可以在命令行上使用docker对其进行标记,或者使用Maven配置将其设置为存储库。示例(不更改pom.xml):

$ mvn com.spotify:dockerfile-maven-plugin:build -Ddockerfile.repository=myorg/myapp

或者在pom.xml中: .pom.xml

<build>
    <plugins>
        <plugin>
            <groupId>com.spotify</groupId>
            <artifactId>dockerfile-maven-plugin</artifactId>
            <version>1.4.8</version>
            <configuration>
                <repository>myorg/${project.artifactId}</repository>
            </configuration>
        </plugin>
    </plugins>
</build>

Palantir Gradle 插件

Palantir Gradle插件与Dockerfile一起工作,它还能为您生成Dockerfile,然后它运行docker,就像您在命令行上运行它一样。 首先需要将插件导入build.gradle: .build.gradle

buildscript {
    ...
    dependencies {
        ...
        classpath('gradle.plugin.com.palantir.gradle.docker:gradle-docker:0.13.0')
    }
}

最后应用该插件并调用其任务: .build.gradle

apply plugin: 'com.palantir.docker'
group = 'myorg'
bootJar {
    baseName = 'myapp'
    version =  '0.1.0'
}
task unpack(type: Copy) {
    dependsOn bootJar
    from(zipTree(tasks.bootJar.outputs.files.singleFile))
    into("build/dependency")
}
docker {
    name "${project.group}/${bootJar.baseName}"
    copySpec.from(tasks.unpack.outputs).into("dependency")
    buildArgs(['DEPENDENCY': "dependency"])
}

在这个示例中,我们选择将Spring Boot fat jar 解压缩到构建目录中的特定位置,该目录是docker构建的根目录。然后来自上面的多层(不是多阶段)Dockerfile将工作。

Jib Maven / Gradle 插件

Google有一个叫做Jib的开放源码工具,这个工具相对来说比较新,但是由于许多原因,它非常有趣。也许最有趣的是您不需要docker来运行它——它使用与从docker构建获得的标准输出相同的标准输出来构建映像,但是除非您要求使用docker,否则不使用docker——因此它在没有安装docker的环境(在构建服务器中并不罕见)下工作。您也不需要Dockerfile(无论如何它都会被忽略),或者pom.xml中的任何内容来获得在Maven中构建的映像(Gradle至少要求您在build.gradle中安装插件)。 Jib的另一个有趣的特性是它对层有自己的看法,并且它以与上面创建的多层Dockerfile稍微不同的方式优化它们。就像fat jar中一样,Jib将本地应用程序资源与依赖项分离,但是它更进一步,还将快照依赖项放到单独的层中,因为它们更可能改变。还有用于进一步定制布局的配置选项。 以Maven的为例(不更改pom.xml):

$ mvn com.google.cloud.tools:jib-maven-plugin:build -Dimage=myorg/myapp

要运行上面的命令,您需要有往myorg存储库前缀下推入Dockerhub的权限。如果已经在命令行上使用docker进行了身份验证,则会利用本地/.docker配置进行身份验证。还可以在/.m2/settings.xml(存储库ios的id)中设置Maven“服务器”身份验证: .settings.xml

    <server>
      <id>registry.hub.docker.com</id>
      <username>myorg</username>
      <password>...</password>
    </server>

还有其他选项,例如,您可以使用dockerBuild目标而不是构建来本地构建docker守护进程(比如在命令行上运行docker)。还支持其他容器注册中心,对于每个注册中心,您将需要通过docker或Maven设置设置设置本地身份验证。 gradle插件具有类似的特性,一旦您在build.gradle中使用了它。 .build.gradle

plugins {
  ...
  id 'com.google.cloud.tools.jib' version '0.9.11'
}

或者在入门指南中使用的旧样式中: .build.gradle

buildscript {
    repositories {
      maven {
        url "https://plugins.gradle.org/m2/"
      }
      mavenCentral()
    }
    dependencies {
        classpath('org.springframework.boot:spring-boot-gradle-plugin:2.0.5.RELEASE')
        classpath('com.google.cloud.tools.jib:com.google.cloud.tools.jib.gradle.plugin:0.9.11')
    }
}

然后您可以使用命令构建映像

$ ./gradlew jib --image=myorg/myapp

与Maven构建一样,如果您已经在命令行上使用docker进行了身份验证,则镜像推送将使用本地~/.docker配置进行身份验证。

持续集成

自动化是当今(或者应该是)每个应用程序生命周期的一部分。人们用来实现自动化的工具往往非常擅长从源代码调用构建系统。因此,如果这样可以得到docker映像,并且构建代理中的环境与开发人员自己的环境一致就行了。对docker注册中心的身份验证可能是最大的挑战,但是所有自动化工具都有帮助实现这一点的特性。 然而,有时最好将容器创建完全留给自动化层,在这种情况下,可能不需要污染用户的代码。容器创建很棘手,开发人员有时并不关心它。如果用户代码更干净,那么另一个工具更有可能“做正确的事情”,应用安全修复,优化缓存等等。自动化有多种选择,它们都带有一些与容器相关的特性。我们来看看几个。

Concourse

Concourse是一个基于流水线的自动化平台,可用于CI和CD。它在Pivotal内部被大量使用,项目工作的主要作者也在那里。Concourse 中的所有内容都是无状态的,除了CLI所有内容都在容器中运行。由于运行容器是自动化流水线的主要业务顺序,因此很好地支持创建容器。Docker Image Resource负责保持构建的输出状态最新(如果它是容器映像)。 下面是一个管道示例,它为上面的示例构建一个docker映像,假设它在myorg/myapp的github中,并且在根目录有一个Dockerfile,在src/main/ci/build.yml中有构建任务声明:

resources:

- name: myapp
  type: git
  source:
    uri: https://github.com/myorg/myapp.git

- name: myapp-image
  type: docker-image
  source:
    email: {{docker-hub-email}}
    username: {{docker-hub-username}}
    password: {{docker-hub-password}}
    repository: myorg/myapp
jobs:

- name: main
  plan:
  - task: build
    file: myapp/src/main/ci/build.yml
  - put: myapp-image
    params:
      build: myapp

管道的结构是非常声明性的:您定义“资源”(输入或输出或两者兼有)和“作业”(对资源使用和应用操作)。如果任何输入资源发生更改,则触发新的构建。如果在作业期间任何输出资源发生变化,则对其进行更新。 管道可以在与应用程序源代码不同的地方定义。对于一般的构建设置,任务声明也可以被集中或外部化。这允许在开发和自动化之间分离一些关注点,如果这就是您工作的方式。

Jenkins

Jenkins 是另一个流行的自动化服务器。它有很多特性,但是最接近这里的其他自动化示例的是管道特性。下面是一个Jenkinsfile,它使用Maven构建Spring Boot项目,然后使用Dockerfile构建映像并将其推送到存储库:

Jenkinsfile
node {
    checkout scm
    sh './mvnw -B -DskipTests clean package'
    docker.build("myorg/myapp").push()
}

对于需要在构建服务器中进行身份验证的(现实的)docker存储库,可以使用docker.withCredentials(…​…​)向上面的docker对象添加凭据。

Buildpacks

Cloud Foundry已经在内部使用容器很多年了,用于将用户代码转换为容器的技术之一是Build Packs,这个想法最初是从Heroku借来的。当前生成的构建包(v2)生成由平台组装到容器中的通用二进制输出。新一代的构建包(v3)是Heroku和其他公司(包括Pivotal)之间的协作,它直接明确地构建容器映像。这对于开发人员和操作员来说非常有趣。开发人员不太关心如何构建容器的细节,但是如果需要,他们可以很容易地创建容器。Buildpacks 还具有许多用于缓存构建结果和依赖项的特性,因此构建包通常比本机docker构建运行得更快。操作员可以扫描容器以审计它们的内容,并将它们转换为补丁,以便进行安全更新。您可以在本地运行 buildpacks(例如,在开发人员机器上,或者在CI服务中),或者在Cloud Foundry这样的平台上运行 buildpacks 。 buildpack 生命周期的输出是一个容器映像,但是您不需要docker或Dockerfile,因此它是CI和自动化友好的。输出映像中的文件系统层由 buildpack 控制,并且通常进行许多优化,而无需开发人员了解它们。在下级层(如包含操作系统的基础映像)和上级层(包含中间件和语言特定的依赖项)之间还有一个应用程序二进制接口。这使得平台(如Cloud Foundry)能够在没有影响应用程序的完整性和功能的情况下对较低层进行补丁(patch)。 为了让您了解 buildpack 的特性,这里给出了一个使用命令行中的Pack CLI的示例(它将与我们在本指南中使用的示例应用程序一起工作,不需要Dockerfile或任何特殊的构建配置):

$ pack build myorg/myapp --builder=nebhale/java-build --path=.
2018/11/07 09:54:48 Pulling builder image 'nebhale/java-build' (use --no-pull flag to skip this step)
2018/11/07 09:54:49 Selected run image 'packs/run' from stack 'io.buildpacks.stacks.bionic'
2018/11/07 09:54:49 Pulling run image 'packs/run' (use --no-pull flag to skip this step)

*** DETECTING:
2018/11/07 09:54:52 Group: Cloud Foundry OpenJDK Buildpack: pass | Cloud Foundry Build System Buildpack: pass | Cloud Foundry JVM Application Buildpack: pass

*** ANALYZING: Reading information from previous image for possible re-use

*** BUILDING:

-----> Cloud Foundry OpenJDK Buildpack 1.0.0-BUILD-SNAPSHOT

-----> OpenJDK JDK 1.8.192: Reusing cached dependency

-----> OpenJDK JRE 1.8.192: Reusing cached launch layer

-----> Cloud Foundry Build System Buildpack 1.0.0-BUILD-SNAPSHOT

-----> Using Maven wrapper
       Linking Maven Cache to /home/pack/.m2

-----> Building application
       Running /workspace/app/mvnw -Dmaven.test.skip=true package
...

---> Running in e6c4a94240c2

---> 4f3a96a4f38c

---> 4f3a96a4f38c
Successfully built 4f3a96a4f38c
Successfully tagged myorg/myapp:latest
$ docker run -p 8080:8080 myorg/myapp
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.5.RELEASE)
2018-11-07 09:41:06.390  INFO 1 --- [main] hello.Application: Starting Application on 1989fb9a00a4 with PID 1 (/workspace/app/BOOT-INF/classes started by pack in /workspace/app)
...

--builder 是一个运行 buildpack 生命周期的Docker映像,通常是所有开发人员或单个平台上的所有开发人员的共享资源。 Ben Hale 为Cloud Foundry维护了较旧的构建包,目前正在开发新一代。本例中的输出转到本地docker守护进程,但是在自动化平台中,它可以是docker注册中心。一旦 pack CLI 达到稳定发行版,默认构建器就可能执行相同的操作。

Knative

容器和平台空间中的另一个新项目是https://cloud.google.com/knative/[Knative]。 Knative 有很多东西,但是如果您不熟悉它,那么可以将它视为构建无服务器平台的构建块。 它是基于https://kubernetes.io/[Kubernetes]构建的,因此最终它会使用容器映像,并将它们转换为平台上的应用程序或“服务”。不过,它的主要特性之一是能够使用源代码为您构建容器,从而使得它更适合开发人员和操作员。Knative Build是这样做的组件,并且它本身是一个将用户代码转换为容器的灵活平台——您可以以几乎任何您喜欢的方式进行。一些模板提供了常见的模式,如Maven和Gradle构建,以及使用 Kaniko 的多阶段docker构建。还有一个模板使用Buildpacks,这对我们很有趣,因为 buildpacks 一直对Spring Boot有很好的支持。Riff和Pivotal Function Service的主张选择 Knative上的 Buildpacks 将用户功能转换为运行中的无服务器应用程序。

结束语

本指南介绍了许多为Spring Boot应用程序构建容器映像的选项。所有这些都是完全有效的选择,现在由您决定需要哪一个。第一个问题应该是“我真的需要构建容器映像吗?”如果答案是“是”,那么需要综合考虑效率和可缓存性以及关注点。您是否希望使开发人员不必过多了解容器映像的创建方法?当需要修补操作系统和中间件漏洞时,您想让开发人员负责更新映像吗?或者开发人员需要完全控制整个过程,并且他们拥有所需的所有工具和知识。

译注

英文原文: Spring Boot Docker

数码
沪ICP备19006215号-4