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 pastaj dorëzimin e tyre në mjedise të ndryshme.

Në këtë artikull shqyrtohen mënyrat e ndryshme për konteinerizimin e aplikacioneve Spring Boot:

  • krijimi i njĂ« imazhi Docker me anĂ« tĂ« skedarit Docker,
  • krijimi i njĂ« imazhi OCI nga kodi burimor me anĂ« tĂ« Cloud-Native Buildpack,
  • dhe optimizimi i imazhit gjatĂ« ekzekutimit duke ndarĂ« pjesĂ«t JAR nĂ« nivele tĂ« ndryshme me anĂ« tĂ« mjeteve shumĂ«-nivelĂ«she.

 Shembulli i kodit

Ky artikull shoqërohet me një shembull kodi në punë në GitHub .

Terminologjia e kontejnerëve

Do të fillojmë me terminologjinë e kontejnerëve që përdoret në artikull:

  • Imazhi i kontejnerit (Container image): njĂ« skedar me format tĂ« caktuar. Ne e konvertojmĂ« aplikacionin tonĂ« nĂ« njĂ« imazh kontejneri duke ekzekutuar njĂ« mjet ndĂ«rtimi.
  • Konteineri: njĂ« ekzemplar ekzekutiv i imazhit tĂ« kontejnerit.
  • Motori i kontejnerit (Container engine): proces demoni qĂ« Ă«shtĂ« pĂ«rgjegjĂ«s pĂ«r ekzekutimin e kontejnerit.
  • Hosti i kontejnerit (Container host): kompjuteri host nĂ« tĂ« cilin funksionon mekanizmi i kontejnerit.
  • Regjistri i kontejnerĂ«ve (Container registry): njĂ« vend i zakonshĂ«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Ă« e hapur e administratĂ«s, e formuar nĂ«n Linux Foundation. Specifikimi i imazheve OCI pĂ«rcakton standardet e industrisĂ« pĂ«r format e imazheve tĂ« kontejnerĂ«ve dhe mjediseve tĂ« ekzekutimit, pĂ«r tĂ« garantuar se tĂ« gjithĂ« mekanizmat e kontejnerĂ«ve mund tĂ« ekzekutojnĂ« imazhet e kontejnerĂ«ve tĂ« krijuar nga çdo mjet ndĂ«rtimi.

Për të vendosur aplikacionin në kontejner, ne e mbështjellim aplikacionin tonë në një imazh kontejneri dhe e publikojmë këtë imazh në një regjistër të zakonshëm. Mjedisi i ekzekutimit të kontejnerit e nxjerr këtë imazh nga regjistri, e shpërndan atë dhe ekzekuton 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 referencat e mĂ«tejshme pĂ«r kontejner nĂ« kĂ«tĂ« artikull do tĂ« nĂ«nkuptojnĂ« Docker.

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

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

Së pari, ne krijojmë një skedar executiv JAR dhe, si pjesë e instrukcioneve të skedarit Docker, kopjojmë skedarin executiv JAR mbi imazhin bazë JRE pas aplikimit të cilësimeve të nevojshme.

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

Krijimi i skedarit Docker

Pastaj ne 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 kopjojmë skedarin tonë JAR, dhe pastaj hapim portin, 8080i cili do të dëgjojë për kërkesa.

Ndërtime aplikacionesh

E para që duhet bërë është krijimi i aplikacionit me Maven ose Gradle. Këtu ne përdorim Maven:

mvn clean package

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

Krijimi i imazhit të kontejnerit

Pastaj ne vendosim këtë skedar executiv JAR në imazhin Docker, duke ekzekutuar komandën docker buildnga katalogu rrënjor i projektit që përmban skedarin Docker, të krijuar më parë:

docker build  -t usersignup:v1 .

Ne mund ta shohim imazhin tonë në listë me komandën:

docker images 

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

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

Shikimi i shtresave brenda imazhit të kontejnerit

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

dive usersignup:v1

Ja një pjesë e rezultateve të ekzekutimit të komandës Dive: 

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

Siç e shohim, niveli aplikativ përbën një pjesë të konsiderueshme të madhësisë së imazhit. Ne duam ta zvogëlojmë madhësinë e këtij niveli në seksionet e ardhshme si pjesë e optimizimit tonë.

Krijimi i imazhit të kontejnerit me Buildpack

Paketa ndërtuese (Buildpacks) është një term i zakonshëm i përdorur nga oferta të ndryshme të "Platformës si Shërbim" (PAAS) për të ndërtuar një imazh kontejneri nga kodi burimor. Ai u lançua nga Heroku në vitin 2011 dhe që atëherë është pranuar nga Cloud Foundry, Google App Engine, Gitlab, Knative dhe disa të tjera.

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

Përfitimi i paketave të ndërtimit në re

Një nga përfitimet kryesore të përdorimit të Buildpack për të krijuar imazhe është se me ndryshimet e konfigurimit të imazhit mund të menaxhohen në mënyrë qendrore (builder) dhe të shpërndahen në të gjitha aplikacionet që përdorin builder.

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

Përdorimi i plugin-it Spring Boot

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

Mund të konfigurojmë emrin e imazhit që nevojitet për t'u dërguar në regjistrin Docker, duke treguar emrin në image tag:

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

Të përdorim Maven për të kryer build-imageqëllimin për ndërtimin e aplikacionit dhe krijimin e imazhit të kontejnerit. Tani nuk po përdorim asnjë skedar Docker.

mvn spring-boot:build-image

Rezultati do të jetë diçka e tillë:

[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:build-image (default-cli) @ usersignup ---
[INFO] Ndërtimi i imazhit 'docker.io/pratikdas/usersignup:v1'
[INFO] 
[INFO]  > Duke tërhequr imazhin ndërtues 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%
.
.
.. [krijuesi]     Shtimi i etiketës 'org.springframework.boot.version'
.. [krijuesi]     *** Imazhet (c311fe74ec73):
.. [krijuesi]           docker.io/pratikdas/usersignup:v1
[INFO] 
[INFO] Imazhi 'docker.io/pratikdas/usersignup:v1' u ndërtua me sukses

Nga të dhënat e daljes, shohim se paketo Cloud-Native buildpackpërdoret për të krijuar një imazh OCI të funksionshëm. Ashtu si më parë, mund të shohim imazhin e specifikuar si imazh Docker duke ekzekutuar komandën:

docker images 

Përfundimi:

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

Krijimi i imazhit të kontejnerit me Jib

Jib është një plugin për ndërtimin e imazheve nga Google që ofron një metodë alternative për ndërtimin e imazhit të kontejnerit nga kodin burimor.

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

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

Pastaj, ne ekzekutojmë plugin-in Jib duke përdorur komandën Maven për të ndërtuar aplikacionin dhe krijuar imazhin e kontejnerit. Ashtu si më parë, këtu nuk po përdorim asnjë skedar Docker:

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

Pas ekzekutimit të komandës Maven të specifikuar më lart, ne marrim daljen e mëposhtme:

[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 tregojnë se imazhi i kontejnerit është ndërtuar dhe vendosur në registrin.

Motivimet dhe metodat për krijimin e imazheve të optimizuara

Kemi dy arsye kryesore për optimizim:

  • Performanca: nĂ« sistemin e orkestrimit tĂ« kontejnerĂ«ve, imazhi i kontejnerit tĂ«rhiqet nga registri i imazheve nĂ« hostin ku Ă«shtĂ« aktivizuar mekanizmi i kontejnerit. Ky proces quhet planifikim. TĂ«rheqja e imazheve me madhĂ«si tĂ« madhe nga registra çon nĂ« njĂ« kohĂ« tĂ« gjatĂ« planifikimi nĂ« sistemet e orkestrimit tĂ« kontejnerĂ«ve dhe njĂ« kohĂ« tĂ« gjatĂ« ndĂ«rtimi nĂ« kanalet 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Ă« e grumbullit, secila pĂ«rfaqĂ«son njĂ« udhĂ«zim nĂ« Dockerfile tonĂ«. Çdo shtresĂ« pĂ«rfaqĂ«son njĂ« dytĂ« ndryshimesh nga shtresa poshtĂ«. Kur tĂ«rheqim imazhin Docker nga registri, ai tĂ«rhiqet nĂ« shtresa dhe ruhet nĂ« host.

Spring Boot përdor "JAR i trashë" si formatin e paketimit të paracaktuar. Kur shikojmë një JAR të trashë, ne shohim se aplikacioni përbën një pjesë shumë të vogël të gjithë JAR-it. Kjo pjesë është ajo që ndryshon më shpesh. Pjesa tjetër përbëhet nga varësitë e Spring Framework. Formula e optimizimit është përqendruar rreth izolimit të aplikacionit në një nivel të veçantë nga varësitë e Spring Framework.

Shtresa e varësive që formon shumicën e JAR-it të trashë ngarkohet vetëm një herë dhe ruhet në sistemin host.

Vetëm shtresa e hollë e aplikacionit tërheq gjatë përditësimeve të aplikacionit dhe planifikimit të kontejnerëve,

siç është ilustruar në këtë diagram: Në seksionet në vazhdim, ne do të shqyrtojmë se si të krijojmë këto imazhe të optimizuara për aplikacionin Spring Boot.

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 Buildpack

Spring Boot 2.3 mbështet shumë-shtresimin duke tërhequr pjesët e JAR-it të trashë në shtresa të veçanta. Funksionaliteti i shtresës është çaktivizuar përcaktimisht dhe duhet të aktivizohet qartë me ndihmën e plugin-it Spring Boot Maven:

Spring Boot 2.3 mbĂ«shtet shumĂ«-niveloßshmĂ«rinĂ« duke nxjerrĂ« pjesĂ«t e njĂ« skedari tĂ« trashĂ« JAR nĂ« shtresa tĂ« veçanta. Funksioni i mbivendosjes Ă«shtĂ« i çaktivizuar nga e drejta dhe duhet tĂ« aktivizohet qartĂ« pĂ«rmes plugin-it Spring Boot Maven:

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

Ne do ta përdorim këtë konfigurim për të krijuar imazhin tonë të kontejnerit së pari me ndihmën e Buildpack, 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ë shtresat në imazhin rezultues, do të shohim se shtresa e aplikacionit (e rrethuar me të kuqe) është shumë më e vogël në rangun e kilobajtëve në krahasim me atë që kemi marrë 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 që të përdorim pluginin Maven ose Gradle, ne gjithashtu mund të krijojmë një imazh Docker me shumë shtresa JAR me skedarin Docker.

Kur përdorim Docker, na nevojiten dy hapa shtesë për të nxjerrë dhe kopjuar shtresat në imazhin përfundimtar.

Përmbajtja e JAR-it të marrë pas ndërtimit me ndihmën e Maven me funksionin e aktivizuar për shtresa 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ë i quajtur spring-boot-jarmode-layertoolsdhe layersfle.idxskedari. Ky JAR shtesë ofron mundësinë e përpunimit me shumë shtresa, siç është përshkruar në seksionin e ardhshë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ë sistemike -Djarmode=layertoolspër të ekzekutuar spring-boot-jarmode-layertoolsJAR-in përveç aplikacionit:

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

Ekzekutimi i kësaj komande 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     Listo shtresat nga jar-i që mund të nxirren
  extract  Nxjerr shtresat nga jar-i për krijimin e imazhit
  help     Ndihmë për çdo komandë

Dalja tregon komandat list, extractdhe helpme helptë jetë në default. Le të ekzekutojmë komandën me listopsionin:

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

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

Shtresat për default:

Emri i shtresës

Përmbajtja

dependencies

çdo varësi, përfshirë versionin që nuk ka SNAPSHOT

spring-boot-loader

Klasa e ngarkuesit të JAR-it

snapshot-dependencies

çdo varësi, përfshirë versionin që ka SNAPSHOT

application

klasat e aplikacioneve dhe burimet

Shtresat janë përcaktuar në layers.idxskedën në rendin në të cilin duhet të shtohen në imazhin Docker. Këto shtresa ruhen në host pas ekstraktimit të parë, pasi ato nuk ndryshojnë. Vetëm niveli i azhurnuar i aplikacionit ngarkohet në host, që ndodh më shpejt për shkak të madhësisë më të vogël. .

Ndërtimi i imazhit me varësitë e nxjerra në shtresa të ndara.

Ne do të ndërtojmë imazhin përfundimtar në dy faza, duke përdorur një metodë të quajtur ndërtim shumëfazor. . Në fazën e parë do të nxjerrim varësitë, dhe në fazën e dytë do t'i kopjojmë varësitë e nxjerra në imazhin përfundimtar.

Le të modifikojmë skedën tonë Docker për ndërtim shumëfazor:

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

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

Të ndërtojmë imazhin Docker duke përdorur komandën:

docker build -f Dockerfile2 -t usersignup:v1 .

Pas ekzekutimit të kësaj komande, ne marrim këtë rezultat:

Dërgimi i kontekstit të ndërtimit në daemon-in Docker  20.41MB
Hapi 1/12 : NGA adoptopenjdk:14-jre-hotspot si ndërtues
14-jre-hotspot: Po tërheq nga library/adoptopenjdk
.
.
Të ndërtuar me sukses a9ebf6970841
Etiketuar me sukses userssignup:v1

Ne shohim se imazhi Docker krijohet me një identifikues imazhi dhe më pas etiketohët.

Më në fund, ne ekzekutojmë komandën Dive, ashtu si më parë, për të kontrolluar shtresat brenda imazhit të gjeneruar Docker. Ne mund të specifikojmë identifikuesin e imazhit ose etiketën si input për komandën Dive:

dive userssignup:v1

Siç shihet nga rezultati, niveli që përmban aplikacionin tani zë vetëm 11 KB, ndërsa varësitë ruhen në shtresa të ndara. 

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

Ekstraktimi i varësive të brendshme në shtresa të ndara.

Ne mund ta zvogëlojmë më tej madhësinë e nivelit të aplikacionit duke nxjerrë çdo një 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ë skedë të tillë të quajtur 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ë skedë layers.idxne kemi shtuar një varësi të personalizuar me emrin, io.myorgqë përmban varësi të organizatës, të marrë nga një depo të përbashkët.

Përfundimi

Në këtë artikull ne shqyrtuam përdorimin e Buildpacks Cloud-Native për krijimin e një imazhi konteineri direkt nga kodi burimor. Kjo është një alternativë ndaj përdorimit të Docker për krijimin e imazhit të konteinerit në mënyrë të zakonshme: fillimisht krijohet një skedë e trashë ekzekutimi JAR, dhe pastaj ajo paketizohet në një imazh konteneri duke përshkruar udhëzimet në skedarin Docker.

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

Mund të gjeni të gjitha kodet burimore të përdorura në artikull në Github .

DHTMLP Komandat

Ja një përmbledhje e shkurtër e komandave që ne përdorëm në këtë artikull për një njohuri të shpejtë.

Pastrimi i kontekstit:

docker system prune -a

Krijimi i imazhit të konteinerit me skedarin Docker:

docker build -f  -t  .

Krijimi i imazhit të konteinerit nga kodi burimor (pa Dockerfile):

mvn spring-boot:build-image

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

java -Djarmode=layertools -jar application.jar list

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

 java -Djarmode=layertools -jar application.jar extract

Shikoni listën e imazheve të konteinerëve

docker images

Shikoni nivelin brenda imazhit të konteinerit (sigurohuni që mjeti për depërtim është i instaluar):

dive

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster