本指南提供了許多用于為 Spring Boot 應(yīng)用程序構(gòu)建容器映像的選項。所有這些都是完全有效的選擇,現(xiàn)在由您決定您需要哪一個。
(資料圖片)
接上篇?「Spring」Boot Docker 認證指南(上)。?
如果你不想docker在你的構(gòu)建中直接調(diào)用,有一套豐富的 Maven 和 Gradle 插件可以為你完成這項工作。這里僅僅是少數(shù)。
您可以使用Maven和Gradle的 Spring Boot 構(gòu)建插件來創(chuàng)建容器映像。docker build這些插件使用Cloud Native Buildpacks創(chuàng)建一個 OCI 映像(與創(chuàng)建的格式相同) 。您不需要Dockerfile,但您確實需要 Docker 守護程序,可以在本地(使用 docker 構(gòu)建時使用)或通過DOCKER_HOST環(huán)境變量遠程進行。默認構(gòu)建器針對 Spring Boot 應(yīng)用程序進行了優(yōu)化,并且圖像像上面的示例一樣有效地分層。
以下示例在不更改pom.xml文件的情況下使用 Maven:
./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=myorg/myapp復(fù)制
以下示例適用于 Gradle,無需更改build.gradle文件:
./gradlew bootBuildImage --imageName=myorg/myapp復(fù)制
第一次構(gòu)建可能需要很長時間,因為它必須下載一些容器鏡像和 JDK,但后續(xù)構(gòu)建應(yīng)該很快。
然后您可以運行映像,如以下清單所示(帶輸出):
docker run -p 8080:8080 -t myorg/myappSetting Active Processor Count to 6Calculating JVM memory based on 14673596K available memoryCalculated 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 truststoreSpring Cloud Bindings EnabledPicked 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復(fù)制
您可以看到應(yīng)用程序正常啟動。您可能還注意到 JVM 內(nèi)存需求是在容器內(nèi)計算并設(shè)置為命令行選項的。這與多年來在 Cloud Foundry 構(gòu)建包中使用的內(nèi)存計算相同。它代表了對一系列 JVM 應(yīng)用程序(包括但不限于 Spring Boot 應(yīng)用程序)的最佳選擇的重要研究,結(jié)果通常比 JVM 的默認設(shè)置好得多。您可以自定義命令行選項并通過設(shè)置環(huán)境變量覆蓋內(nèi)存計算器,如Paketo buildpacks 文檔中所示。
Spotify Maven 插件是一個受歡迎的選擇。它要求您編寫 aDockerfile然后docker為您運行,就像您在命令行上執(zhí)行它一樣。docker 鏡像標(biāo)簽和其他東西有一些配置選項,但它使您的應(yīng)用程序中的 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] ------------------------------------------------------------------------復(fù)制
這將構(gòu)建一個匿名 docker 映像。我們現(xiàn)在可以在命令行上標(biāo)記它docker或使用 Maven 配置將其設(shè)置為repository. 以下示例在不更改pom.xml文件的情況下工作:
$ mvn com.spotify:dockerfile-maven-plugin:build -Ddockerfile.repository=myorg/myapp復(fù)制
或者,您更改pom.xml文件:
pom.xml:
復(fù)制 com.spotify dockerfile-maven-plugin 1.4.8 myorg/${project.artifactId}
Palantir Gradle 插件與 a 一起使用,Dockerfile并且還可Dockerfile以為您生成 a。然后它docker就像在命令行上運行它一樣運行。
首先,您需要將插件導(dǎo)入您的build.gradle:
build.gradle:
buildscript { ... dependencies { ... classpath("gradle.plugin.com.palantir.gradle.docker:gradle-docker:0.13.0") }}復(fù)制
然后,最后,您可以應(yīng)用插件并調(diào)用它的任務(wù):
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"])}復(fù)制
在本例中,我們選擇將 Spring Boot fat JAR 解壓到build目錄中的特定位置,該位置是 docker build 的根目錄。Dockerfile然后早期顯示的多層(不是多階段)起作用。
Google 有一個名為Jib的開源工具,它相對較新,但出于多種原因非常有趣??赡茏钣腥さ氖悄恍枰?docker 來運行它。Jib 使用與您獲得的相同標(biāo)準(zhǔn)輸出來構(gòu)建映像,docker build但除非您要求它,否則它不會使用docker,因此它可以在未安裝 docker 的環(huán)境中工作(在構(gòu)建服務(wù)器中很常見)。您也不需要Dockerfile(無論如何都會被忽略)或任何東西pom.xml來獲得在 Maven 中構(gòu)建的圖像(Gradle 將要求您至少在 中安裝插件build.gradle)。
DockerfileJib 的另一個有趣的特性是它對層有意見,并且它以與上面創(chuàng)建的多層略有不同的方式優(yōu)化它們。與胖 JAR 中一樣,Jib 將本地應(yīng)用程序資源與依賴項分開,但它更進一步,還將快照依賴項放入單獨的層,因為它們更有可能發(fā)生變化。有用于進一步自定義布局的配置選項。
以下示例在不更改 Maven 的情況下使用pom.xml:
$ mvn com.google.cloud.tools:jib-maven-plugin:build -Dimage=myorg/myapp復(fù)制
myorg要運行該命令,您需要具有在存儲庫前綴下推送到 Dockerhub 的權(quán)限。如果您已docker在命令行上進行了身份驗證,則可以在本地~/.docker配置中使用。~/.m2/settings.xml您還可以在您的(id存儲庫的重要)中設(shè)置 Maven“服務(wù)器”身份驗證:
settings.xml:
復(fù)制 registry.hub.docker.com myorg ...
還有其他選項——例如,您可以docker使用dockerBuild目標(biāo)而不是build. 還支持其他容器注冊表。對于每一項,您都需要通過 Docker 或 Maven 設(shè)置來設(shè)置本地身份驗證。
gradle 插件具有類似的功能,一旦你在你的build.gradle:
build.gradle:
plugins { ... id "com.google.cloud.tools.jib" version "1.8.0"}復(fù)制
以下清單使用入門指南中使用的舊 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") }}復(fù)制
然后,您可以通過運行以下命令來構(gòu)建映像:
./gradlew jib --image=myorg/myapp復(fù)制
與 Maven 構(gòu)建一樣,如果您已docker在命令行上進行了身份驗證,則圖像推送將從您的本地~/.docker配置進行身份驗證。
如今,自動化(或應(yīng)該是)是每個應(yīng)用程序生命周期的一部分。人們用來進行自動化的工具往往非常擅長從源代碼調(diào)用構(gòu)建系統(tǒng)。因此,如果這為您提供了一個 docker 映像,并且構(gòu)建代理中的環(huán)境與開發(fā)人員自己的環(huán)境充分一致,那可能就足夠了。對 docker 注冊表進行身份驗證可能是最大的挑戰(zhàn),但所有自動化工具中都有一些功能可以幫助解決這個問題。
但是,有時最好將容器創(chuàng)建完全留給自動化層,在這種情況下,可能不需要污染用戶的代碼。容器創(chuàng)建很棘手,開發(fā)人員有時不需要真正關(guān)心它。如果用戶代碼更干凈,則不同的工具更有可能“做正確的事”(應(yīng)用安全修復(fù)、優(yōu)化緩存等)。自動化有多種選擇,如今它們都帶有一些與容器相關(guān)的功能。我們將看一對夫婦。
Concourse是一個基于管道的自動化平臺,可用于 CI 和 CD。它在 VMware 內(nèi)部使用,該項目的主要作者在那里工作。Concourse 中的所有內(nèi)容都是無狀態(tài)的,并且在容器中運行,CLI 除外。由于運行容器是自動化管道的主要業(yè)務(wù)順序,因此很好地支持創(chuàng)建容器。Docker Image Resource負責(zé)保持構(gòu)建的輸出狀態(tài)是最新的,如果它是一個容器鏡像的話。
以下示例管道為前面顯示的示例構(gòu)建了一個 docker 映像,假設(shè)它位于 github 中myorg/myapp,Dockerfile在根中有一個,并且在 中有一個構(gòu)建任務(wù)聲明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/myappjobs:- name: main plan: - task: build file: myapp/src/main/ci/build.yml - put: myapp-image params: build: myapp復(fù)制
管道的結(jié)構(gòu)是非常具有聲明性的:您定義“資源”(輸入、輸出或兩者)和“作業(yè)”(使用資源并將操作應(yīng)用于資源)。如果任何輸入資源發(fā)生更改,則會觸發(fā)新的構(gòu)建。如果任何輸出資源在作業(yè)期間發(fā)生更改,則會對其進行更新。
管道可以在與應(yīng)用程序源代碼不同的地方定義。此外,對于通用構(gòu)建設(shè)置,任務(wù)聲明也可以集中或外部化。這允許在開發(fā)和自動化之間分離一些關(guān)注點,這適合一些軟件開發(fā)組織。
Jenkins是另一個流行的自動化服務(wù)器。它具有大量功能,但最接近此處其他自動化示例的是管道功能。下面Jenkinsfile使用 Maven 構(gòu)建一個 Spring Boot 項目,然后使用 aDockerfile構(gòu)建一個鏡像并將其推送到存儲庫:
Jenkinsfile:
node { checkout scm sh "./mvnw -B -DskipTests clean package" docker.build("myorg/myapp").push()}復(fù)制
對于需要在構(gòu)建服務(wù)器中進行身份驗證的(實際)docker 存儲庫,您可以docker使用docker.withCredentials(…).
packSpring Boot Maven 和 Gradle 插件使用構(gòu)建包的方式與CLI 在以下示例中的使用方式完全相同。給定相同的輸入,生成的圖像是相同的。
Cloud Foundry在內(nèi)部使用容器已經(jīng)很多年了,用于將用戶代碼轉(zhuǎn)換為容器的部分技術(shù)是 Build Packs,這個想法最初是從Heroku借來的。當(dāng)前一代的 buildpacks (v2) 生成由平臺組裝到容器中的通用二進制輸出。新一代構(gòu)建包(v3) 是 Heroku 與其他公司(包括 VMware)的合作,它直接明確地構(gòu)建容器鏡像。這對開發(fā)人員和運營商來說很有趣。開發(fā)人員不需要太關(guān)心如何構(gòu)建容器的細節(jié),但如果需要,他們可以輕松創(chuàng)建一個。Buildpacks 還具有許多用于緩存構(gòu)建結(jié)果和依賴項的功能。通常,構(gòu)建包的運行速度比原生 Docker 構(gòu)建快得多。操作員可以掃描容器以審核其內(nèi)容并將其轉(zhuǎn)換為修補它們以進行安全更新。此外,您可以在本地(例如,在開發(fā)人員機器或 CI 服務(wù)中)或在 Cloud Foundry 等平臺中運行構(gòu)建包。
buildpack 生命周期的輸出是容器映像,但您不需要Dockerfile. 輸出映像中的文件系統(tǒng)層由 buildpack 控制。通常,許多優(yōu)化都是在開發(fā)人員不必知道或關(guān)心它們的情況下進行的。在較低層(例如包含操作系統(tǒng)的基礎(chǔ)映像)和較高層(包含中間件和語言特定依賴項)之間還有一個應(yīng)用程序二進制接口。這使得 Cloud Foundry 等平臺可以在有安全更新的情況下修補較低層,而不會影響應(yīng)用程序的完整性和功能。
為了讓您了解 buildpack 的功能,以下示例(顯示其輸出)從命令行使用Pack CLI(它可以與我們在本指南中使用的示例應(yīng)用程序一起使用 – 不需要Dockerfile或任何特殊的構(gòu)建配置):
pack build myorg/myapp --builder=paketobuildpacks/builder:base --path=.base: Pulling from paketobuildpacks/builderDigest: sha256:4fae5e2abab118ca9a37bf94ab42aa17fef7c306296b0364f5a0e176702ab5cbStatus: Image is up to date for paketobuildpacks/builder:basebase-cnb: Pulling from paketobuildpacks/runDigest: sha256:a285e73bc3697bc58c228b22938bc81e9b11700e087fd9d44da5f42f14861812Status: Image is up to date for paketobuildpacks/run:base-cnb===> DETECTING7 of 18 buildpacks participatingpaketo-buildpacks/ca-certificates 2.3.2paketo-buildpacks/bellsoft-liberica 8.2.0paketo-buildpacks/maven 5.3.2paketo-buildpacks/executable-jar 5.1.2paketo-buildpacks/apache-tomcat 5.6.1paketo-buildpacks/dist-zip 4.1.2paketo-buildpacks/spring-boot 4.4.2===> ANALYZINGPrevious image with name "myorg/myapp" not found===> RESTORING===> BUILDINGPaketo 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-helperPaketo 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.delimPaketo 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 codePaketo 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===> EXPORTINGAdding 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/myappAdding 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"復(fù)制
這–builder是一個運行 buildpack 生命周期的 Docker 鏡像。通常,它將是所有開發(fā)人員或單個平臺上的所有開發(fā)人員的共享資源。您可以在命令行上設(shè)置默認構(gòu)建器(在 中創(chuàng)建一個文件~/.pack),然后從后續(xù)構(gòu)建中省略該標(biāo)志。
paketobuildpacks/builder:base還知道如何從可執(zhí)行 JAR 文件構(gòu)建映像,因此您可以先使用 Maven 構(gòu)建,然后將其指向–pathJAR 文件以獲得相同的結(jié)果。
容器和平臺領(lǐng)域的另一個新項目是Knative。如果您不熟悉它,可以將其視為構(gòu)建無服務(wù)器平臺的構(gòu)建塊。它建立在Kubernetes 之上,因此最終它會使用容器鏡像并將它們轉(zhuǎn)化為平臺上的應(yīng)用程序或“服務(wù)”。不過,它的主要功能之一是能夠使用源代碼并為您構(gòu)建容器,使其對開發(fā)人員和操作員更加友好。Knative Build是執(zhí)行此操作的組件,它本身就是一個靈活的平臺,用于將用戶代碼轉(zhuǎn)換為容器——您幾乎可以以任何您喜歡的方式進行操作。一些模板提供了通用模式(例如 Maven 和 Gradle 構(gòu)建)和多階段 docker 構(gòu)建使用卡尼科。還有一個模板使用了Buildpacks,這對我們來說很有趣,因為 buildpacks 一直對 Spring Boot 有很好的支持。
本指南提供了許多用于為 Spring Boot 應(yīng)用程序構(gòu)建容器映像的選項。所有這些都是完全有效的選擇,現(xiàn)在由您決定您需要哪一個。您的第一個問題應(yīng)該是“我真的需要構(gòu)建容器映像嗎?” 如果答案是“是”,那么您的選擇可能會受到效率、可緩存性和關(guān)注點分離的驅(qū)動。您是否想讓開發(fā)人員無需過多了解容器鏡像的創(chuàng)建方式?當(dāng)需要修補操作系統(tǒng)和中間件漏洞時,您是否想讓開發(fā)人員負責(zé)更新映像?或者,開發(fā)人員可能需要完全控制整個過程,并且他們擁有所需的所有工具和知識。