接上篇「Spring」Boot Docker 认证指南(上)。
构建插件
如果你不想docker在你的构建中直接调用,有一套丰富的 Maven 和 Gradle 插件可以为你完成这项工作。这里仅仅是少数。
Spring Boot Maven 和 Gradle 插件
您可以使用Maven和Gradle的 Spring Boot 构建插件来创建容器映像。docker build这些插件使用Cloud Native Buildpacks创建一个 OCI 映像(与创建的格式相同) 。您不需要Dockerfile,但您确实需要 Docker 守护程序,可以在本地(使用 docker 构建时使用)或通过DOCKER_HOST环境变量远程进行。默认构建器针对 Spring Boot 应用程序进行了优化,并且图像像上面的示例一样有效地分层。
以下示例在不更改pom.xml文件的情况下使用 Maven:
./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=myorg/myapp复制
以下示例适用于 Gradle,无需更改build.gradle文件:
./gradlew bootBuildImage --imageName=myorg/myapp复制
第一次构建可能需要很长时间,因为它必须下载一些容器镜像和 JDK,但后续构建应该很快。
然后您可以运行映像,如以下清单所示(带输出):
docker run -p 8080:8080 -t myorg/myapp
Setting Active Processor Count to 6
Calculating JVM memory based on 14673596K available memory
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx14278122K -XX:MaxMetaspaceSize=88273K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 14673596K, Thread Count: 50, Loaded Class Count: 13171, Headroom: 0%)
Adding 129 container CA certificates to JVM truststore
Spring Cloud Bindings Enabled
Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -agentpath:/layers/paketo-buildpacks_bellsoft-liberica/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=6 -XX:MaxDirectMemorySize=10M -Xmx14278122K -XX:MaxMetaspaceSize=88273K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true
....
2015-03-31 13:25:48.035 INFO 1 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2015-03-31 13:25:48.037 INFO 1 --- [ main] hello.Application复制
您可以看到应用程序正常启动。您可能还注意到 JVM 内存需求是在容器内计算并设置为命令行选项的。这与多年来在 Cloud Foundry 构建包中使用的内存计算相同。它代表了对一系列 JVM 应用程序(包括但不限于 Spring Boot 应用程序)的最佳选择的重要研究,结果通常比 JVM 的默认设置好得多。您可以自定义命令行选项并通过设置环境变量覆盖内存计算器,如Paketo buildpacks 文档中所示。
Spotify Maven 插件
Spotify Maven 插件是一个受欢迎的选择。它要求您编写 aDockerfile然后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 配置将其设置为repository. 以下示例在不更改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 插件与 a 一起使用,Dockerfile并且还可Dockerfile以为您生成 a。然后它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 解压到build目录中的特定位置,该位置是 docker build 的根目录。Dockerfile然后早期显示的多层(不是多阶段)起作用。
Jib Maven 和 Gradle 插件
Google 有一个名为Jib的开源工具,它相对较新,但出于多种原因非常有趣。可能最有趣的是您不需要 docker 来运行它。Jib 使用与您获得的相同标准输出来构建映像,docker build但除非您要求它,否则它不会使用docker,因此它可以在未安装 docker 的环境中工作(在构建服务器中很常见)。您也不需要Dockerfile(无论如何都会被忽略)或任何东西pom.xml来获得在 Maven 中构建的图像(Gradle 将要求您至少在 中安装插件build.gradle)。
DockerfileJib 的另一个有趣的特性是它对层有意见,并且它以与上面创建的多层略有不同的方式优化它们。与胖 JAR 中一样,Jib 将本地应用程序资源与依赖项分开,但它更进一步,还将快照依赖项放入单独的层,因为它们更有可能发生变化。有用于进一步自定义布局的配置选项。
以下示例在不更改 Maven 的情况下使用pom.xml:
$ mvn com.google.cloud.tools:jib-maven-plugin:build -Dimage=myorg/myapp复制
myorg要运行该命令,您需要具有在存储库前缀下推送到 Dockerhub 的权限。如果您已docker在命令行上进行了身份验证,则可以在本地~/.docker配置中使用。~/.m2/settings.xml您还可以在您的(id存储库的重要)中设置 Maven“服务器”身份验证:
settings.xml:
<server>
<id>registry.hub.docker.com</id>
<username>myorg</username>
<password>...</password>
</server>复制
还有其他选项——例如,您可以docker使用dockerBuild目标而不是build. 还支持其他容器注册表。对于每一项,您都需要通过 Docker 或 Maven 设置来设置本地身份验证。
gradle 插件具有类似的功能,一旦你在你的build.gradle:
build.gradle:
plugins {
...
id 'com.google.cloud.tools.jib' version '1.8.0'
}复制
以下清单使用入门指南中使用的旧 Gradle 样式:
build.gradle:
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
mavenCentral()
}
dependencies {
classpath('org.springframework.boot:spring-boot-gradle-plugin:2.2.1.RELEASE')
classpath('com.google.cloud.tools.jib:com.google.cloud.tools.jib.gradle.plugin:1.8.0')
}
}复制
然后,您可以通过运行以下命令来构建映像:
./gradlew jib --image=myorg/myapp复制
与 Maven 构建一样,如果您已docker在命令行上进行了身份验证,则图像推送将从您的本地~/.docker配置进行身份验证。
持续集成
如今,自动化(或应该是)是每个应用程序生命周期的一部分。人们用来进行自动化的工具往往非常擅长从源代码调用构建系统。因此,如果这为您提供了一个 docker 映像,并且构建代理中的环境与开发人员自己的环境充分一致,那可能就足够了。对 docker 注册表进行身份验证可能是最大的挑战,但所有自动化工具中都有一些功能可以帮助解决这个问题。
但是,有时最好将容器创建完全留给自动化层,在这种情况下,可能不需要污染用户的代码。容器创建很棘手,开发人员有时不需要真正关心它。如果用户代码更干净,则不同的工具更有可能“做正确的事”(应用安全修复、优化缓存等)。自动化有多种选择,如今它们都带有一些与容器相关的功能。我们将看一对夫妇。
大厅
Concourse是一个基于管道的自动化平台,可用于 CI 和 CD。它在 VMware 内部使用,该项目的主要作者在那里工作。Concourse 中的所有内容都是无状态的,并且在容器中运行,CLI 除外。由于运行容器是自动化管道的主要业务顺序,因此很好地支持创建容器。Docker Image Resource负责保持构建的输出状态是最新的,如果它是一个容器镜像的话。
以下示例管道为前面显示的示例构建了一个 docker 映像,假设它位于 github 中myorg/myapp,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是另一个流行的自动化服务器。它具有大量功能,但最接近此处其他自动化示例的是管道功能。下面Jenkinsfile使用 Maven 构建一个 Spring Boot 项目,然后使用 aDockerfile构建一个镜像并将其推送到存储库:
Jenkinsfile:
node {
checkout scm
sh './mvnw -B -DskipTests clean package'
docker.build("myorg/myapp").push()
}复制
对于需要在构建服务器中进行身份验证的(实际)docker 存储库,您可以docker使用docker.withCredentials(…).
构建包
packSpring Boot Maven 和 Gradle 插件使用构建包的方式与CLI 在以下示例中的使用方式完全相同。给定相同的输入,生成的图像是相同的。
Cloud Foundry在内部使用容器已经很多年了,用于将用户代码转换为容器的部分技术是 Build Packs,这个想法最初是从Heroku借来的。当前一代的 buildpacks (v2) 生成由平台组装到容器中的通用二进制输出。新一代构建包(v3) 是 Heroku 与其他公司(包括 VMware)的合作,它直接明确地构建容器镜像。这对开发人员和运营商来说很有趣。开发人员不需要太关心如何构建容器的细节,但如果需要,他们可以轻松创建一个。Buildpacks 还具有许多用于缓存构建结果和依赖项的功能。通常,构建包的运行速度比原生 Docker 构建快得多。操作员可以扫描容器以审核其内容并将其转换为修补它们以进行安全更新。此外,您可以在本地(例如,在开发人员机器或 CI 服务中)或在 Cloud Foundry 等平台中运行构建包。
buildpack 生命周期的输出是容器映像,但您不需要Dockerfile. 输出映像中的文件系统层由 buildpack 控制。通常,许多优化都是在开发人员不必知道或关心它们的情况下进行的。在较低层(例如包含操作系统的基础映像)和较高层(包含中间件和语言特定依赖项)之间还有一个应用程序二进制接口。这使得 Cloud Foundry 等平台可以在有安全更新的情况下修补较低层,而不会影响应用程序的完整性和功能。
为了让您了解 buildpack 的功能,以下示例(显示其输出)从命令行使用Pack CLI(它可以与我们在本指南中使用的示例应用程序一起使用 - 不需要Dockerfile或任何特殊的构建配置):
pack build myorg/myapp --builder=paketobuildpacks/builder:base --path=.
base: Pulling from paketobuildpacks/builder
Digest: sha256:4fae5e2abab118ca9a37bf94ab42aa17fef7c306296b0364f5a0e176702ab5cb
Status: Image is up to date for paketobuildpacks/builder:base
base-cnb: Pulling from paketobuildpacks/run
Digest: sha256:a285e73bc3697bc58c228b22938bc81e9b11700e087fd9d44da5f42f14861812
Status: Image is up to date for paketobuildpacks/run:base-cnb
===> DETECTING
7 of 18 buildpacks participating
paketo-buildpacks/ca-certificates 2.3.2
paketo-buildpacks/bellsoft-liberica 8.2.0
paketo-buildpacks/maven 5.3.2
paketo-buildpacks/executable-jar 5.1.2
paketo-buildpacks/apache-tomcat 5.6.1
paketo-buildpacks/dist-zip 4.1.2
paketo-buildpacks/spring-boot 4.4.2
===> ANALYZING
Previous image with name "myorg/myapp" not found
===> RESTORING
===> BUILDING
Paketo CA Certificates Buildpack 2.3.2
https://github.com/paketo-buildpacks/ca-certificates
Launch Helper: Contributing to layer
Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper
Paketo BellSoft Liberica Buildpack 8.2.0
https://github.com/paketo-buildpacks/bellsoft-liberica
Build Configuration:
$BP_JVM_VERSION 11 the Java version
Launch Configuration:
$BPL_JVM_HEAD_ROOM 0 the headroom in memory calculation
$BPL_JVM_LOADED_CLASS_COUNT 35% of classes the number of loaded classes in memory calculation
$BPL_JVM_THREAD_COUNT 250 the number of threads in memory calculation
$JAVA_TOOL_OPTIONS the JVM launch flags
BellSoft Liberica JDK 11.0.12: Contributing to layer
Downloading from https://github.com/bell-sw/Liberica/releases/download/11.0.12+7/bellsoft-jdk11.0.12+7-linux-amd64.tar.gz
Verifying checksum
Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jdk
Adding 129 container CA certificates to JVM truststore
Writing env.build/JAVA_HOME.override
Writing env.build/JDK_HOME.override
BellSoft Liberica JRE 11.0.12: Contributing to layer
Downloading from https://github.com/bell-sw/Liberica/releases/download/11.0.12+7/bellsoft-jre11.0.12+7-linux-amd64.tar.gz
Verifying checksum
Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jre
Adding 129 container CA certificates to JVM truststore
Writing env.launch/BPI_APPLICATION_PATH.default
Writing env.launch/BPI_JVM_CACERTS.default
Writing env.launch/BPI_JVM_CLASS_COUNT.default
Writing env.launch/BPI_JVM_SECURITY_PROVIDERS.default
Writing env.launch/JAVA_HOME.default
Writing env.launch/MALLOC_ARENA_MAX.default
Launch Helper: Contributing to layer
Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/active-processor-count
Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/java-opts
Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/link-local-dns
Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/memory-calculator
Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/openssl-certificate-loader
Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-configurer
Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-classpath-9
JVMKill Agent 1.16.0: Contributing to layer
Downloading from https://github.com/cloudfoundry/jvmkill/releases/download/v1.16.0.RELEASE/jvmkill-1.16.0-RELEASE.so
Verifying checksum
Copying to /layers/paketo-buildpacks_bellsoft-liberica/jvmkill
Writing env.launch/JAVA_TOOL_OPTIONS.append
Writing env.launch/JAVA_TOOL_OPTIONS.delim
Java Security Properties: Contributing to layer
Writing env.launch/JAVA_SECURITY_PROPERTIES.default
Writing env.launch/JAVA_TOOL_OPTIONS.append
Writing env.launch/JAVA_TOOL_OPTIONS.delim
Paketo Maven Buildpack 5.3.2
https://github.com/paketo-buildpacks/maven
Build Configuration:
$BP_MAVEN_BUILD_ARGUMENTS -Dmaven.test.skip=true package the arguments to pass to Maven
$BP_MAVEN_BUILT_ARTIFACT target/*.[jw]ar the built application artifact explicitly. Supersedes $BP_MAVEN_BUILT_MODULE
$BP_MAVEN_BUILT_MODULE the module to find application artifact in
Creating cache directory /home/cnb/.m2
Compiled Application: Contributing to layer
Executing mvnw --batch-mode -Dmaven.test.skip=true package
[ ... Maven build output ... ]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 53.474 s
[INFO] Finished at: 2021-07-23T20:10:28Z
[INFO] ------------------------------------------------------------------------
Removing source code
Paketo Executable JAR Buildpack 5.1.2
https://github.com/paketo-buildpacks/executable-jar
Class Path: Contributing to layer
Writing env/CLASSPATH.delim
Writing env/CLASSPATH.prepend
Process types:
executable-jar: java org.springframework.boot.loader.JarLauncher (direct)
task: java org.springframework.boot.loader.JarLauncher (direct)
web: java org.springframework.boot.loader.JarLauncher (direct)
Paketo Spring Boot Buildpack 4.4.2
https://github.com/paketo-buildpacks/spring-boot
Creating slices from layers index
dependencies
spring-boot-loader
snapshot-dependencies
application
Launch Helper: Contributing to layer
Creating /layers/paketo-buildpacks_spring-boot/helper/exec.d/spring-cloud-bindings
Spring Cloud Bindings 1.7.1: Contributing to layer
Downloading from https://repo.spring.io/release/org/springframework/cloud/spring-cloud-bindings/1.7.1/spring-cloud-bindings-1.7.1.jar
Verifying checksum
Copying to /layers/paketo-buildpacks_spring-boot/spring-cloud-bindings
Web Application Type: Contributing to layer
Reactive web application detected
Writing env.launch/BPL_JVM_THREAD_COUNT.default
4 application slices
Image labels:
org.opencontainers.image.title
org.opencontainers.image.version
org.springframework.boot.version
===> EXPORTING
Adding layer 'paketo-buildpacks/ca-certificates:helper'
Adding layer 'paketo-buildpacks/bellsoft-liberica:helper'
Adding layer 'paketo-buildpacks/bellsoft-liberica:java-security-properties'
Adding layer 'paketo-buildpacks/bellsoft-liberica:jre'
Adding layer 'paketo-buildpacks/bellsoft-liberica:jvmkill'
Adding layer 'paketo-buildpacks/executable-jar:classpath'
Adding layer 'paketo-buildpacks/spring-boot:helper'
Adding layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings'
Adding layer 'paketo-buildpacks/spring-boot:web-application-type'
Adding 5/5 app layer(s)
Adding layer 'launcher'
Adding layer 'config'
Adding layer 'process-types'
Adding label 'io.buildpacks.lifecycle.metadata'
Adding label 'io.buildpacks.build.metadata'
Adding label 'io.buildpacks.project.metadata'
Adding label 'org.opencontainers.image.title'
Adding label 'org.opencontainers.image.version'
Adding label 'org.springframework.boot.version'
Setting default process type 'web'
Saving myorg/myapp...
*** Images (ed1f92885df0):
myorg/myapp
Adding cache layer 'paketo-buildpacks/bellsoft-liberica:jdk'
Adding cache layer 'paketo-buildpacks/maven:application'
Adding cache layer 'paketo-buildpacks/maven:cache'
Successfully built image 'myorg/myapp'复制
这--builder是一个运行 buildpack 生命周期的 Docker 镜像。通常,它将是所有开发人员或单个平台上的所有开发人员的共享资源。您可以在命令行上设置默认构建器(在 中创建一个文件~/.pack),然后从后续构建中省略该标志。
构建器
paketobuildpacks/builder:base还知道如何从可执行 JAR 文件构建映像,因此您可以先使用 Maven 构建,然后将其指向--pathJAR 文件以获得相同的结果。
原生
容器和平台领域的另一个新项目是Knative。如果您不熟悉它,可以将其视为构建无服务器平台的构建块。它建立在Kubernetes 之上,因此最终它会使用容器镜像并将它们转化为平台上的应用程序或“服务”。不过,它的主要功能之一是能够使用源代码并为您构建容器,使其对开发人员和操作员更加友好。Knative Build是执行此操作的组件,它本身就是一个灵活的平台,用于将用户代码转换为容器——您几乎可以以任何您喜欢的方式进行操作。一些模板提供了通用模式(例如 Maven 和 Gradle 构建)和多阶段 docker 构建使用卡尼科。还有一个模板使用了Buildpacks,这对我们来说很有趣,因为 buildpacks 一直对 Spring Boot 有很好的支持。
结束
本指南提供了许多用于为 Spring Boot 应用程序构建容器映像的选项。所有这些都是完全有效的选择,现在由您决定您需要哪一个。您的第一个问题应该是“我真的需要构建容器映像吗?” 如果答案是“是”,那么您的选择可能会受到效率、可缓存性和关注点分离的驱动。您是否想让开发人员无需过多了解容器镜像的创建方式?当需要修补操作系统和中间件漏洞时,您是否想让开发人员负责更新映像?或者,开发人员可能需要完全控制整个过程,并且他们拥有所需的所有工具和知识。