Krijimi i imazheve të optimizuara Docker për aplikacionin Spring Boot

Kontejnerët janë bërë mjeti i preferuar për paketimin e aplikacioneve me të gjitha varësitë e softuerit dhe sistemin operativ, dhe më pas dërgimin e tyre në ambiente të ndryshme.

Në këtë artikull shqyrtohen mënyrat e ndryshme të kontejnizimit të aplikacionit Spring Boot:

  • krijimi i njĂ« imazhi Docker me ndihmĂ«n e njĂ« skedari Docker,
  • krijimi i njĂ« imazhi OCI nga kodin burimor me ndihmĂ«n e Cloud-Native Buildpack,
  • dhe optimizimi i imazhit gjatĂ« ekzekutimit duke ndarĂ« pjesĂ«t e JAR nĂ« nivele tĂ« ndryshme me ndihmĂ«n e mjeteve shumĂ«pĂ«rmasore.

 Shembulli i kodit

Ky artikull shoqërohet nga një shembull funksional i kodit në GitHub .

Terminologjia e konteinerëve

Ne do të fillojmë me terminologjinë e konteinerëve të përdorur në artikull:

  • Imazhi i konteinerit (Container image): njĂ« skedar me format tĂ« caktuar. Ne e konvertojmĂ« aplikacionin tonĂ« nĂ« njĂ« imazh konteineri duke ekzekutuar mjetin e ndĂ«rtimit.
  • Kontejner: njĂ« instancĂ« qĂ« ekzekutohet e imazhit tĂ« konteinerit.
  • Motorri i konteinerit (Container engine): njĂ« proces-demoni qĂ« Ă«shtĂ« pĂ«rgjegjĂ«s pĂ«r ekzekutimin e konteinerit.
  • Hosti i konteinerit (Container host): kompjuteri host nĂ« tĂ« cilin funksionon mekanizmi i konteinerit.
  • Regjistri i kontejnerĂ«ve (Container registry): njĂ« vend i pĂ«rgjithshĂ«m i pĂ«rdorur pĂ«r publikimin dhe shpĂ«rndarjen e imazheve tĂ« kontejnerĂ«ve.
  • Standardi OCI: Open Container Initiative (OCI) — Ă«shtĂ« njĂ« strukturĂ« e lehtĂ« dhe e hapur e menaxhimit, e formuar nĂ« kuadĂ«r tĂ« Fondacionit Linux. Specifikimi i imazheve OCI pĂ«rcakton standardet e industrisĂ« pĂ«r format e imazheve tĂ« kontejnerĂ«ve dhe ambientet e ekzekutimit, pĂ«r tĂ« garantuar qĂ« tĂ« gjithĂ« mekanizmat e kontejnerĂ«ve mund tĂ« ekzekutojnĂ« imazhet e kontejnerĂ«ve tĂ« krijuara nga çdo mjet ndĂ«rtimi.

Për të vendosur aplikacionin në një kontejner, ne e mbyllim aplikacionin tonë në një imazh kontejneri dhe e publikojmë këtë imazh në regjistrin e përbashkët. Ambienti i ekzekutimit të kontejnerëve e nxjerr këtë imazh nga regjistri, e shpërbën atë dhe fillon aplikacionin brenda tij.

Versioni 2.3 i Spring Boot ofron plugina për krijimin e imazheve OCI.

Docker — implementimi mĂ« i pĂ«rdorur i kontejnerĂ«ve, dhe ne pĂ«rdorim Docker nĂ« shembujt tanĂ«, prandaj tĂ« gjitha referimet e mĂ«tejshme nĂ« kontejner nĂ« kĂ«tĂ« artikull do tĂ« duan tĂ« thonĂ« Docker.

Krijimi i imazhit të kontejnerit në mënyrë tradicionale

Të krijosh imazhe Docker për aplikacionet Spring Boot është shumë e lehtë, mjafton të shtosh disa udhëzime në skedarin Docker.

Fillimisht krijojmë skedarin ekzekutiv JAR dhe, si pjesë e udhëzimeve të skedarit Docker, kopjojmë skedarin ekzekutiv JAR mbi imazhin bazë JRE pas aplikimit të konfigurimeve të nevojshme.

Të krijojmë aplikacionin tonë Spring në Spring Initializr me varësitë web, lombokdhe aktuator. Ne gjithashtu shtojmë një kontrollues rest për të ofruar API me GETmetodën.

Krijimi i skedarit Docker

Më pas ne e vendosim këtë aplikacion në kontejner, duke shtuar Dockerfile:

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

Skedari ynë Docker përmban një imazh bazë, nga adoptopenjdk, mbi të cilin ne kopjojmë skedarin tonë JAR, dhe më pas hapim portin 8080i cili do të dëgjojë kërkesat.

Ndërtimi i aplikacionit

Fillimisht duhet të krijojmë aplikacionin me Maven ose Gradle. Këtu përdorim Maven:

mvn clean package

Kjo krijon skedarin ekzekutiv JAR të aplikacionit. Na nevojitet ta konvertojmë këtë JAR ekzekutiv në një imazh Docker për të funksionuar në motorin Docker.

Krijimi i imazhit të kontejnerit

Më pas ne e vendosim këtë skedar ekzekutiv JAR në imazhin Docker, duke ekzekutuar komandën docker buildnga direktoria e projektit, që përmban skedarin Docker, të krijuar më parë:

docker build  -t usersignup:v1 .

Ne mund ta shohim imazhin tonë në listë duke përdorur komandën:

docker images 

Rezultati i ekzekutimit të komandës së mësipërme përfshin imazhin tonë usersignupbashkë me imazhin bazë, adoptopenjdk, i specifikuar në skedarin tonë Docker.

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

Shikimi i shtresave brenda imazhit të enës

Le të shohim grumbullin e shtresave brenda imazhit. Ne do të përdorim mjetin  dive, për të parë këto shtresa:

dive usersignup:v1

Këtu është një pjesë e rezultateve të ekzekutimit të komandës Dive: 

Krijimi i imazheve të optimizuara Docker për aplikacionin Spring Boot

Siç shohim, niveli aplikativ përbën një pjesë të rëndësishme të madhësisë së imazhit. Ne duam të ulim madhësinë e kësaj shtrese në seksionet në vijim në kuadër të optimizimit tonë.

Krijimi i imazhit të enës me ndihmën e Buildpack

Buildpack-et (Buildpacks) — Ă«shtĂ« njĂ« term i pĂ«rgjithshĂ«m qĂ« pĂ«rdoret nga ofertat e ndryshme 'Platform as a Service' (PAAS) pĂ«r tĂ« ndĂ«rtuar imazhin e enĂ«s nga kodi burimor. Ai u lançua nga Heroku nĂ« 2011 dhe qĂ« atĂ«herĂ« Ă«shtĂ« pranuar nga Cloud Foundry, Google App Engine, Gitlab, Knative dhe disa tĂ« tjerĂ«.

Krijimi i imazheve të optimizuara Docker për aplikacionin Spring Boot

Përparësia e paketave ndërtuese në cloud

Një nga përfitimet kryesore të përdorimit të Buildpack për krijimin e imazheve është se ndryshimet në konfigurimin e imazhit mund të menaxhohen në mënyrë qendrore (builder) dhe të shpërndahen në të gjitha aplikacionet që përdorin builder-in.

Paketat ndërtuese ishin ngushtësisht të lidhura me platformën. Cloud-Native Buildpacks ofrojnë standardizim midis platformave, duke mbështetur formatin e imazhit OCI, i cili garanton se imazhi mund të ekzekutohet nga motori Docker.

Përdorimi i plugin-it të Spring Boot

Plugin-i Spring Boot krijon imazhe OCI nga kodi burimor duke përdorur Buildpack. Imazhet krijohen duke përdorur bootBuildImagedetyrën (Gradle) ose spring-boot:build-imageqëllimin (Maven) dhe instalimin lokal të Docker.

Ne mund të konfigurojmë emrin e imazhit të nevojshëm për të dërguar në regjistrin Docker duke specifikuar emrin në image tag:

org.springframework.boot
  spring-boot-maven-plugin
  
    
      docker.io/pratikdas/${project.artifactId}:v1

Le të përdorim Maven për të realizuar build-imageqëllimin për të krijuar aplikacionin dhe për të krijuar imazhin e kontejnerit. Tani nuk po përdorim asnjë skedar Docker.

mvn spring-boot:build-image

Rezultati do të jetë afërsisht kështu:

[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:build-image (default-cli) @ usersignup ---
[INFO] Po ndërtohet imazhi 'docker.io/pratikdas/usersignup:v1'
[INFO] 
[INFO]  > Po tërhiqet imazhi i ndërtuesit 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%
.
.
.. [creator]     Duke shtuar etiketën 'org.springframework.boot.version'
.. [creator]     *** Imazhet (c311fe74ec73):
.. [creator]           docker.io/pratikdas/usersignup:v1
[INFO] 
[INFO] Imazhi 'docker.io/pratikdas/usersignup:v1' u ndërtua me sukses

Nga informacioni i dalë shohim se paketo Cloud-Native buildpackpërdoret për të ndërtuar një imazh të punueshëm OCI. Si më parë, ne mund të shohim imazhin, i specifikuar si imazhi Docker, duke ekzekutuar komandën:

docker images 

Dalja:

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

Krijimi i imazhit të konteinerit me Jib

Jib është një konfigurim për ndërtimin e imazhesh nga Google, i cili ofron një metodë alternative për krijimin e një imazhi konteineri nga kodi burimor.

Konfigurojmë jib-maven-pluginnë pom.xml:

      com.google.cloud.tools
        jib-maven-plugin
        2.5.2

Më pas, ne ekzekutojmë plugin-in Jib me komandën Maven për të ndërtuar aplikacionin dhe për të krijuar imazhin e konteinerit. Si më parë, këtu nuk përdorim ndonjë skedë Docker:

mvn compile jib:build -Dimage=/usersignup:v1

Pas pastrimi i komandës Maven të mësipërme, marrim rezultatin në vijim:

[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

Të dhënat e dalura tregojnë se imazhi i kontejnerit është krijuar dhe vendosur në regjistrin.

Motivimet dhe metodat për optimizimin e imazheve

Kemi dy arsye kryesore për optimizimin:

  • Performanca: nĂ« sistemin e orkestrimit tĂ« kontejnerĂ«ve, imazhi i kontejnerit tĂ«rhiqet nga regjistri i imazheve nĂ« hostin ku Ă«shtĂ« aktivizuar mekanizmi i kontejnerĂ«ve. Ky proces quhet planifikim. TĂ«rheqja e imazheve tĂ« mĂ«dha nga regjistri çon nĂ« kohĂ« tĂ« gjatĂ« planifikimi nĂ« sistemet e orkestrimit tĂ« kontejnerĂ«ve dhe kohĂ« tĂ« zgjatur ndĂ«rtimi nĂ« tubacionet CI.
  • Siguria: imazhet e mĂ«dha gjithashtu kanĂ« njĂ« hapĂ«sirĂ« mĂ« tĂ« madhe pĂ«r dobĂ«si.

Imazhi Docker pĂ«rbĂ«het nga njĂ« shtresĂ« tĂ« stack-ut, çdo njĂ«ra pĂ«rfaqĂ«son njĂ« udhĂ«zim nĂ« Dockerfile-nĂ« tonĂ«. Çdo nivel pĂ«rfaqĂ«son njĂ« deltĂ« tĂ« ndryshimeve tĂ« nivelit nĂ«n. Kur ne nxjerrim njĂ« imazh Docker nga regjistri, ai nxirret nĂ« nivele dhe ruhet nĂ« host.

Spring Boot përdor «JAR i trashë» si formatin e paketimit të paracaktuar. Kur shohim një JAR të trashë, ne shohim se aplikacioni përbën një pjesë shumë të vogël të gjithë JAR-it. Kjo është pjesa që ndryshon më shpesh. Pjesa tjetër përbëhet nga varësitë e Spring Framework.

Formula e optimizimit përqëndrohet rreth izolimit të aplikacionit në një nivel të veçantë nga varësitë e Spring Framework.

Niveli i varësive, që formon pjesën kryesore të JAR-it të trashë, ngarkohet vetëm një herë dhe ruhet në sistemin host.

Vetëm niveli i hollë i aplikacionit tërheqët gjatë azhurnimeve të aplikacionit dhe planifikimit të kontejnerëve, siç tregohet në këtë diagram:

Krijimi i imazheve të optimizuara Docker për aplikacionin Spring Boot

Në seksionet në vazhdim do të shqyrtojmë si të krijojmë këto imazhe të optimizuara për aplikacionin Spring Boot.

Krijimi i një imazhi të optimizuar të kontejnerit për aplikacionin Spring Boot me ndihmën e Buildpack

Spring Boot 2.3 mbështet shumë-nivellësinë duke nxjerrë pjesë të skedarit të trashë JAR në nivele të veçanta. Funksioni i përmbysjes është i çaktivizuar nga default dhe duhet të aktivizohet qartë me anë të plugin-it Spring Boot Maven:

org.springframework.boot
  spring-boot-maven-plugin
  
    
      true

Ne do të përdorim këtë konfigurim për të ndërtuar imazhin tonë të kontejnerit fillimisht me ndihmën e Buildpack, dhe pastaj me Docker në seksionet në vazhdim.

Le të ekzekutojmë build-imageqëllimin Maven për të krijuar imazhin e kontejnerit:

mvn spring-boot:build-image

Nëse ekzekutojmë Dive për të parë nivelet në imazhin rezultues, do të shohim se niveli i aplikacionit (i rrethuar me të kuqe) është shumë më i vogël në intervalin e kilobajtëve krahasuar me atë që morëm duke përdorur formatin e trashë JAR:

Krijimi i imazheve të optimizuara Docker për aplikacionin Spring Boot

Krijimi i një imazhi të optimizuar të kontejnerit për aplikacionin Spring Boot me ndihmën e Docker

Në vend të përdorimit të plugin-it Maven ose Gradle, ne gjithashtu mund të krijojmë një imazh shumë-nivellësh JAR Docker me një skedë Docker.

Kur përdorim Docker, na nevojitet të kryejmë dy hapa shtesë për të nxjerrë shtresat dhe t'i kopjojmë ato në imazhin përfundimtar.

Përmbajtja e JAR-it të marrë pas ndërtimit me Maven me funksionin e shtresimit aktivizuar do të duket si më poshtë:

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

Në daljet shfaqet një JAR shtesë me emrin spring-boot-jarmode-layertoolsdhe layersfle.idxfile. Ky JAR shtesë ofron mundësinë e përpunimit me shumë shtresa, siç përshkruhet në seksionin e mëposhtëm.

Nxjerrja e varësive në shtresa të veçanta

Për të parë dhe nxjerrë shtresat nga JAR-i ynë me shumë shtresa, ne përdorim pronën sistemore -Djarmode=layertoolspër të ekzekutuar spring-boot-jarmode-layertoolsJAR-in në vend të aplikacionit:

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

Ekzekutimi i këtij komandimi jep një dalje që përmban opsionet e disponueshme të komandës:

Përdorimi:
  java -Djarmode=layertools -jar usersignup-0.0.1-SNAPSHOT.jar

Komandat e disponueshme:
  list     Liston shtresat nga jar që mund të nxirren
  extract  Nxjerr shtresat nga jar për krijimin e imazhit
  help     Ndihmë për çdo komandë

Dalja tregon komandat listë, nxjerrdhe helpme helpduhet të jetë e paracaktuar. Le të ekzekutojmë komandën me listëopsionin:

java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar list
varësitë
spring-boot-loader
varësitë-snapshot
aplikacioni

Ne shohim një listë varësish që mund të shtohen si shtresa.

Shtresat e paracaktuara:

Emri i shtresës

Përmbajtja

varësi

çdo varësi, versioni i së cilës nuk përmban SNAPSHOT

spring-boot-loader

Klasa e ngarkuesit JAR

varësitë-snapshot

çdo varësi, versioni i së cilës përmban SNAPSHOT

aplikacioni

klasat e aplikacioneve dhe burimet

Shtresat janë të përcaktuara në layers.idxskedari në renditjen në të cilën ato duhet të shtohen në imazhin Docker. Këto shtresa ruhen në host pas nxjerrjes së parë, pasi ato nuk ndryshojnë. Në host ngarkohet vetëm niveli i azhurnuar i aplikacionit, që ndodh më shpejt për shkak të madhësisë së zvogëluar .

Ndërtimi i imazhit me varësitë e nxjerra në shtresa të veçanta

Ne do tĂ« ndĂ«rtojmĂ« imazhin final nĂ« dy etapa, duke pĂ«rdorur njĂ« metodĂ« tĂ« quajtur ndĂ«rtim shumĂ«pĂ«rfitues . NĂ« etapĂ«n e parĂ« ne do tĂ« nxjerrim varĂ«sitĂ«, dhe nĂ« etapĂ«n e dytĂ« do t’i kopjojmĂ« varĂ«sitĂ« e nxjerra nĂ« imazhin pĂ«rfundimtar.

Le të modifikojmë skedarin tonë Docker për ndërtimin shumëpërfitues:

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

RuajmĂ« kĂ«tĂ« konfigurim nĂ« njĂ« skedĂ« tĂ« veçantĂ« — Dockerfile2.

Ndërtojmë imazhin Docker me komandën:

docker build -f Dockerfile2 -t usersignup:v1 .

Pas pastrimit të këtij urdhri, ne marrim këtë dalje:

Dërgimi i kontekstit të ndërtimit në demon Docker 20.41MB
Hapi 1/12 : NGA adoptopenjdk:14-jre-hotspot si ndërtues
14-jre-hotspot: Po tërhiqet nga biblioteka/adoptopenjdk
.
.
Ndërtimi u realizua me sukses a9ebf6970841
U ndërlidh me sukses pengguna v1

Shikojmë se imazhi Docker po krijohet me identifikuesin e imazhit, dhe më pas etiketizohet.

Në fund, ekzekutojmë komandën Dive, ashtu si më herët, për të kontrolluar shtresat brenda imazhit Docker të gjeneruar. Ne mund të japim identifikuesin e imazhit ose etiketën si hyrje për komandën Dive:

dive userssignup:v1

Siç shihet nga dalja, niveli që përmban aplikacionin tani zë vetëm 11 KB, ndërsa varësitë ruhet në shtresa të veçanta. 

Krijimi i imazheve të optimizuara Docker për aplikacionin Spring Boot

Nxjerrja e varësive të brendshme në shtresa të veçanta

Ne mund të zvogëlojmë më tej madhësinë e nivelit të aplikacionit, duke nxjerrë ndonjë nga varësitë tona të personalizuara në një nivel të veçantë në vend që t'i paketojmë ato së bashku me aplikacionin, duke i shpallur ato në ymlnjë skedar të tillë me emrin 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/"

Në këtë skedar layers.idxne kemi shtuar një varësi të personalizuar me emrin, io.myorgme organizatat e varura nga një depo e përbashkët.

Përfundim

Në këtë artikull shqyrtuam përdorimin e Cloud-Native Buildpacks për të krijuar një imazh kontejneri direkt nga kodi burimor. Kjo është një alternativë ndaj përdorimit të Docker për të krijuar një imazh kontejneri në mënyrën tradicionale: fillimisht krijohet një skedar të ekzekutueshëm të trashë JAR, dhe pastaj paketimi i tij në një imazh kontejneri duke specifikuar udhëzime në skedarin Docker.

Ne gjithashtu shqyrtuam optimizimin e kontejnerit tonë duke përfshirë funksionin e mbivendosjes, i cili nxjerr varësitë në nivele të veçanta që ruhen në host, ndërsa shtresa e hollë e aplikacionit ngarkohet gjatë planifikimit në mekanizmat e ekzekutimit të kontejnerëve.

Mund të gjeni të gjithë kodin burimor që është përdorur në artikull në Github .

Udhëzues i komandave

Ja një përmbledhje e shpejtë e komandave që kemi përdorur në këtë artikull për njohuri të shpejtë.

Pastrimi i kontekstit:

docker system prune -a

Krijimi i imazhit të kontejnerit me skedarin Docker:

docker build -f  -t  .

Ndërtojmë imazhin e kontejnerit nga kodi burimor (pa Dockerfile):

mvn spring-boot:build-image

Shikoni nivelet e varësive. Para se të ndiheni JAR-në e aplikacionit, sigurohuni që funksioni i mbivendosjes të jetë i aktivizuar në spring-boot-maven-plugin:

java -Djarmode=layertools -jar application.jar list

Shkarkimi i niveleve të varësive. Para se të ndiheni JAR-në e aplikacionit, sigurohuni që funksioni i mbivendosjes të jetë i aktivizuar në spring-boot-maven-plugin:

 java -Djarmode=layertools -jar application.jar extract

Shikoni listën e imazheve të kontejnerëve

docker images

Shikoni nivelet brenda imazhit të kontejnerit (sigurohuni që të keni instaluar mjetin për zhytje):

dive

Burimi: habr.com

Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« đŸ”„ Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« | ProHoster