Bouwe optimisearre Docker-ôfbyldings foar in Spring Boot-applikaasje

Containers binne it foarkommende middel wurden wurden om in applikaasje te ferpakken mei al syn software- en bestjoeringssysteemôfhinklikens en se dan oan ferskate omjouwings te leverjen.

Dit artikel behannelt ferskate manieren om in Spring Boot-applikaasje te kontenerearjen:

  • it bouwen fan in dockerôfbylding mei in dockerfile,
  • it bouwen fan in OCI-ôfbylding fan boarne mei Cloud-Native Buildpack,
  • en ôfbyldingsoptimalisaasje by runtime troch JAR-dielen te skieden yn ferskate nivo's mei laach ark.

 Koade foarbyld

Dit artikel wurdt begelaat troch in foarbyld fan wurkkoade op GitHub .

Container terminology

Wy sille begjinne mei de kontenerterminology dy't yn it heule artikel wurdt brûkt:

  • Container ôfbylding: in bestân fan in spesifyk formaat. Wy konvertearje ús applikaasje yn in kontenerôfbylding troch it build-ark út te fieren.
  • Kontener: In útfierbere eksimplaar fan de kontenerôfbylding.
  • Container motor: It daemonproses ferantwurdlik foar it útfieren fan de kontener.
  • Container host: De hostmasine dêr't de kontenermotor op rint.
  • Container registraasje: De algemiene lokaasje dy't brûkt wurdt om de kontenerôfbylding te publisearjen en te fersprieden.
  • OCI standertOpen Container Initiative (OCI) is in lichtgewicht, iepen-boarne-behearskader foarme troch de Linux Foundation. De OCI Image Specification definieart yndustrynormen foar kontenerôfbyldingsformaten en de runtime om te soargjen dat alle kontenermotoren kontenerôfbyldings kinne útfiere dy't makke binne troch elk bouwark.

Om in applikaasje te kontenerearjen, ferpakke wy ús applikaasje yn in kontenerôfbylding en publisearje dy ôfbylding yn it iepenbiere register. De kontener-runtime hellet dizze ôfbylding út it register, pakt it út en rint de applikaasje deryn.

Ferzje 2.3 fan Spring Boot biedt plugins foar it bouwen fan OCI-ôfbyldings.

Havenarbeider is de meast brûkte kontener-ymplemintaasje, en wy brûke Docker yn ús foarbylden, dus alle folgjende kontenerferwizings yn dit artikel sille ferwize nei Docker.

It bouwen fan in kontenerôfbylding op 'e tradisjonele manier

Docker-ôfbyldings bouwe foar Spring Boot-applikaasjes is heul maklik troch in pear ynstruksjes ta te foegjen oan jo Dockerfile.

Wy meitsje earst in útfierbere JAR en, as ûnderdiel fan 'e Dockerfile-ynstruksjes, kopiearje de útfierbere JAR boppe op' e basis JRE-ôfbylding nei it tapassen fan de nedige oanpassingen.

Litte wy ús Spring-applikaasje oanmeitsje Spring Initializr mei ôfhinklikens weblombokи actuator. Wy foegje ek in restcontroller ta om in API te leverjen GETmetoade.

In Dockerfile oanmeitsje

Wy pleatse dizze applikaasje dan yn in kontener troch tafoegjen Dockerfile:

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

Us Dockerfile befettet in basisôfbylding, fan adoptopenjdk, wêrop wy ús JAR-bestân kopiearje en dan de poarte iepenje, 8080dy't sil harkje nei fersiken.

Applikaasje gearkomste

Earst moatte jo in applikaasje oanmeitsje mei Maven of Gradle. Hjir brûke wy Maven:

mvn clean package

Dit makket in útfierber JAR-bestân foar de applikaasje. Wy moatte dizze útfierbere JAR konvertearje nei in Docker-ôfbylding om op 'e Docker-motor te rinnen.

Meitsje in kontenerôfbylding

Wy sette dan dizze JAR-útfierbere yn 'e Docker-ôfbylding troch it kommando út te fieren docker buildfan 'e root-map fan it projekt mei de Dockerfile dy't earder makke is:

docker build  -t usersignup:v1 .

Wy kinne ús ôfbylding sjen yn 'e list mei it kommando:

docker images 

De útfier fan it boppesteande kommando befettet ús ôfbylding usersignuptegearre mei de basisôfbylding, adoptopenjdkspesifisearre yn ús Dockerfile.

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

Besjoch lagen binnen in kontenerôfbylding

Litte wy nei de stapel lagen yn 'e ôfbylding sjen. Wy sille brûke ynstrumint  dûke, om dizze lagen te besjen:

dive usersignup:v1

Hjir is in diel fan 'e útfier fan it kommando Dive: 

Bouwe optimisearre Docker-ôfbyldings foar in Spring Boot-applikaasje

Lykas wy kinne sjen, makket de tapassingslaach in signifikant diel út fan 'e ôfbyldingsgrutte. Wy wolle de grutte fan dizze laach ferminderje yn 'e folgjende seksjes as ûnderdiel fan ús optimalisaasje.

It bouwen fan in kontenerôfbylding mei Buildpack

Assembly pakketten (Buildpacks) is in generike term dy't brûkt wurdt troch ferskate Platform as in Service (PAAS) oanbiedingen om in kontenerôfbylding te meitsjen fan boarnekoade. It waard lansearre troch Heroku yn 2011 en is sûnt oannommen troch Cloud Foundry, Google App Engine, Gitlab, Knative en in pear oaren.

Bouwe optimisearre Docker-ôfbyldings foar in Spring Boot-applikaasje

Foardiel fan Cloud Build Packages

Ien fan 'e wichtichste foardielen fan it brûken fan Buildpack om ôfbyldings te bouwen is dat image konfiguraasje feroarings kinne wurde beheard sintraal (bouwer) en propagearre nei alle applikaasjes mei help fan de bouwer.

De boupakketten wiene nau bûn oan it platfoarm. Cloud-Native Buildpacks leverje standerdisearring oer platfoarms troch it OCI-ôfbyldingsformaat te stypjen, wat soarget dat de ôfbylding kin wurde útfierd troch de Docker-motor.

Mei help fan de Spring Boot Plugin

De Spring Boot-plugin bout OCI-ôfbyldings fan boarne mei Buildpack. Ofbyldings wurde makke mei bootBuildImagetaken (Gradle) of spring-boot:build-imagedoel (Maven) en lokale Docker ynstallaasje.

Wy kinne de namme fan 'e ôfbylding oanpasse dy't wy moatte drukke nei it Docker-register troch de namme yn te spesifisearjen 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>

Litte wy Maven brûke om út te fieren build-imagedoelen foar it meitsjen fan in applikaasje en it meitsjen fan in kontenerôfbylding. Wy brûke op it stuit gjin Dockerfiles.

mvn spring-boot:build-image

It resultaat sil sa'n ding wêze:

[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'

Ut de útfier sjogge wy dat paketo Cloud-Native buildpackbrûkt om in wurkjende OCI-ôfbylding te meitsjen. Lykas earder kinne wy ​​​​de ôfbylding sjen litte as in Docker-ôfbylding troch it kommando út te fieren:

docker images 

Fermelding:

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

It meitsjen fan in kontenerôfbylding mei Jib

Jib is in plugin foar it meitsjen fan ôfbyldings fan Google dy't in alternative metoade leveret foar it meitsjen fan in kontenerôfbylding fan boarne.

Konfigurearje jib-maven-pluginyn pom.xml:

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

Dêrnei rinne wy ​​de Jib-plugin mei it Maven-kommando om de applikaasje te bouwen en de kontenerôfbylding te meitsjen. Lykas earder brûke wy hjir gjin Dockerfiles:

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

Nei it útfieren fan it boppesteande Maven-kommando krije wy de folgjende útfier:

[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

De útfier lit sjen dat de kontenerôfbylding is makke en pleatst yn it register.

Motivaasjes en metoaden foar it meitsjen fan optimalisearre ôfbyldings

Wy hawwe twa haadredenen foar optimalisearjen:

  • Produktiviteit: Yn in kontenerorkestraasjesysteem wurdt de kontenerôfbylding lutsen fan it byldregister nei de host dy't de kontenermotor draait. Dit proses wurdt planning neamd. It lûken fan grutte ôfbyldings út it register resultearret yn lange skematiden yn kontenerorkestraasjesystemen en lange bouwtiden yn CI-pipelines.
  • Feiligens: grutte bylden hawwe ek in grut gebiet foar kwetsberens.

In Docker-ôfbylding is opboud út in stapel lagen, elk dy't in ferklearring yn ús Dockerfile fertsjintwurdiget. Elke laach stiet foar de delta fan feroaringen yn 'e ûnderlizzende laach. As wy in Docker-ôfbylding út it register lûke, wurdt it yn lagen lutsen en op 'e host bewarre.

Spring Boot brûkt "fat JAR" yn as it standert ferpakkingsformaat. As wy sjogge nei in fet JAR, wy sjogge dat de applikaasje is in hiel lyts part fan de hiele JAR. Dit is it diel dat it meast feroaret. De rest bestiet út Spring Framework ôfhinklikens.

De optimalisaasjeformule is sintraal om it isolearjen fan de applikaasje op in apart nivo fan Spring Framework-ôfhinklikens.

De ôfhinklikheidslaach dy't it grutste part fan it dikke JAR-bestân foarmet, wurdt mar ien kear ynladen en yn it cache op it hostsysteem bewarre.

Allinich in tinne laach fan 'e app wurdt lutsen tidens app-updates en kontenerplanning, lykas werjûn yn dit diagram:

Bouwe optimisearre Docker-ôfbyldings foar in Spring Boot-applikaasje

Yn 'e folgjende seksjes sille wy sjen hoe't jo dizze optimalisearre ôfbyldings kinne meitsje foar in Spring Boot-applikaasje.

Bouwe in optimisearre kontenerôfbylding foar in Spring Boot-applikaasje mei Buildpack

Spring Boot 2.3 stipet lagen troch dielen fan in dikke JAR-bestân te ekstrahearjen yn aparte lagen. De lagenfunksje is standert útskeakele en moat eksplisyt ynskeakele wurde mei it Spring Boot Maven-plugin:

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

Wy sille dizze konfiguraasje brûke om ús kontenerôfbylding earst te bouwen mei Buildpack en dan mei Docker yn 'e folgjende seksjes.

Litte wy drave build-imageMaven doel om in kontenerôfbylding te meitsjen:

mvn spring-boot:build-image

As wy Dive útfiere om de lagen yn 'e resultearjende ôfbylding te sjen, kinne wy ​​​​sjogge dat de applikaasjelaach (omjûn yn read) folle lytser is yn it kilobyteberik yn ferliking mei wat wy krigen mei it dikke JAR-formaat:

Bouwe optimisearre Docker-ôfbyldings foar in Spring Boot-applikaasje

Bouwe in optimisearre kontenerôfbylding foar in Spring Boot-applikaasje mei Docker

Ynstee fan in Maven- of Gradle-plugin te brûken, kinne wy ​​ek in lagen Docker JAR-ôfbylding meitsje mei in Docker-bestân.

As wy Docker brûke, moatte wy twa ekstra stappen nimme om de lagen te ekstrahearjen en te kopiearjen yn 'e definitive ôfbylding.

De ynhâld fan 'e resultearjende JAR nei it bouwen mei Maven mei lagen ynskeakele sil der sa útsjen:

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

De útfier toant in ekstra JAR neamd spring-boot-jarmode-layertoolsи layersfle.idxmap. Dit ekstra JAR-bestân leveret laachmooglikheden, lykas beskreaun yn 'e folgjende paragraaf.

Extract ôfhinklikens op aparte lagen

Om lagen te besjen en te ekstrahearjen fan ús lagen JAR, brûke wy it systeemeigenskip -Djarmode=layertoolsfoar start spring-boot-jarmode-layertoolsJAR ynstee fan tapassing:

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

It útfieren fan dit kommando produsearret in útfier mei de beskikbere kommando-opsjes:

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

De útfier toant de kommando's listextractи helpс helpde standert wêze. Litte wy it kommando útfiere mei listopsje:

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

Wy sjogge in list mei ôfhinklikens dy't kinne wurde tafoege as lagen.

Standert lagen:

Laach namme

Ynhâld

dependencies

elke ôfhinklikens waans ferzje gjin SNAPSHOT befettet

spring-boot-loader

JAR Loader Classes

snapshot-dependencies

elke ôfhinklikens waans ferzje SNAPSHOT befettet

application

applikaasje klassen en boarnen

Lagen wurde definiearre yn layers.idxtriem yn 'e folchoarder wêryn't se moatte wurde tafoege oan de Docker-ôfbylding. Dizze lagen wurde nei it earste opheljen op 'e host yn 'e cache bewarre, om't se net feroarje. Allinich de bywurke applikaasjelaach wurdt ynladen nei de host, wat rapper is troch de fermindere grutte .

It bouwen fan in ôfbylding mei ôfhinklikens ekstrahearre yn aparte lagen

Wy sille bouwe de definitive ôfbylding yn twa stappen mei help fan in metoade neamd multi-stage gearkomste . Yn 'e earste stap sille wy de ôfhinklikens ekstrahearje en yn' e twadde stap sille wy de ekstrahearre ôfhinklikens kopiearje nei de finale.

Litte wy ús Dockerfile wizigje foar in multi-stage build:

# 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"]

Wy bewarje dizze konfiguraasje yn in apart bestân - Dockerfile2.

Wy bouwe de Docker-ôfbylding mei it kommando:

docker build -f Dockerfile2 -t usersignup:v1 .

Nei it útfieren fan dit kommando krije wy de folgjende útfier:

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

Wy kinne sjen dat de Docker-ôfbylding is makke mei in ôfbyldings-ID en dan tagged.

Uteinlik rinne wy ​​it kommando Dive lykas earder om de lagen yn 'e generearre Docker-ôfbylding te kontrolearjen. Wy kinne in ôfbyldings-ID of tag leverje as ynfier foar it Dive-kommando:

dive userssignup:v1

Lykas jo kinne sjen fan 'e útfier, is de laach mei de applikaasje no mar 11 KB en de ôfhinklikens wurde yn aparte lagen bewarre. 

Bouwe optimisearre Docker-ôfbyldings foar in Spring Boot-applikaasje

Extract ynterne ôfhinklikens op aparte lagen

Wy kinne de grutte fan 'e applikaasjelaach fierder ferminderje troch ien fan ús oanpaste ôfhinklikens te ekstrahearjen yn in aparte laach ynstee fan se te ferpakken mei de applikaasje troch se te ferklearjen yn ymlferlykbere triem neamd 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/"

Yn dizze triem layers.idxwy hawwe in oanpaste ôfhinklikens tafoege mei de namme, io.myorgmei organisaasjeôfhinklikens ophelle út it dielde repository.

konklúzje

Yn dit artikel hawwe wy sjoen nei it brûken fan Cloud-Native Buildpacks om in kontenerôfbylding direkt fan boarne te bouwen. Dit is in alternatyf foar it brûken fan Docker om in kontenerôfbylding op 'e gewoane manier te meitsjen: earst wurdt in dikke útfierbere JAR-bestân oanmakke en dan ferpakt yn in kontenerôfbylding troch de ynstruksjes yn' e Dockerfile op te jaan.

Wy hawwe ek sjoen nei it optimalisearjen fan ús kontener troch in lagenfunksje op te nimmen dy't ôfhinklikens ekstrahearret yn aparte lagen dy't yn 'e cache binne op' e host en in tinne applikaasjelaach wurdt laden op 'e skematiid yn' e útfieringsmotoren fan 'e kontener.

Jo kinne fine alle boarne koade brûkt yn it artikel op Github .

Kommando Referinsje

Hjir is in gearfetting fan 'e kommando's dy't wy yn dit artikel hawwe brûkt foar in rappe referinsje.

Kontekst wiskje:

docker system prune -a

Bouwe in kontenerôfbylding mei in Dockerfile:

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

Bou containerôfbylding fan boarne (sûnder Dockerfile):

mvn spring-boot:build-image

Besjoch ôfhinklikheidlagen. Foardat jo it applikaasje-jar-bestân bouwe, soargje derfoar dat de lagenfunksje ynskeakele is yn 'e spring-boot-maven-plugin:

java -Djarmode=layertools -jar application.jar list

Extract ôfhinklikheid lagen. Foardat jo it applikaasje-jar-bestân bouwe, soargje derfoar dat de lagenfunksje ynskeakele is yn 'e spring-boot-maven-plugin:

 java -Djarmode=layertools -jar application.jar extract

Besjoch in list mei kontenerôfbyldings

docker images

Sjoch links yn 'e kontenerôfbylding (soargje derfoar dat it dûkark ynstalleare is):

dive <image ID or image tag>

Boarne: www.habr.com