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  .
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: â Ă«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.
â 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ë  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 packageKjo 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 229MBShikimi i shtresave brenda imazhit të enës
Le të shohim grumbullin e shtresave brenda imazhit. Ne do të përdorim   për të parë këto shtresa:
dive usersignup:v1KĂ«tu Ă«shtĂ« njĂ« pjesĂ« e rezultateve tĂ« ekzekutimit tĂ« komandĂ«s Dive:Â

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
() â Ă«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Ă«.

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}:v1Le 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-imageRezultati 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 suksesNga 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 257MBKrijimi 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.2Më 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:v1Pas 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% completeTë 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  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:

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
trueNe 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-imageNë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 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.idxNë 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.jarEkzekutimi 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 listvarësitë
spring-boot-loader
varësitë-snapshot
aplikacioniNe 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  . 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 v1Shikojmë 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:v1Siç shihet nga dalja, niveli qĂ« pĂ«rmban aplikacionin tani zĂ« vetĂ«m 11 KB, ndĂ«rsa varĂ«sitĂ« ruhet nĂ« shtresa tĂ« veçanta.Â

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ë  .
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 -aKrijimi 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-imageShikoni 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 listShkarkimi 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 extractShikoni listën e imazheve të kontejnerëve
docker imagesShikoni nivelet brenda imazhit të kontejnerit (sigurohuni që të keni instaluar mjetin për zhytje):
diveBurimi: habr.com
