Optimoitujen Docker-kuvien luominen kevätkäynnistyssovellukseen

Konteista on tullut suosituin tapa pakata sovellus kaikkine ohjelmisto- ja käyttöjärjestelmäriippuvuuksineen ja toimittaa ne sitten eri ympäristöihin.

Tämä artikkeli kattaa erilaisia ​​tapoja säilyttää Spring Boot -sovellus:

  • Docker-kuvan rakentaminen docker-tiedoston avulla,
  • OCI-kuvan rakentaminen lähteestä Cloud-Native Buildpackin avulla,
  • ja kuvan optimointi ajon aikana erottamalla JAR-osat eri tasoille kerrostettujen työkalujen avulla.

 Esimerkki koodista

Tämän artikkelin mukana on toimiva koodiesimerkki GitHubissa .

Säilön terminologia

Aloitamme koko artikkelissa käytetyllä säilön terminologialla:

  • Säilön kuva: tietyn muotoinen tiedosto. Muunnamme sovelluksemme konttikuvaksi suorittamalla rakennustyökalun.
  • Astia: Säiliön kuvan suoritettava esiintymä.
  • Konttimoottori: Säilön suorittamisesta vastaava demoniprosessi.
  • Kontin isäntä: Isäntäkone, jolla konttimoottori on käynnissä.
  • Säilön rekisteri: Yleinen sijainti, jota käytetään säilön kuvan julkaisemiseen ja jakeluun.
  • OCI standardiAvoin kontti -aloite (OCI) on kevyt, avoimen lähdekoodin hallintakehys, jonka on muodostanut Linux Foundation. OCI Image Specification määrittelee alan standardit konttikuvamuodoille ja suoritusajalle varmistaakseen, että kaikki konttimoottorit voivat käyttää millä tahansa rakennustyökalulla luotuja säilökuvia.

Säilyttääksesi sovelluksen, käärimme sovelluksemme säilökuvaan ja julkaisemme kuvan julkisessa rekisterissä. Säilön suoritusaika noutaa tämän kuvan rekisteristä, purkaa sen ja suorittaa sovelluksen siinä.

Spring Bootin versio 2.3 sisältää laajennuksia OCI-kuvien rakentamiseen.

Satamatyöläinen on yleisimmin käytetty säilötoteutus, ja käytämme esimerkeissämme Dockeria, joten kaikki myöhemmät säilöviittaukset tässä artikkelissa viittaavat Dockeriin.

Konttikuvan rakentaminen perinteisellä tavalla

Docker-kuvien luominen Spring Boot -sovelluksiin on erittäin helppoa lisäämällä muutamia ohjeita Docker-tiedostoosi.

Luomme ensin suoritettavan JAR:n ja osana Dockerfile-ohjeita kopioimme suoritettavan JAR-tiedoston JRE-peruskuvan päälle, kun tarvittavat mukautukset on tehty.

Luodaan kevätsovelluksemme Kevät Initializr riippuvuuksien kanssa weblombokи actuator. Lisäämme myös lepoohjaimen API:n tarjoamiseksi GETmenetelmä.

Docker-tiedoston luominen

Asetamme tämän sovelluksen sitten säiliöön lisäämällä Dockerfile:

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

Docker-tiedostomme sisältää peruskuvan adoptopenjdk, jonka päälle kopioimme JAR-tiedostomme ja avaamme sitten portin, 8080joka kuuntelee pyyntöjä.

Sovelluksen kokoonpano

Ensin sinun on luotava sovellus Mavenin tai Gradlen avulla. Tässä käytämme Mavenia:

mvn clean package

Tämä luo sovellukselle suoritettavan JAR-tiedoston. Meidän on muutettava tämä suoritettava JAR Docker-kuvaksi, jotta sitä voidaan käyttää Docker-moottorissa.

Luo säilön kuva

Laitamme sitten tämän JAR-suoritettavan tiedoston Docker-kuvaan suorittamalla komennon docker buildaiemmin luodun Docker-tiedoston sisältävän projektin juurihakemistosta:

docker build  -t usersignup:v1 .

Näemme kuvamme luettelossa komennolla:

docker images 

Yllä olevan komennon tulos sisältää kuvamme usersignupperuskuvan kanssa, adoptopenjdkmääritelty Docker-tiedostossamme.

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

Näytä tasot säilökuvan sisällä

Katsotaanpa kuvan sisällä olevaa kerrosten pinoa. Me käytämme työkalu  sukeltaa, nähdäksesi nämä tasot:

dive usersignup:v1

Tässä on osa Dive-komennon tulosta: 

Optimoitujen Docker-kuvien luominen kevätkäynnistyssovellukseen

Kuten näemme, sovelluskerros muodostaa merkittävän osan kuvan koosta. Haluamme pienentää tämän kerroksen kokoa seuraavissa osioissa osana optimointiamme.

Säilön kuvan luominen Buildpackin avulla

Kokoonpanopaketit (Rakennuspaketit) on yleinen termi, jota useat PAAS (Platform as a Service) -tarjoukset käyttävät säilön kuvan luomiseen lähdekoodista. Heroku julkaisi sen vuonna 2011, ja sen jälkeen Cloud Foundry, Google App Engine, Gitlab, Knative ja muutamat muut ovat ottaneet sen käyttöön.

Optimoitujen Docker-kuvien luominen kevätkäynnistyssovellukseen

Cloud Build -pakettien etu

Yksi Buildpackin käytön kuvien luomiseen tärkeimmistä eduista on se kuvan kokoonpanomuutoksia voidaan hallita keskitetysti (builder) ja levittää kaikkiin sovelluksiin, jotka käyttävät rakennustyökalua.

Rakennuspaketit olivat tiiviisti sidoksissa alustaan. Cloud-Native Buildpackit tarjoavat standardoinnin eri alustoilla tukemalla OCI-kuvamuotoa, mikä varmistaa, että Docker-moottori voi ajaa kuvaa.

Spring Boot Pluginin käyttäminen

Spring Boot -laajennus rakentaa OCI-kuvat lähteestä Buildpackin avulla. Kuvat luodaan käyttämällä bootBuildImagetehtävät (Gradle) tai spring-boot:build-imagekohde (Maven) ja paikallinen Docker-asennus.

Voimme mukauttaa Docker-rekisteriin siirrettävän kuvan nimen määrittämällä nimen 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>

Suoritukseen käytetään Mavenia build-imagetavoitteet sovelluksen luomiseen ja säiliökuvan luomiseen. Emme tällä hetkellä käytä Docker-tiedostoja.

mvn spring-boot:build-image

Tulos tulee olemaan jotain tämän kaltaista:

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

Tuotoksesta näemme sen paketo Cloud-Native buildpackkäytetään toimivan OCI-kuvan luomiseen. Kuten ennenkin, voimme nähdä kuvan Docker-kuvana suorittamalla komennon:

docker images 

Johtopäätös:

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

Säilön kuvan luominen Jibin avulla

Jib on Googlen kuvien luontilaajennus, joka tarjoaa vaihtoehtoisen tavan luoda säilökuva lähteestä.

Määritetään jib-maven-pluginpom.xml:ssä:

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

Seuraavaksi suoritamme Jib-laajennuksen Maven-komennolla sovelluksen rakentamiseksi ja säiliökuvan luomiseksi. Kuten aiemmin, emme käytä Docker-tiedostoja täällä:

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

Yllä olevan Maven-komennon suorittamisen jälkeen saamme seuraavan lähdön:

[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

Tulos näyttää, että säilön kuva on luotu ja sijoitettu rekisteriin.

Motiiveja ja menetelmiä optimoitujen kuvien luomiseen

Meillä on kaksi tärkeintä syytä optimoida:

  • Suorituskyky: Säilöorkesterijärjestelmässä säilön näköistiedosto vedetään kuvarekisteristä konttimoottoria käyttävään isäntään. Tätä prosessia kutsutaan suunnitteluksi. Suurten kuvien vetäminen rekisteristä johtaa pitkiin ajoitusaikoihin kontin orkestrointijärjestelmissä ja pitkiä rakennusaikoja CI-putkissa.
  • Безопасность: suurissa kuvissa on myös suuri alue haavoittuvuuksille.

Docker-kuva koostuu pinosta tasoja, joista jokainen edustaa Docker-tiedostossamme olevaa lausuntoa. Jokainen kerros edustaa alla olevan kerroksen muutosten deltaa. Kun vedämme Docker-kuvan rekisteristä, se vedetään kerroksittain ja tallennetaan isännän välimuistiin.

Spring Boot käyttö "Fat JAR" sisään oletuspakkausmuotona. Kun katsomme rasvaa JARia, näemme, että sovellus on hyvin pieni osa koko JAR:ia. Tämä on se osa, joka muuttuu eniten. Loput koostuvat Spring Framework -riippuvuuksista.

Optimointikaava keskittyy eristämään sovellus erillisellä tasolla Spring Framework -riippuvuuksista.

Riippuvuuskerros, joka muodostaa suurimman osan paksusta JAR-tiedostosta, ladataan vain kerran ja tallennetaan isäntäjärjestelmän välimuistiin.

Vain ohut kerros sovellusta vedetään sovelluspäivitysten ja säilön ajoituksen aikana, kuten tässä kaaviossa näkyy:

Optimoitujen Docker-kuvien luominen kevätkäynnistyssovellukseen

Seuraavissa osissa tarkastellaan näiden optimoitujen kuvien luomista Spring Boot -sovellukselle.

Optimoidun säiliökuvan luominen Spring Boot -sovellukselle Buildpackilla

Spring Boot 2.3 tukee kerrostamista purkamalla paksun JAR-tiedoston osia erillisiin kerroksiin. Tasoitusominaisuus on oletuksena pois käytöstä, ja se on otettava erikseen käyttöön Spring Boot Maven -laajennuksella:

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

Käytämme tätä kokoonpanoa konttikuvan rakentamiseen ensin Buildpackin ja sitten Dockerin avulla seuraavissa osissa.

Juostaan build-imageMaven kohde säilökuvan luomiseen:

mvn spring-boot:build-image

Jos suoritamme Diven nähdäksemme tuloksena olevan kuvan kerrokset, voimme nähdä, että sovelluskerros (ympyröity punaisella) on paljon pienempi kilotavualueella verrattuna siihen, mitä saimme käyttämällä paksua JAR-muotoa:

Optimoitujen Docker-kuvien luominen kevätkäynnistyssovellukseen

Optimoidun säiliökuvan luominen Spring Boot -sovellukselle Dockerin avulla

Maven- tai Gradle-laajennuksen sijaan voimme myös luoda kerrosteisen Docker JAR -kuvan Docker-tiedostolla.

Kun käytämme Dockeria, meidän on suoritettava kaksi ylimääräistä vaihetta tasojen purkamiseksi ja kopioimiseksi lopulliseen kuvaan.

Tuloksena olevan JAR:n sisältö Mavenin kanssa rakentamisen jälkeen, kun kerros on käytössä, näyttää tältä:

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

Lähtö näyttää ylimääräisen JAR-nimisen spring-boot-jarmode-layertoolsи layersfle.idxtiedosto. Tämä ylimääräinen JAR-tiedosto tarjoaa tasoitusominaisuudet, kuten seuraavassa osassa kuvataan.

Poimi riippuvuudet erillisistä tasoista

Käytämme järjestelmäominaisuutta tarkastellaksemme ja poimiaksemme tasoja kerrostetusta JAR:sta -Djarmode=layertoolsalkuun spring-boot-jarmode-layertoolsJAR sovelluksen sijaan:

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

Tämän komennon suorittaminen tuottaa tulosteen, joka sisältää käytettävissä olevat komentovaihtoehdot:

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

Tulos näyttää komennot listextractи helpс helpolla oletusarvo. Suoritetaan komento kanssa listvaihtoehto:

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

Näemme luettelon riippuvuuksista, jotka voidaan lisätä tasoksi.

Tasot oletuksena:

Tason nimi

Pitoisuus

dependencies

kaikki riippuvuudet, joiden versio ei sisällä SNAPSHOTia

spring-boot-loader

JAR Loader -luokat

snapshot-dependencies

mikä tahansa riippuvuus, jonka versio sisältää SNAPSHOT

application

sovellusluokat ja resurssit

Tasot on määritelty layers.idxtiedostot siinä järjestyksessä, jossa ne tulee lisätä Docker-kuvaan. Nämä tasot tallennetaan palvelimen välimuistiin ensimmäisen haun jälkeen, koska ne eivät muutu. Vain päivitetty sovelluskerros ladataan isäntään, mikä on nopeampaa pienennetyn koon vuoksi .

Kuvan rakentaminen riippuvuuksilla, jotka on purettu erillisiin kerroksiin

Rakennamme lopullisen kuvan kahdessa vaiheessa menetelmällä nimeltä monivaiheinen kokoonpano . Ensimmäisessä vaiheessa puramme riippuvuudet ja toisessa vaiheessa kopioimme puretut riippuvuudet lopulliseen .

Muokkaamme Docker-tiedostoamme monivaiheista koontiversiota varten:

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

Tallennamme tämän kokoonpanon erilliseen tiedostoon - Dockerfile2.

Rakennamme Docker-kuvan komennolla:

docker build -f Dockerfile2 -t usersignup:v1 .

Tämän komennon suorittamisen jälkeen saamme seuraavan tulosteen:

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

Näemme, että Docker-kuva luodaan kuvatunnuksella ja merkitään sitten tunnisteella.

Lopuksi suoritamme Sukellus-komennon kuten ennenkin tarkistaaksemme luodun Docker-kuvan sisällä olevat tasot. Voimme antaa kuvatunnuksen tai tunnisteen syötteenä Dive-komentoon:

dive userssignup:v1

Kuten tulosteesta näkyy, sovelluksen sisältävä kerros on nyt vain 11 kt ja riippuvuudet on välimuistissa eri kerroksissa. 

Optimoitujen Docker-kuvien luominen kevätkäynnistyssovellukseen

Pura sisäiset riippuvuudet erillisistä tasoista

Voimme pienentää sovelluskerroksen kokoa entisestään purkamalla mitkä tahansa mukautetut riippuvuutemme erilliseen kerrokseen sen sijaan, että pakkaamme ne sovelluksen mukana ilmoittamalla ne ymlsamanlainen tiedosto nimeltä 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/"

Tässä tiedostossa layers.idxolemme lisänneet mukautetun riippuvuuden nimeltä io.myorgsisältää organisaation riippuvuuksia, jotka on haettu jaetusta arkistosta.

johtopäätös

Tässä artikkelissa tarkastelimme Cloud-Native Buildpacks -pakettien käyttöä säilön kuvan rakentamiseen suoraan lähteestä. Tämä on vaihtoehto Dockerin käyttämiselle säilökuvan luomiseen tavalliseen tapaan: ensin luodaan paksu suoritettava JAR-tiedosto ja pakataan sitten säilökuvaksi määrittämällä Docker-tiedoston ohjeet.

Tarkastelimme myös säilömme optimointia sisällyttämällä siihen kerrosominaisuuden, joka erottaa riippuvuudet erillisiksi kerroksiksi, jotka tallennetaan välimuistiin isännässä, ja ohut sovelluskerros ladataan ajoitusaikana säilön suoritusmoottoreihin.

Löydät kaiken artikkelissa käytetyn lähdekoodin osoitteessa Github .

Komentoviittaus

Tässä on yhteenveto tässä artikkelissa käyttämistämme komennoista nopeaa ohjetta varten.

Kontekstin tyhjennys:

docker system prune -a

Säilön kuvan luominen Docker-tiedostolla:

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

Rakenna säilön kuva lähteestä (ilman Dockerfileä):

mvn spring-boot:build-image

Näytä riippuvuustasot. Ennen kuin rakennat sovellusjar-tiedoston, varmista, että kerrostoiminto on käytössä spring-boot-maven-pluginissa:

java -Djarmode=layertools -jar application.jar list

Pura riippuvuustasot. Ennen kuin rakennat sovellusjar-tiedoston, varmista, että kerrostoiminto on käytössä spring-boot-maven-pluginissa:

 java -Djarmode=layertools -jar application.jar extract

Säilön kuvien luettelon tarkasteleminen

docker images

Näkymä vasemmalla säiliökuvan sisällä (varmista, että sukellustyökalu on asennettu):

dive <image ID or image tag>

Lähde: will.com