Bou geoptimaliseerde Docker-beelde vir 'n Spring Boot-toepassing

Houers het die voorkeurmiddel geword om 'n toepassing met al sy sagteware en bedryfstelselafhanklikhede te verpak en dit dan aan verskillende omgewings te lewer.

Hierdie artikel dek verskillende maniere om 'n Spring Boot-toepassing te hou:

  • die bou van 'n docker-beeld met behulp van 'n dockerfile,
  • die bou van 'n OCI-beeld vanaf bron met behulp van Cloud-Native Buildpack,
  • en beeldoptimalisering tydens looptyd deur JAR-onderdele in verskillende vlakke te skei met behulp van gelaagde gereedskap.

 Kode voorbeeld

Hierdie artikel word vergesel van 'n werkende kode voorbeeld op GitHub .

Houer terminologie

Ons begin met die houerterminologie wat deur die hele artikel gebruik word:

  • Houerbeeld: 'n lΓͺer van 'n spesifieke formaat. Ons omskep ons toepassing in 'n houerbeeld deur die bounutsding uit te voer.
  • Houer: 'n Uitvoerbare instansie van die houerbeeld.
  • Houer enjin: Die daemon proses wat verantwoordelik is vir die bestuur van die houer.
  • Houergasheer: Die gasheermasjien waarop die houerenjin loop.
  • Houerregister: Die algemene ligging wat gebruik word om die houerprent te publiseer en te versprei.
  • OCI standaardOpen Container Initiative (OCI) is 'n liggewig, oopbronbestuursraamwerk wat deur die Linux-stigting gevorm is. Die OCI-beeldspesifikasie definieer industriestandaarde vir houerbeeldformate en die looptyd om te verseker dat alle houerenjins houerprente kan laat loop wat deur enige bouinstrument geskep is.

Om 'n aansoek te hou, draai ons ons aansoek in 'n houerbeeld en publiseer daardie beeld na die publieke register. Die houerlooptyd haal hierdie prent uit die register, pak dit uit en laat die toepassing daarin loop.

Weergawe 2.3 van Spring Boot bied plugins vir die bou van OCI-beelde.

Docker is die mees gebruikte houerimplementering, en ons gebruik Docker in ons voorbeelde, dus sal alle daaropvolgende houerverwysings in hierdie artikel na Docker verwys.

Bou 'n houerbeeld op die tradisionele manier

Die bou van Docker-beelde vir Spring Boot-toepassings is baie maklik deur 'n paar instruksies by jou Dockerfile by te voeg.

Ons skep eers 'n uitvoerbare JAR en, as deel van die Dockerfile-instruksies, kopieer die uitvoerbare JAR bo-op die basis JRE-beeld nadat ons die nodige aanpassings toegepas het.

Kom ons skep ons Lente-toepassing op Spring Inisialisr met afhanklikhede weblombokΠΈ actuator. Ons voeg ook 'n rusbeheerder by om 'n API mee te voorsien GETmetode.

Skep 'n Dockerfile

Ons plaas dan hierdie toepassing in 'n houer deur by te voeg Dockerfile:

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

Ons Dockerfile bevat 'n basisbeeld, van adoptopenjdk, boonop kopieer ons ons JAR-lΓͺer en maak dan die poort oop, 8080wat vir versoeke sal luister.

Toepassing samestelling

Eerstens moet jy 'n toepassing skep met Maven of Gradle. Hier gebruik ons ​​Maven:

mvn clean package

Dit skep 'n uitvoerbare JAR-lΓͺer vir die toepassing. Ons moet hierdie uitvoerbare JAR omskakel na 'n Docker-beeld om op die Docker-enjin te loop.

Skep 'n houerbeeld

Ons plaas dan hierdie JAR-uitvoerbare in die Docker-beeld deur die opdrag uit te voer docker buildvanaf die wortelgids van die projek wat die Dockerfile bevat wat vroeΓ«r geskep is:

docker build  -t usersignup:v1 .

Ons kan ons beeld in die lys sien met die opdrag:

docker images 

Die uitvoer van die bogenoemde opdrag sluit ons beeld in usersignupsaam met die basisbeeld, adoptopenjdkgespesifiseer in ons Dockerfile.

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

Bekyk lae binne 'n houerprent

Kom ons kyk na die stapel lae binne die prent. Ons sal gebruik hulpmiddel  duik, om hierdie lae te sien:

dive usersignup:v1

Hier is 'n deel van die uitvoer van die duik-opdrag: 

Bou geoptimaliseerde Docker-beelde vir 'n Spring Boot-toepassing

Soos ons kan sien, maak die toepassingslaag 'n beduidende deel van die beeldgrootte uit. Ons wil die grootte van hierdie laag in die volgende afdelings verminder as deel van ons optimalisering.

Bou 'n houerbeeld met Buildpack

Monteer pakkette (Boupakke) is 'n generiese term wat deur verskeie Platform as a Service (PAAS) aanbiedinge gebruik word om 'n houerbeeld van bronkode te skep. Dit is in 2011 deur Heroku bekendgestel en is sedertdien deur Cloud Foundry, Google App Engine, Gitlab, Knative en 'n paar ander aangeneem.

Bou geoptimaliseerde Docker-beelde vir 'n Spring Boot-toepassing

Voordeel van wolkboupakkette

Een van die belangrikste voordele van die gebruik van Buildpack om beelde te bou, is dat beeldkonfigurasieveranderinge kan sentraal bestuur word (bouer) en versprei word na alle toepassings wat die bouer gebruik.

Die boupakkette was nou gekoppel aan die platform. Cloud-Native Buildpacks bied standaardisering oor platforms heen deur die OCI-beeldformaat te ondersteun, wat verseker dat die prent deur die Docker-enjin bestuur kan word.

Gebruik die Spring Boot Plugin

Die Spring Boot-inprop bou OCI-beelde vanaf bron deur Buildpack te gebruik. Beelde word geskep met behulp van bootBuildImagetake (Gradle) of spring-boot:build-imageteiken (Maven) en plaaslike Docker-installasie.

Ons kan die naam van die prent wat ons na die Docker-register moet stoot aanpas deur die naam in te spesifiseer 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>

Kom ons gebruik Maven om uit te voer build-imagedoelwitte vir die skep van 'n toepassing en die skep van 'n houerbeeld. Ons gebruik tans geen Dockerfiles nie.

mvn spring-boot:build-image

Die resultaat sal iets soos volg wees:

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

Uit die uitset sien ons dit paketo Cloud-Native buildpackgebruik om 'n werkende OCI-beeld te skep. Soos voorheen, kan ons die prent sien as 'n Docker-beeld deur die opdrag uit te voer:

docker images 

Gevolgtrekking:

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

Skep 'n houerbeeld met Jib

Jib is 'n beeld-outeur-inprop van Google wat 'n alternatiewe metode bied om 'n houerprent vanaf die bron te skep.

Konfigureer jib-maven-pluginin pom.xml:

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

Vervolgens voer ons die Jib-inprop uit deur die Maven-opdrag te gebruik om die toepassing te bou en die houerbeeld te skep. Soos voorheen gebruik ons ​​geen Dockerfiles hier nie:

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

Nadat ons die bogenoemde Maven-opdrag uitgevoer het, kry ons die volgende uitvoer:

[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

Die uitvoer wys dat die houerbeeld geskep en in die register geplaas is.

Motiverings en metodes vir die skep van geoptimaliseerde beelde

Ons het twee hoofredes vir optimalisering:

  • produktiwiteit: In 'n houer-orkestrasiestelsel word 'n houerbeeld van die beeldregister na die gasheer getrek wat die houer-enjin bestuur. Hierdie proses word beplanning genoem. Om groot beelde uit die register te trek, lei tot lang skeduleringtye in houerorkestrasiestelsels en lang boutye in CI-pypleidings.
  • sekuriteit: groot beelde het ook 'n groot area vir kwesbaarhede.

'n Docker-prent bestaan ​​uit 'n stapel lae, wat elkeen 'n stelling in ons Dockerfile verteenwoordig. Elke laag verteenwoordig die delta van veranderinge in die onderliggende laag. Wanneer ons 'n Docker-prent uit die register trek, word dit in lae getrek en op die gasheer gekas.

Spring Boot gebruik "vet JAR" in as die verstek verpakking formaat. Wanneer ons na 'n vet JAR kyk, sien ons dat die toepassing 'n baie klein deel van die hele JAR is. Dit is die deel wat die meeste verander. Die res bestaan ​​uit Lenteraamwerk-afhanklikhede.

Die optimaliseringsformule is gesentreer om die toepassing op 'n aparte vlak van Spring Framework-afhanklikhede te isoleer.

Die afhanklikheidslaag wat die grootste deel van die dik JAR-lΓͺer vorm, word slegs een keer afgelaai en op die gasheerstelsel in die kas geberg.

Slegs 'n dun laag van die toepassing word getrek tydens toepassingopdaterings en houerskedulering, soos in hierdie diagram getoon:

Bou geoptimaliseerde Docker-beelde vir 'n Spring Boot-toepassing

In die volgende afdelings sal ons kyk hoe om hierdie geoptimaliseerde beelde vir 'n Spring Boot-toepassing te skep.

Bou 'n geoptimaliseerde houerbeeld vir 'n Spring Boot-toepassing met Buildpack

Spring Boot 2.3 ondersteun lae deur dele van 'n dik JAR-lΓͺer in aparte lae te onttrek. Die lae-kenmerk is by verstek gedeaktiveer en moet uitdruklik geaktiveer word met die Spring Boot Maven-inprop:

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

Ons sal hierdie konfigurasie gebruik om ons houerbeeld eers met Buildpack en dan met Docker in die volgende afdelings te bou.

Kom ons hardloop build-imageMaven teiken om 'n houerbeeld te skep:

mvn spring-boot:build-image

As ons Dive hardloop om die lae in die resulterende prent te sien, kan ons sien dat die toepassingslaag (in rooi omsirkel) baie kleiner is in die kilogreepreeks in vergelyking met wat ons gekry het met die dik JAR-formaat:

Bou geoptimaliseerde Docker-beelde vir 'n Spring Boot-toepassing

Bou 'n geoptimaliseerde houerbeeld vir 'n Spring Boot-toepassing met Docker

In plaas daarvan om 'n Maven- of Gradle-inprop te gebruik, kan ons ook 'n gelaagde Docker JAR-beeld met 'n Docker-lΓͺer skep.

Wanneer ons Docker gebruik, moet ons twee ekstra stappe neem om die lae te onttrek en dit na die finale prent te kopieer.

Die inhoud van die resulterende JAR nadat jy gebou het met Maven met gelaagdheid geaktiveer, sal soos volg lyk:

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

Die uitset wys 'n bykomende JAR genaamd spring-boot-jarmode-layertoolsΠΈ layersfle.idxlΓͺer. Hierdie bykomende JAR-lΓͺer bied lae-vermoΓ«ns, soos beskryf in die volgende afdeling.

Onttrek afhanklikhede op afsonderlike lae

Om lae uit ons gelaagde JAR te bekyk en te onttrek, gebruik ons ​​die stelsel-eienskap -Djarmode=layertoolsvir begin spring-boot-jarmode-layertoolsJAR in plaas van toepassing:

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

Deur hierdie opdrag uit te voer, lewer 'n afvoer wat die beskikbare opdragopsies bevat:

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

Die uitset wys die opdragte listextractΠΈ helpс helpdie verstek wees. Kom ons voer die opdrag uit met listopsie:

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

Ons sien 'n lys van afhanklikhede wat as lae bygevoeg kan word.

Lae by verstek:

Laag naam

inhoud

dependencies

enige afhanklikheid waarvan die weergawe nie SNAPSHOT bevat nie

spring-boot-loader

JAR-laaierklasse

snapshot-dependencies

enige afhanklikheid waarvan die weergawe SNAPSHOT bevat

application

toepassingsklasse en hulpbronne

Lae word gedefinieer in layers.idxlΓͺer in die volgorde waarin dit by die Docker-prent gevoeg moet word. Hierdie lae word op die gasheer gekas na die eerste haal omdat hulle nie verander nie. Slegs die opgedateerde toepassingslaag word na die gasheer afgelaai, wat vinniger is as gevolg van die verminderde grootte .

Bou 'n beeld met afhanklikhede wat in aparte lae onttrek word

Ons sal die finale beeld in twee stappe bou deur 'n metode genaamd multi-stadium samestelling . In die eerste stap sal ons die afhanklikhede onttrek en in die tweede stap sal ons die onttrekde afhanklikhede na die finale kopieer.

Kom ons verander ons Dockerfile vir 'n multi-stadium bou:

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

Ons stoor hierdie konfigurasie in 'n aparte lΓͺer - Dockerfile2.

Ons bou die Docker-beeld met behulp van die opdrag:

docker build -f Dockerfile2 -t usersignup:v1 .

Nadat ons hierdie opdrag uitgevoer het, kry ons die volgende uitvoer:

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

Ons kan sien dat die Docker-prent met 'n beeld-ID geskep word en dan gemerk is.

Laastens voer ons die Dive-opdrag soos voorheen uit om die lae binne die gegenereerde Docker-beeld na te gaan. Ons kan 'n beeld-ID of merker verskaf as insette vir die duik-opdrag:

dive userssignup:v1

Soos u uit die afvoer kan sien, is die laag wat die toepassing bevat nou slegs 11 KB en die afhanklikhede word in aparte lae gekas. 

Bou geoptimaliseerde Docker-beelde vir 'n Spring Boot-toepassing

Onttrek interne afhanklikhede op afsonderlike lae

Ons kan die grootte van die toepassingslaag verder verminder deur enige van ons pasgemaakte afhanklikhede in 'n aparte laag te onttrek in plaas daarvan om dit saam met die toepassing te verpak deur dit te verklaar in ymlsoortgelyke lΓͺer genoem 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/"

In hierdie lΓͺer layers.idxons het 'n pasgemaakte afhanklikheid bygevoeg genaamd, io.myorgwat organisasieafhanklikhede bevat wat uit die gedeelde bewaarplek verkry is.

Output

In hierdie artikel het ons gekyk na die gebruik van Cloud-Native Buildpacks om 'n houerprent direk vanaf die bron te bou. Dit is 'n alternatief vir die gebruik van Docker om 'n houerbeeld op die gewone manier te skep: eerstens word 'n dik uitvoerbare JAR-lΓͺer geskep en dan in 'n houerprent verpak deur die instruksies in die Dockerfile te spesifiseer.

Ons het ook gekyk na die optimalisering van ons houer deur 'n lae-eienskap in te sluit wat afhanklikhede in afsonderlike lae onttrek wat op die gasheer gekas is en 'n dun toepassingslaag word gelaai tydens skedulering in die houer se uitvoering-enjins.

Jy kan al die bronkode wat in die artikel gebruik word vind by GitHub .

Bevelverwysing

Hier is 'n opsomming van die opdragte wat ons in hierdie artikel gebruik het vir 'n vinnige verwysing.

Konteks skoonmaak:

docker system prune -a

Bou 'n houerbeeld met 'n Dockerfile:

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

Bou houerbeeld vanaf bron (sonder Dockerfile):

mvn spring-boot:build-image

Bekyk afhanklikheidslae. Voordat u die toepassingsjare-lΓͺer bou, maak seker dat die lae-funksie in die spring-boot-maven-plugin geaktiveer is:

java -Djarmode=layertools -jar application.jar list

Onttrek afhanklikheidslae. Voordat u die toepassingsjare-lΓͺer bou, maak seker dat die lae-funksie in die spring-boot-maven-plugin geaktiveer is:

 java -Djarmode=layertools -jar application.jar extract

Bekyk 'n lys van houerbeelde

docker images

Kyk aan die linkerkant binne-in die houerprent (maak seker die duikinstrument is geΓ―nstalleer):

dive <image ID or image tag>

Bron: will.com