Vytváranie optimalizovaných obrazov Docker pre aplikáciu Spring Boot

Kontajnery sa stali preferovaným prostriedkom na zabalenie aplikácie so všetkými závislosťami od softvéru a operačného systému a ich následné dodanie do rôznych prostredí.

Tento článok popisuje rôzne spôsoby kontajnerizácie aplikácie Spring Boot:

  • vytvorenie obrazu Docker pomocou súboru Docker,
  • vytvorenie obrazu OCI zo zdroja pomocou Cloud-Native Buildpack,
  • a optimalizácia obrazu za chodu oddelením častí JAR do rôznych vrstiev pomocou viacvrstvových nástrojov.

 Príklad kódu

Tento článok je doplnený príkladom pracovného kódu na GitHub .

Kontajnerová terminológia

Začneme kontajnerovou terminológiou použitou v článku:

  • Obrázok kontajnera: súbor špecifického formátu. Našu aplikáciu prevedieme na obrázok kontajnera spustením nástroja na zostavenie.
  • Kontajner: Spustiteľná inštancia obrázka kontajnera.
  • Kontajnerový motor: Proces démona zodpovedný za spustenie kontajnera.
  • Hostiteľ kontajnera: Hostiteľský počítač, na ktorom beží kontajnerový engine.
  • Register kontajnerov: Všeobecné umiestnenie používané na zverejnenie a distribúciu obrázka kontajnera.
  • štandard OCIOpen Container Initiative (OCI) je ľahká, otvorená štruktúra riadenia vytvorená v rámci Linux Foundation. Špecifikácia obrázkov OCI definuje priemyselné štandardy pre formáty obrázkov kontajnerov a runtime, aby sa zabezpečilo, že všetky kontajnerové nástroje môžu spúšťať obrázky kontajnerov vytvorené akýmkoľvek nástrojom na zostavovanie.

Aby sme aplikáciu kontajnerizovali, zabalíme našu aplikáciu do obrázka kontajnera a zverejníme tento obrázok v zdieľanom registri. Runtime kontajnera získa tento obrázok z registra, rozbalí ho a spustí aplikáciu v ňom.

Verzia 2.3 Spring Boot poskytuje doplnky na vytváranie obrazov OCI.

prístavný robotník je najbežnejšie používaná implementácia kontajnera a v našich príkladoch používame Docker, takže všetky nasledujúce odkazy na kontajnery v tomto článku budú odkazovať na Docker.

Vytváranie obrazu kontajnera tradičným spôsobom

Vytváranie obrazov Docker pre aplikácie Spring Boot je veľmi jednoduché pridaním niekoľkých pokynov do súboru Docker.

Najprv vytvoríme spustiteľný súbor JAR a ako súčasť pokynov pre súbor Docker skopírujeme spustiteľný súbor JAR na základný obrázok JRE po použití potrebných nastavení.

Poďme vytvoriť našu jarnú aplikáciu Spring Initializr so závislosťami weblombokи actuator. Pridávame tiež ovládač odpočinku, ktorý poskytuje API GETmetóda.

Vytvorenie súboru Dockerfile

Túto aplikáciu potom kontajnerizujeme pridávaním Dockerfile:

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

Náš súbor Docker obsahuje základný obrázok z adoptopenjdk, na ktorý skopírujeme náš súbor JAR a potom otvoríme port, 8080ktorý bude počúvať žiadosti.

Vytváranie aplikácie

Najprv musíte vytvoriť aplikáciu pomocou Maven alebo Gradle. Tu používame Maven:

mvn clean package

Tým sa vytvorí spustiteľný súbor JAR pre aplikáciu. Potrebujeme previesť tento spustiteľný JAR na obraz Docker, aby sa spustil na Docker engine.

Vytvorenie obrázka kontajnera

Tento spustiteľný súbor JAR potom vložíme do obrazu Docker spustením príkazu docker buildz koreňového adresára projektu obsahujúceho skôr vytvorený súbor Docker:

docker build  -t usersignup:v1 .

Náš obrázok môžeme vidieť v zozname pomocou príkazu:

docker images 

Výstup vyššie uvedeného príkazu obsahuje náš obrázok usersignupspolu so základným obrázkom, adoptopenjdkšpecifikované v našom súbore Docker.

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

Zobrazenie vrstiev vo vnútri obrázka kontajnera

Pozrime sa na hromadu vrstiev vo vnútri obrázka. Budeme používať náradie  ponor ak chcete zobraziť tieto vrstvy:

dive usersignup:v1

Tu je časť výstupu z príkazu Dive: 

Vytváranie optimalizovaných obrazov Docker pre aplikáciu Spring Boot

Ako vidíme, aplikačná vrstva tvorí významnú časť veľkosti obrázka. V rámci našej optimalizácie chceme v nasledujúcich častiach zmenšiť veľkosť tejto vrstvy.

Vytvorenie obrazu kontajnera pomocou Buildpack

Montážne balíčky (Buildpacks) je všeobecný pojem, ktorý používajú rôzne ponuky platformy ako služba (PAAS) na vytvorenie obrázka kontajnera zo zdrojového kódu. Bola spustená spoločnosťou Heroku v roku 2011 a odvtedy bola prijatá spoločnosťami Cloud Foundry, Google App Engine, Gitlab, Knative a niekoľkými ďalšími.

Vytváranie optimalizovaných obrazov Docker pre aplikáciu Spring Boot

Výhoda cloudových zostavovacích balíkov

Jednou z hlavných výhod používania Buildpack na vytváranie obrázkov je to Zmeny konfigurácie obrazu je možné spravovať centrálne (builder) a šíriť do všetkých aplikácií pomocou buildera.

Zostavovacie balíčky boli pevne spojené s platformou. Cloud-Native Buildpacks poskytujú štandardizáciu naprieč platformami tým, že podporujú obrazový formát OCI, ktorý zaisťuje, že obraz môže byť spustený pomocou nástroja Docker.

Pomocou doplnku Spring Boot

Doplnok Spring Boot vytvára obrázky OCI zo zdroja pomocou Buildpack. Obrázky sú vytvorené pomocou bootBuildImageúlohy (Gradle) príp spring-boot:build-imageciele (Maven) a lokálna inštalácia Docker.

Názov obrázka potrebného na odoslanie do registra Docker môžeme prispôsobiť zadaním názvu v 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>

Využime na to Maven build-imageciele pre vytvorenie aplikácie a vytvorenie obrazu kontajnera. Momentálne nepoužívame žiadne súbory Dockerfiles.

mvn spring-boot:build-image

Výsledkom bude niečo takéto:

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

Z výstupu to vidíme paketo Cloud-Native buildpackpoužíva sa na vytvorenie pracovného obrazu OCI. Ako predtým, môžeme vidieť obrázok uvedený ako obrázok Docker spustením príkazu:

docker images 

Záver:

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

Vytvorenie obrazu kontajnera pomocou Jib

Jib je doplnok na vytváranie obrázkov od spoločnosti Google, ktorý poskytuje alternatívnu metódu na vytvorenie obrázka kontajnera zo zdrojového kódu.

Konfigurácia jib-maven-pluginv pom.xml:

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

Ďalej spustíme doplnok Jib pomocou príkazu Maven na zostavenie aplikácie a vytvorenie obrázka kontajnera. Ako predtým, tu nepoužívame žiadne súbory Docker:

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

Po vykonaní vyššie uvedeného príkazu Maven dostaneme nasledujúci výstup:

[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

Výstup ukazuje, že obrázok kontajnera bol vytvorený a umiestnený do registra.

Motivácie a techniky na vytváranie optimalizovaných obrázkov

Na optimalizáciu máme dva hlavné dôvody:

  • produktivita: V systéme orchestrácie kontajnerov sa obrázok kontajnera získa z registra obrázkov do hostiteľa, na ktorom je spustený kontajnerový mechanizmus. Tento proces sa nazýva plánovanie. Sťahovanie veľkých obrázkov z registra má za následok dlhé časy plánovania v systémoch orchestrácie kontajnerov a dlhé časy zostavovania v kanáloch CI.
  • zabezpečenia: Väčšie obrázky majú tiež väčšiu oblasť pre chyby zabezpečenia.

Obraz Docker sa skladá z hromady vrstiev, z ktorých každá predstavuje inštrukciu v našom súbore Docker. Každá vrstva predstavuje deltu zmien v podkladovej vrstve. Keď vytiahneme obrázok Docker z registra, stiahne sa vo vrstvách a uloží sa do vyrovnávacej pamäte na hostiteľovi.

Spring Boot používa "tučný JAR" v ako predvolený formát balenia. Keď sa pozrieme na hrubý JAR, vidíme, že aplikácia tvorí veľmi malú časť celého JAR. Toto je časť, ktorá sa mení najčastejšie. Zvyšok tvoria závislosti Spring Frameworku.

Optimalizačný vzorec sa sústreďuje na izoláciu aplikácie na oddelenej úrovni od závislostí Spring Framework.

Vrstva závislostí, ktorá tvorí väčšinu hrubého súboru JAR, sa stiahne iba raz a uloží do vyrovnávacej pamäte hostiteľského systému.

Pri aktualizáciách aplikácií a plánovaní kontajnerov sa stiahne iba tenká vrstva aplikácie. ako je znázornené na tomto diagrame:

Vytváranie optimalizovaných obrazov Docker pre aplikáciu Spring Boot

V nasledujúcich častiach sa pozrieme na to, ako vytvoriť tieto optimalizované obrázky pre aplikáciu Spring Boot.

Vytvorenie optimalizovaného obrazu kontajnera pre aplikáciu Spring Boot pomocou Buildpack

Spring Boot 2.3 podporuje vrstvenie extrahovaním častí hrubého súboru JAR do samostatných vrstiev. Funkcia vrstvenia je predvolene vypnutá a musí byť explicitne povolená pomocou doplnku Spring Boot Maven:

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

Túto konfiguráciu použijeme na vytvorenie obrazu kontajnera najprv pomocou Buildpack a potom pomocou Docker v nasledujúcich častiach.

Poďme spustiť build-imageCieľ Maven na vytvorenie obrázka kontajnera:

mvn spring-boot:build-image

Ak spustíme Dive, aby sme videli vrstvy vo výslednom obrázku, môžeme vidieť, že aplikačná vrstva (označená červenou farbou) je oveľa menšia v rozsahu kilobajtov v porovnaní s tým, čo sme získali pomocou tučného formátu JAR:

Vytváranie optimalizovaných obrazov Docker pre aplikáciu Spring Boot

Vytvorenie optimalizovaného obrazu kontajnera pre aplikáciu Spring Boot pomocou Docker

Namiesto použitia doplnku Maven alebo Gradle môžeme tiež vytvoriť vrstvený obraz Docker JAR so súborom Docker.

Keď používame Docker, musíme vykonať dva ďalšie kroky na extrahovanie vrstiev a ich skopírovanie do konečného obrázka.

Obsah výsledného JAR po zostavení pomocou Maven so zapnutým vrstvením bude vyzerať takto:

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

Výstup zobrazuje ďalší JAR s názvom spring-boot-jarmode-layertoolsи layersfle.idxsúbor. Tento dodatočný súbor JAR poskytuje možnosti vrstveného spracovania, ako je popísané v ďalšej časti.

Extrahovanie závislostí na jednotlivých vrstvách

Na zobrazenie a extrahovanie vrstiev z nášho vrstveného JAR používame vlastnosť system -Djarmode=layertoolsna začiatok spring-boot-jarmode-layertoolsJAR namiesto aplikácie:

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

Spustenie tohto príkazu vytvorí výstup obsahujúci dostupné možnosti príkazu:

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

Výstup zobrazuje príkazy listextractи helpс helpbyť predvolený. Spustíme príkaz s listmožnosť:

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

Vidíme zoznam závislostí, ktoré je možné pridať ako vrstvy.

Predvolené vrstvy:

Názov vrstvy

Obsah

dependencies

akúkoľvek závislosť, ktorej verzia neobsahuje SNAPSHOT

spring-boot-loader

Triedy nakladača JAR

snapshot-dependencies

akúkoľvek závislosť, ktorej verzia obsahuje SNAPSHOT

application

aplikačné triedy a prostriedky

Vrstvy sú definované v layers.idxsúbor v poradí, v akom sa majú pridať do obrazu Docker. Tieto vrstvy sa uložia do vyrovnávacej pamäte v hostiteľovi po prvom načítaní, pretože sa nemenia. Na hostiteľa sa stiahne iba aktualizovaná aplikačná vrstva, čo je rýchlejšie vďaka zmenšenej veľkosti .

Vytváranie obrazu so závislosťami extrahovanými do samostatných vrstiev

Výsledný obrázok vytvoríme v dvoch fázach pomocou metódy tzv viacstupňová montáž . V prvom kroku vyextrahujeme závislosti a v druhom kroku skopírujeme vyextrahované závislosti do výsledného obrázku.

Upravme náš Dockerfile pre viacstupňové zostavenie:

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

Túto konfiguráciu uložíme do samostatného súboru - Dockerfile2.

Obrázok Docker vytvoríme pomocou príkazu:

docker build -f Dockerfile2 -t usersignup:v1 .

Po spustení tohto príkazu dostaneme nasledujúci výstup:

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

Vidíme, že obrázok Docker sa vytvorí s ID obrázka a potom sa označí.

Nakoniec spustíme príkaz Dive ako predtým, aby sme skontrolovali vrstvy vo vygenerovanom obrázku Docker. Ako vstup do príkazu Dive môžeme poskytnúť ID obrázka alebo značku:

dive userssignup:v1

Ako môžete vidieť vo výstupe, vrstva obsahujúca aplikáciu má teraz len 11 KB a závislosti sú uložené v medzipamäti v samostatných vrstvách. 

Vytváranie optimalizovaných obrazov Docker pre aplikáciu Spring Boot

Extrahovanie vnútorných závislostí na jednotlivých vrstvách

Veľkosť aplikačnej vrstvy môžeme ďalej zmenšiť extrahovaním akýchkoľvek našich vlastných závislostí do samostatnej vrstvy namiesto toho, aby sme ich pribalili do aplikácie tak, že ich deklarujeme v ymlpodobný súbor s názvom 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/"

V tomto súbore layers.idxpridali sme vlastnú závislosť s názvom, io.myorgobsahujúce závislosti organizácie získané zo zdieľaného úložiska.

Výkon

V tomto článku sme sa zamerali na používanie Cloud-Native Buildpacks na vytvorenie obrazu kontajnera priamo zo zdrojového kódu. Toto je alternatíva k použitiu Dockera na vytvorenie obrazu kontajnera zvyčajným spôsobom: najprv vytvorte hrubý spustiteľný súbor JAR a potom ho zabaľte do obrazu kontajnera zadaním pokynov v súbore Docker.

Pozreli sme sa aj na optimalizáciu nášho kontajnera povolením funkcie vrstvenia, ktorá stiahne závislosti do samostatných vrstiev, ktoré sú uložené vo vyrovnávacej pamäti na hostiteľovi, a tenká vrstva aplikácie sa načíta v čase plánovania v spúšťacích nástrojoch kontajnera.

Všetky použité zdrojové kódy nájdete v článku na adrese GitHub .

Odkaz na príkaz

Tu je rýchly prehľad príkazov, ktoré sme použili v tomto článku.

Vyčistenie kontextu:

docker system prune -a

Vytvorenie obrázka kontajnera pomocou súboru Docker:

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

Vytvoríme obrázok kontajnera zo zdrojového kódu (bez Dockerfile):

mvn spring-boot:build-image

Zobraziť vrstvy závislostí. Pred vytvorením súboru JAR aplikácie sa uistite, že funkcia vrstvenia je povolená v doplnku spring-boot-maven-plugin:

java -Djarmode=layertools -jar application.jar list

Extrahovanie vrstiev závislosti. Pred vytvorením súboru JAR aplikácie sa uistite, že funkcia vrstvenia je povolená v doplnku spring-boot-maven-plugin:

 java -Djarmode=layertools -jar application.jar extract

Pozrite si zoznam obrázkov kontajnerov

docker images

Zobraziť vľavo vo vnútri obrázka kontajnera (uistite sa, že je nainštalovaný potápačský nástroj):

dive <image ID or image tag>

Zdroj: hab.com