Konstruado de Optimumigitaj Docker-bildoj por Spring Boot-Apliko

Ujoj fariĝis la preferata rimedo por enpaki aplikaĵon kun ĉiuj ĝiaj softvaraj kaj operaciumaj dependecoj kaj poste liveri ilin al malsamaj medioj.

Ĉi tiu artikolo kovras malsamajn manierojn enhavigi Spring Boot-aplikaĵon:

  • konstruante docker-bildon uzante dockerfile,
  • konstrui OCI-bildon el fonto uzante Cloud-Native Buildpack,
  • kaj bildoptimumigo ĉe rultempo disigante JAR-partojn en malsamajn nivelojn uzante tavoligitajn ilojn.

 Ekzemplo de kodo

Ĉi tiu artikolo estas akompanata de funkcia kodekzemplo sur GitHub .

Uja terminologio

Ni komencos per la uja terminologio uzata tra la artikolo:

  • Bildo de ujo: dosiero de specifa formato. Ni konvertas nian aplikaĵon en ujan bildon rulante la konstruilon.
  • Ujo: plenumebla okazo de la ujo-bildo.
  • Kontenera motoro: La demona procezo respondeca pri rulado de la ujo.
  • Uja gastiganto: La gastiga maŝino sur kiu funkcias la ujo-motoro.
  • Registro de ujoj: La ĝenerala loko uzata por publikigi kaj distribui la ujbildon.
  • OCI-normoOpen Container Initiative (OCI) estas malpeza, malfermfonta administradkadro formita de la Linukso-Fondaĵo. La OCI Bilda Specifo difinas industriajn normojn por ujbildaj formatoj kaj la rultempo por certigi, ke ĉiuj ujmotoroj povas ruli ujajn bildojn kreitajn per iu ajn konstruilo.

Por kontenigi aplikaĵon, ni envolvas nian aplikaĵon en kontenerbildon kaj publikigas tiun bildon al la publika registro. La ujo rultempo prenas ĉi tiun bildon el la registro, malpakigas ĝin kaj rulas la aplikaĵon ene de ĝi.

Versio 2.3 de Spring Boot disponigas kromaĵojn por konstrui OCI-bildojn.

Docker estas la plej ofte uzata uja efektivigo, kaj ni uzas Docker en niaj ekzemploj, do ĉiuj postaj ujreferencoj en ĉi tiu artikolo referencos al Docker.

Konstrui ujan bildon laŭ la tradicia maniero

Konstrui Docker-bildojn por Spring Boot-aplikoj estas tre facila aldonante kelkajn instrukciojn al via Dockerfile.

Ni unue kreas plenumeblan JAR kaj, kiel parto de la instrukcioj de Dockerfile, kopias la plenumeblan JAR supre de la baza JRE-bildo post apliki la necesajn personigojn.

Ni kreu nian Spring-aplikaĵon Printempa Inicializr kun dependecoj weblombokи actuator. Ni ankaŭ aldonas ripozregilon por provizi API GETmetodo.

Kreante Dockerfile

Ni tiam metas ĉi tiun aplikon en ujo per aldonado Dockerfile:

FROM adoptopenjdk:11-jre-hotspot
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/application.jar"]

Nia Dockerfile enhavas bazan bildon, de adoptopenjdk, sur kiu ni kopias nian JAR-dosieron kaj poste malfermas la havenon, 8080kiu aŭskultos petojn.

Aplika asembleo

Unue vi devas krei aplikaĵon per Maven aŭ Gradle. Jen ni uzas Maven:

mvn clean package

Ĉi tio kreas plenumeblan JAR-dosieron por la aplikaĵo. Ni devas konverti ĉi tiun ruleblan JAR al Docker-bildo por funkcii per la Docker-motoro.

Kreu ujan bildon

Ni tiam metas ĉi tiun JAR-plenigeblan en la bildon de Docker rulante la komandon docker buildde la radika dosierujo de la projekto enhavanta la Dockerfile kreitan pli frue:

docker build  -t usersignup:v1 .

Ni povas vidi nian bildon en la listo kun la komando:

docker images 

La eligo de la supra komando inkluzivas nian bildon usersignupkune kun la baza bildo, adoptopenjdkspecifita en nia Dockerfile.

REPOSITORY          TAG                 SIZE
usersignup          v1                  249MB
adoptopenjdk        11-jre-hotspot      229MB

Rigardu tavolojn ene de uja bildo

Ni rigardu la stakon de tavoloj ene de la bildo. Ni uzos ilo  plonĝi, por vidi ĉi tiujn tavolojn:

dive usersignup:v1

Jen parto de la eligo de la Dive-komando: 

Konstruado de Optimumigitaj Docker-bildoj por Spring Boot-Apliko

Kiel ni povas vidi, la aplika tavolo konsistigas signifan parton de la bilda grandeco. Ni volas redukti la grandecon de ĉi tiu tavolo en la sekvaj sekcioj kiel parto de nia optimumigo.

Konstrui ujan bildon per Buildpack

Asembleaj pakoj (Konstrupakoj) estas ĝenerala termino uzata de diversaj proponoj de Platform as a Service (PAAS) por krei ujobildon el fontkodo. Ĝi estis lanĉita de Heroku en 2011 kaj poste estis adoptita de Cloud Foundry, Google App Engine, Gitlab, Knative kaj kelkaj aliaj.

Konstruado de Optimumigitaj Docker-bildoj por Spring Boot-Apliko

Avantaĝo de Cloud Build Packages

Unu el la ĉefaj avantaĝoj uzi Buildpack por konstrui bildojn estas tio bildaj agordaj ŝanĝoj povas esti administritaj centre (konstruanto) kaj disvastigitaj al ĉiuj aplikoj uzante la konstruilon.

La konstrupakaĵoj estis proksime ligitaj al la platformo. Cloud-Native Buildpacks disponigas normigon tra platformoj subtenante la OCI-bildformaton, kiu certigas, ke la bildo povas esti prizorgita de la Docker-motoro.

Uzante la Spring Boot Kromprogramon

La kromaĵo Spring Boot konstruas OCI-bildojn el fonto uzante Buildpack. Bildoj estas kreitaj uzante bootBuildImagetaskoj (Gradle) aŭ spring-boot:build-imagecelo (Maven) kaj loka Docker-instalado.

Ni povas personecigi la nomon de la bildo, kiun ni devas puŝi al la Docker-registro specifante la nomon enen image tag:

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <image>
      <name>docker.io/pratikdas/${project.artifactId}:v1</name>
    </image>
  </configuration>
</plugin>

Ni uzu Maven por ekzekuti build-imageceloj por krei aplikaĵon kaj krei ujan bildon. Ni nuntempe ne uzas iujn ajn Dockerfiles.

mvn spring-boot:build-image

La rezulto estos io kiel ĉi tio:

[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:build-image (default-cli) @ usersignup ---
[INFO] Building image 'docker.io/pratikdas/usersignup:v1'
[INFO] 
[INFO]  > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%
.
.
.. [creator]     Adding label 'org.springframework.boot.version'
.. [creator]     *** Images (c311fe74ec73):
.. [creator]           docker.io/pratikdas/usersignup:v1
[INFO] 
[INFO] Successfully built image 'docker.io/pratikdas/usersignup:v1'

De la eligo, ni vidas tion paketo Cloud-Native buildpackuzata por krei funkciantan OCI-bildon. Kiel antaŭe, ni povas vidi la bildon listigitan kiel bildon de Docker rulante la komandon:

docker images 

Konkludo:

REPOSITORY                             SIZE
paketobuildpacks/run                  84.3MB
gcr.io/paketo-buildpacks/builder      652MB
pratikdas/usersignup                  257MB

Krei Ujan Bildon per Jib

Jib estas bildverka kromaĵo de Guglo kiu disponigas alternativan metodon por krei ujan bildon el fonto.

Agordi jib-maven-pluginen pom.xml:

      <plugin>
        <groupId>com.google.cloud.tools</groupId>
        <artifactId>jib-maven-plugin</artifactId>
        <version>2.5.2</version>
      </plugin>

Poste ni rulas la aldonaĵon Jib uzante la komandon Maven por konstrui la aplikaĵon kaj krei la ujan bildon. Kiel antaŭe, ni ne uzas iujn ajn Dockerfile ĉi tie:

mvn compile jib:build -Dimage=<docker registry name>/usersignup:v1

Post ekzekuti la supran komandon de Maven, ni ricevas la sekvan eligon:

[INFO] Containerizing application to pratikdas/usersignup:v1...
.
.
[INFO] Container entrypoint set to [java, -cp, /app/resources:/app/classes:/app/libs/*, io.pratik.users.UsersignupApplication]
[INFO] 
[INFO] Built and pushed image as pratikdas/usersignup:v1
[INFO] Executing tasks:
[INFO] [==============================] 100.0% complete

La eligo montras, ke la ujo-bildo estis kreita kaj metita en la registron.

Instigoj kaj metodoj por krei optimumigitajn bildojn

Ni havas du ĉefajn kialojn por optimumigo:

  • Produkteco: En ujo-instrumentadsistemo, ujo-bildo estas tirita de la bildregistro al la gastiganto funkcianta la ujo-motoron. Ĉi tiu procezo nomiĝas planado. Eltiri grandajn bildojn el la registro rezultigas longajn planajn tempojn en kontenaj orkestradsistemoj kaj longajn konstrutempojn en CI-duktoj.
  • Sekureco: grandaj bildoj ankaŭ havas grandan areon por vundeblecoj.

Docker-bildo konsistas el stako de tavoloj, ĉiu reprezentante deklaron en nia Dockerfile. Ĉiu tavolo reprezentas la delton de ŝanĝoj en la subesta tavolo. Kiam ni tiras Docker-bildon el la registro, ĝi estas tirita en tavoloj kaj kaŝita sur la gastiganto.

Spring Boot uzas "grasa VASO" en kiel la defaŭlta pakformato. Kiam ni rigardas grasan BOĈON, ni vidas, ke la aplikaĵo estas tre malgranda parto de la tuta JAR. Ĉi tiu estas la parto, kiu plej ŝanĝiĝas. La resto konsistas el Spring Framework dependecoj.

La optimumiga formulo estas centrita ĉirkaŭ izolado de la aplikaĵo je aparta nivelo de la dependecoj de Spring Framework.

La dependeca tavolo, kiu formas la plej grandan parton de la dika JAR-dosiero, estas elŝutita nur unufoje kaj konservita en la gastiga sistemo.

Nur maldika tavolo de la aplikaĵo estas tirita dum la ĝisdatigoj de la aplikaĵo kaj planado de ujo, kiel montrite en ĉi tiu diagramo:

Konstruado de Optimumigitaj Docker-bildoj por Spring Boot-Apliko

En la sekvaj sekcioj, ni rigardos kiel krei ĉi tiujn optimumigitajn bildojn por aplikaĵo Spring Boot.

Konstruado de Optimumigita Uja Bildo por Printempa Bota Apliko kun Buildpack

Spring Boot 2.3 subtenas tavoligon ĉerpante partojn de dika JAR-dosiero en apartajn tavolojn. La tavoliga funkcio estas malebligita defaŭlte kaj devas esti eksplicite ebligita per la kromaĵo Spring Boot Maven:

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <layers>
      <enabled>true</enabled>
    </layers>
  </configuration> 
</plugin>

Ni uzos ĉi tiun agordon por konstrui nian ujan bildon unue kun Buildpack kaj poste kun Docker en la sekvaj sekcioj.

Ni kuru build-imageMaven celo krei ujan bildon:

mvn spring-boot:build-image

Se ni rulas Dive por vidi la tavolojn en la rezulta bildo, ni povas vidi, ke la aplika tavolo (ruĝe rondigita) estas multe pli malgranda en la kilobajta gamo kompare kun tio, kion ni akiris uzante la dikan JAR-formaton:

Konstruado de Optimumigitaj Docker-bildoj por Spring Boot-Apliko

Konstruante Optimumigitan Ujan Bildon por Printempa Bota Apliko kun Docker

Anstataŭ uzi aldonaĵon Maven aŭ Gradle, ni ankaŭ povas krei tavoligitan Docker JAR-bildon kun Docker-dosiero.

Kiam ni uzas Docker, ni devas fari du kromajn paŝojn por ĉerpi la tavolojn kaj kopii ilin en la finan bildon.

La enhavo de la rezulta JAR post konstruado kun Maven kun tavoligado ebligita aspektos jene:

META-INF/
.
BOOT-INF/lib/
.
BOOT-INF/lib/spring-boot-jarmode-layertools-2.3.3.RELEASE.jar
BOOT-INF/classpath.idx
BOOT-INF/layers.idx

La eligo montras plian JAR nomitan spring-boot-jarmode-layertoolsи layersfle.idxdosiero. Ĉi tiu aldona JAR-dosiero disponigas tavoligkapablojn, kiel priskribite en la sekva sekcio.

Eltiru dependecojn sur apartaj tavoloj

Por vidi kaj ĉerpi tavolojn el nia tavoligita JAR, ni uzas la sisteman posedaĵon -Djarmode=layertoolspor komenco spring-boot-jarmode-layertoolsJAR anstataŭ aplikaĵo:

java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar

Ruli ĉi tiun komandon produktas eligon enhavantan la disponeblajn komandopciojn:

Usage:
  java -Djarmode=layertools -jar usersignup-0.0.1-SNAPSHOT.jar

Available commands:
  list     List layers from the jar that can be extracted
  extract  Extracts layers from the jar for image creation
  help     Help about any command

La eligo montras la komandojn listextractи helpс helpestu la defaŭlta. Ni rulu la komandon kun listopcio:

java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar list
dependencies
spring-boot-loader
snapshot-dependencies
application

Ni vidas liston de dependecoj kiuj povas esti aldonitaj kiel tavoloj.

Tavoloj defaŭlte:

Tavolnomo

Enhavo

dependencies

ajna dependeco, kies versio ne enhavas SNAPSHOT

spring-boot-loader

JAR-Ŝargilo-Klasoj

snapshot-dependencies

ajna dependeco, kies versio enhavas SNAPSHOT

application

aplikaj klasoj kaj rimedoj

Tavoloj estas difinitaj en layers.idxdosiero en la ordo en kiu ili devus esti aldonitaj al la Docker-bildo. Ĉi tiuj tavoloj estas konservitaj en kaŝmemoro sur la gastiganto post la unua alportado ĉar ili ne ŝanĝiĝas. Nur la ĝisdatigita aplikaĵa tavolo estas elŝutita al la gastiganto, kiu estas pli rapida pro la reduktita grandeco .

Konstruante bildon kun dependecoj ĉerpitaj en apartajn tavolojn

Ni konstruos la finan bildon en du paŝoj uzante metodon nomitan plurŝtupa asembleo . En la unua paŝo ni ĉerpos la dependecojn kaj en la dua paŝo ni kopios la ĉerpitajn dependecojn en la finan .

Ni modifu nian Dockerfile por plurŝtupa konstruo:

# the first stage of our build will extract the layers
FROM adoptopenjdk:14-jre-hotspot as builder
WORKDIR application
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract

# the second stage of our build will copy the extracted layers
FROM adoptopenjdk:14-jre-hotspot
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

Ni konservas ĉi tiun agordon en aparta dosiero - Dockerfile2.

Ni konstruas la Docker-bildon per la komando:

docker build -f Dockerfile2 -t usersignup:v1 .

Post ekzekuto de ĉi tiu komando, ni ricevas la sekvan eligon:

Sending build context to Docker daemon  20.41MB
Step 1/12 : FROM adoptopenjdk:14-jre-hotspot as builder
14-jre-hotspot: Pulling from library/adoptopenjdk
.
.
Successfully built a9ebf6970841
Successfully tagged userssignup:v1

Ni povas vidi, ke la bildo de Docker estas kreita kun bilda ID kaj poste etikedita.

Fine, ni rulas la Dive-komandon kiel antaŭe por kontroli la tavolojn ene de la generita Docker-bildo. Ni povas provizi bildan identigilon aŭ etikedon kiel enigon al la Dive-komando:

dive userssignup:v1

Kiel vi povas vidi el la eligo, la tavolo enhavanta la aplikaĵon nun estas nur 11 KB kaj la dependecoj estas konservitaj en apartaj tavoloj. 

Konstruado de Optimumigitaj Docker-bildoj por Spring Boot-Apliko

Eltiru internajn dependecojn sur apartaj tavoloj

Ni povas plu redukti la grandecon de la aplika tavolo ĉerpante iun ajn el niaj kutimaj dependecoj en apartan tavolon anstataŭ paki ilin per la aplikaĵo deklarante ilin en ymlsimila dosiero nomita layers.idx:

- "dependencies":
  - "BOOT-INF/lib/"
- "spring-boot-loader":
  - "org/"
- "snapshot-dependencies":
- "custom-dependencies":
  - "io/myorg/"
- "application":
  - "BOOT-INF/classes/"
  - "BOOT-INF/classpath.idx"
  - "BOOT-INF/layers.idx"
  - "META-INF/"

En ĉi tiu dosiero layers.idxni aldonis kutiman dependecon nomitan, io.myorgenhavanta organizajn dependecojn prenitajn de la komuna deponejo.

konkludo

En ĉi tiu artikolo, ni rigardis uzi Cloud-Native Buildpacks por konstrui ujan bildon rekte de la fonto. Ĉi tio estas alternativo al uzado de Docker por krei ujobildon laŭ la kutima maniero: unue, dika rulebla JAR-dosiero estas kreita kaj poste pakita en ujo-bildon specifante la instrukciojn en la Dockerfile.

Ni ankaŭ rigardis optimumigi nian ujon inkluzivante tavoligantan funkcion, kiu ĉerpas dependecojn en apartajn tavolojn, kiuj estas konservitaj en kaŝmemoro sur la gastiganto kaj maldika aplikaĵa tavolo estas ŝarĝita je planado en la ekzekutmotoroj de la ujo.

Vi povas trovi la tutan fontkodon uzatan en la artikolo ĉe GitHub .

Komando Referenco

Jen resumo de la komandoj, kiujn ni uzis en ĉi tiu artikolo por rapida referenco.

Kunteksta malplenigo:

docker system prune -a

Konstruante ujan bildon kun Dockerfile:

docker build -f <Docker file name> -t <tag> .

Konstruu ujobildon el fonto (sen Dockerfile):

mvn spring-boot:build-image

Rigardu dependecaj tavoloj. Antaŭ ol konstrui la aplikaĵan jardosieron, certigu, ke la tavoliga funkcio estas ebligita en la spring-boot-maven-plugin:

java -Djarmode=layertools -jar application.jar list

Ekstraktu dependecaj tavoloj. Antaŭ ol konstrui la aplikaĵan jardosieron, certigu, ke la tavoliga funkcio estas ebligita en la spring-boot-maven-plugin:

 java -Djarmode=layertools -jar application.jar extract

Vidante Liston de Uj Bildoj

docker images

Vidu maldekstre ene de la ujbildo (certu, ke la plonĝa ilo estas instalita):

dive <image ID or image tag>

fonto: www.habr.com