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.
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 OCI: Open 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 web, lombokи 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:
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:
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.
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:
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:
Ď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:
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:
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:
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:
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:
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:
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 list, extractи helpс helpbyť predvolený. Spustíme príkaz s listmožnosť:
java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar list
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.
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:
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: