Kontajnery sa stali preferovaným prostriedkom na balenie aplikácie so všetkými závislosťami softvéru a operačného systému, a ich následné dodávanie do rôznych prostredí.
V tomto článku sa skúmajú rôzne spôsoby kontajnerizácie aplikácie Spring Boot:
- vytvorenie obrazu Docker pomocou súboru Docker,
- vytvorenie obrazu OCI zo zdrojového kódu pomocou Cloud-Native Buildpack,
- a optimalizácia obrazu počas spúšťania oddelením častí JAR na rôzne úrovne pomocou viacúrovňových nástrojov.
Príklad kódu
Tento článok je sprevádzaný príkladom funkčného kódu .
Terminológia kontajnerov
Začneme s terminológiou kontajnerov, ktorá sa používa v článku:
- Obraz kontajnera (Container image): súbor určitého formátu. Konvertujeme našu aplikáciu na obraz kontajnera spustením nástroja na zostavenie.
- Kontejner: spustiteľný inštancie obrazu kontajnera.
- Motív kontajnera (Container engine): procesný démon zodpovedný za spúšťanie kontajnera.
- Hostiteľ kontajnera (Container host): hostiteľský počítač, na ktorom beží motív kontajnera.
- Registrácia kontajnerov (Container registry): spoločná lokalita používaná na publikovanie a distribúciu obrazu kontajnera.
- Štandard OCI: je ľahká otvorená štruktúra riadenia, vytvorená v rámci Linux Foundation. Špecifikácia obrazov OCI určuje priemyselné štandardy pre formáty obrazov kontajnerov a prostredia vykonávania, aby zabezpečila, že všetky motívy kontajnerov môžu spúšťať obrazy kontajnerov vytvorené akýmkoľvek nástrojom na zostavenie.
Aby sme umiestnili aplikáciu do kontajnera, zabalíme našu aplikáciu do obrazu kontajnera a publikujeme tento obraz do spoločnej registrácie. Prostredie vykonávania kontajnera načíta tento obraz z registrácie, extrahuje ho a spúšťa aplikáciu v jehonútri.
Verzia 2.3 Spring Boot poskytuje pluginy na vytváranie obrazov OCI.
je najčastejšie používaná implementácia kontajnera, a my používame Docker v našich príkladoch, takže všetky nasledujúce odkazy na kontajner v tomto článku budú znamenať Docker.
Vytvorenie obrazu kontajnera tradičným spôsobom
Vytváranie obrazov Docker pre aplikácie Spring Boot je veľmi jednoduché, stačí pridať niekoľko inštrukcií do súboru Docker.
Na začiatku vytvoríme spustiteľný súbor JAR a ako súčasť pokynov Docker súboru skopírujeme spustiteľný súbor JAR na základný obraz JRE po aplikovaní potrebných nastavení.
Vytvorme našu aplikáciu Spring na so závislosťami web, lomboka actuator. Taktiež pridávame rest kontrolér, aby sme poskytli API s GETmetódou.
Vytvorenie Docker súboru
Potom túto aplikáciu umiestnime do kontajnera pridaní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áš Docker súbor obsahuje základný obraz, z adoptopenjdk, na ktorý kopírujeme náš súbor JAR, a potom otvárame port, 8080ktorý bude počúvať požiadavky.
Zostavenie aplikácie
Najprv je potrebné vytvoriť aplikáciu pomocou Maven alebo Gradle. Tu používame Maven:
mvn clean packageTo vytvorí spustiteľný JAR súbor aplikácie. Musíme tento spustiteľný JAR transformovať do obrazu Docker na fungovanie v Docker engine.
Vytvorenie obrazu kontajnera
Potom umiestnime tento spustiteľný súbor JAR do obrazu Docker vykonaním príkazu docker buildz koreňového adresára projektu, ktorý obsahuje predtým vytvorený Docker súbor:
docker build -t usersignup:v1 .Naše obrázky môžeme vidieť v zozname pomocou príkazu:
docker images Výsledok vykonania vyššie uvedeného príkazu obsahuje náš obraz usersignupspolu so základným obrazom, adoptopenjdk, uvedeným v našom Docker súbore.
REPOSITORY TAG SIZE
usersignup v1 249MB
adoptopenjdk 11-jre-hotspot 229MBPrehľadávame vrstvy vo vnútri obrazu kontajnera
Pozrime sa na zásobník vrstiev vo vnútri obrazu. Na prehliadanie týchto vrstiev použijeme aby sme si tieto vrstvy prezreli:
dive usersignup:v1Tu sú niektoré výsledky vykonania príkazu Dive:

Ako vidíme, aplikačná vrstva tvorí značnú časť veľkosti obrazu. V nasledujúcich sekciách sa snažíme zmenšiť veľkosť tejto vrstvy v rámci našej optimalizácie.
Vytvorenie obrazu kontajnera pomocou Buildpack
() - to je všeobecný termín používaný rôznymi ponukami "Platform as a Service" (PAAS) na vytvorenie obrazu kontajnera zo zdrojového kódu. Bol zavedený spoločnosťou Heroku v roku 2011 a odvtedy ho prijali Cloud Foundry, Google App Engine, Gitlab, Knative a ďalší.

Výhoda používania cloudových buildpackov
Jednou z hlavných výhod používania Buildpacku na vytváranie obrazov je to, že Zmenami konfigurácie obrazu je možné spravovať centrálne (builder) a distribuovať ich na všetky aplikácie, ktoré používajú builder.
Buildpacky boli úzko spojené s platformou. Cloud-Native Buildpacks zabezpečujú štandardizáciu medzi platformami, pričom podporujú formát obrazu OCI, ktorý zaručuje, že obraz môže byť spustený motorom Docker.
Používanie pluginu Spring Boot
Plugin Spring Boot vytvára obrazy OCI z zdrojového kódu pomocou Buildpacku. Obrazy sa vytvárajú s použitím bootBuildImageúloh (Gradle) alebo spring-boot:build-imagecieľov (Maven) a lokálnej inštalácie Docker.
Môžeme si nastaviť názov obrazu, ktorý je potrebné zaslať do registrového Dockeru, určením názvu v image tag:
org.springframework.boot
spring-boot-maven-plugin
docker.io/pratikdas/${project.artifactId}:v1Poďme použiť Maven na vykonanie build-imagecieľa na vytvorenie aplikácie a vytvorenie kontajnerového obrazu. Momentálne nepoužívame žiadne Docker súbory.
mvn spring-boot:build-imageVýsledok bude približne takýto:
[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:build-image (default-cli) @ usersignup ---
[INFO] Vytváram obraz 'docker.io/pratikdas/usersignup:v1'
[INFO]
[INFO] > Sťahovanie builder obrazu 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%
.
.
.. [creator] Pridávanie štítku 'org.springframework.boot.version'
.. [creator] *** Obrazy (c311fe74ec73):
.. [creator] docker.io/pratikdas/usersignup:v1
[INFO]
[INFO] Úspešne postavený obraz 'docker.io/pratikdas/usersignup:v1'Z výstupných údajov vidíme, že paketo Cloud-Native buildpacksa používa na vytvorenie funkčného obrazu OCI. Ako predtým, môžeme vidieť obraz, ktorý je označený ako Docker obraz, vykonaním príkazu:
docker images Výstup:
REPOSITORY SIZE
paketobuildpacks/run 84.3MB
gcr.io/paketo-buildpacks/builder 652MB
pratikdas/usersignup 257MBVytváranie kontajnerového obrazu pomocou Jib
Jib je plugin na vytváranie obrazov od Google, ktorý poskytuje alternatívnu metódu vytvárania kontajnerového obrazu zo zdrojového kódu.
Nastavujeme jib-maven-pluginv pom.xml:
com.google.cloud.tools
jib-maven-plugin
2.5.2Potom spustíme plugin Jib pomocou príkazu Maven na zostavenie aplikácie a vytvorenie kontajnerového obrazu. Ako predtým, tu nepoužívame žiadne Docker súbory:
mvn compile jib:build -Dimage=/usersignup:v1Po vykonaní vyššie uvedeného príkazu Maven dostávame nasledujúci výstup:
[INFO] Kontejnerizovanie aplikácie do pratikdas/usersignup:v1...
.
.
[INFO] Vstupný bod kontajnera nastavený na [java, -cp, /app/resources:/app/classes:/app/libs/*, io.pratik.users.UsersignupApplication]
[INFO]
[INFO] Vytvorený a odoslaný obraz ako pratikdas/usersignup:v1
[INFO] Vykonávanie úloh:
[INFO] [==============================] 100.0% dokončenéVýstupy ukazujú, že obrázok kontajnera bol vytvorený a umiestnený do registra.
Motivácie a metódy optimalizácie obrázkov
Máme dva hlavné dôvody na optimalizáciu:
- Výkon: v systéme orchestrácie kontajnerov sa obraz kontajnera načítava z registra obrázkov na hostiteľa, na ktorom je spustený mechanizmus kontajnerov. Tento proces sa nazýva plánovanie. Naťahovanie veľkých obrázkov z registra vedie k dlhému plánovaciemu času v systémoch orchestrácie kontajnerov a dlhému času zostavovania v CI pipeline.
- Bezpečnosť: veľké obrázky tiež majú väčšiu plochu pre zraniteľnosti.
Obraz Docker sa skladá zo zásobníka vrstiev, pričom každá vrstva predstavuje inštrukciu v našom Dockerfile. Každá vrstva predstavuje delta zmien v spodnej vrstve. Keď načítame obraz Docker z registra, načítava sa po vrstvách a cache-uje sa na hostiteľovi.
Spring Boot používa ako formát balenia v predvolenom nastavení. Keď si prezeráme hrubý JAR, vidíme, že aplikácia predstavuje veľmi malú časť celého JAR. Táto časť sa mení najčastejšie. Zvyšok sa skladá zo závislostí Spring Framework.
Vzorec optimalizácie sa zameriava na izoláciu aplikácie na samostatnej úrovni od závislostí Spring Framework.
Vrstva závislostí, ktorá tvorí hlavnú časť hrubého JAR súboru, sa načítava iba raz a cache-uje sa v hostiteľskom systéme.
Iba tenká vrstva aplikácie sa načítava počas aktualizácií aplikácie a plánovania kontajnerov, ako je znázornené na tomto diagrame:

V nasledujúcich častiach sa pozrieme na to, ako vytvárať 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 viacúrovňovosť tým, že načítava časti hrubého JAR súboru do samostatných vrstiev. Funkcia názvu je predvolene vypnutá a je potrebné ju explicitne zapnúť pomocou pluginu Spring Boot Maven:
org.springframework.boot
spring-boot-maven-plugin
trueTúto konfiguráciu použijeme na vytvorenie nášho kontajnerového obrazu najprv pomocou Buildpacku, a potom pomocou Dockeru v nasledujúcich sekciách.
Spustime build-imagecieľ Maven na vytvorenie kontajnerového obrazu:
mvn spring-boot:build-imageAk spustíme Dive, aby sme videli vrstvy v výslednom obraze, zistíme, že úroveň aplikácie (vyznačená červenou) je v porovnaní s tým, čo sme dostali použitím hrubého formátu JAR, oveľa menšia a pohybuje sa v rozsahu kilobajtov:

Vytvorenie optimalizovaného kontajnerového obrazu pre aplikáciu Spring Boot pomocou Dockeru
Namiesto použitia pluginu Maven alebo Gradle môžeme tiež vytvoriť viacúrovňový obraz JAR Docker s Dockerfile.
Keď používame Docker, musíme vykonať dva dodatočné kroky na extrakciu vrstiev a ich kopírovanie do konečného obrazu.
Obsah získaného JAR po zostavení pomocou Maven s povolenou funkciou vrstvenia bude vyzerať nasledovne:
META-INF/
.
BOOT-INF/lib/
.
BOOT-INF/lib/spring-boot-jarmode-layertools-2.3.3.RELEASE.jar
BOOT-INF/classpath.idx
BOOT-INF/layers.idxVo výstupoch sa zobrazuje ďalší JAR s názvom spring-boot-jarmode-layertoolsa layersfle.idxsúbor. Tento ďalší JAR súbor poskytuje možnosť viacúrovňového spracovania, ako je popísané v nasledujúcej sekcii.
Extrahovanie závislostí na samostatných vrstvách
Ak chceme zobraziť a extrahovať vrstvy z nášho viacúrovňového JAR, použijeme systémový parameter -Djarmode=layertoolsna spustenie spring-boot-jarmode-layertoolsJAR namiesto aplikácie:
java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jarVykonanie tejto príkazu poskytne výstup obsahujúci dostupné príkazy:
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 commandVýstup ukazuje príkazy list, extracta helps helpbyť predvolenou. Spustime príkaz s listmožnosťou:
java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar listdependencies
spring-boot-loader
snapshot-dependencies
applicationVidíme zoznam závislostí, ktoré môžeme pridať ako vrstvy.
Predvolené vrstvy:
Názov vrstvy
Obsah
závislosti
akákoľvek závislosť, verzia ktorej neobsahuje SNAPSHOT
spring-boot-loader
Triedy načítavača JAR
snapshot-dependencies
akákoľvek závislosť, verzia ktorej obsahuje SNAPSHOT
application
triedy aplikácií a zdroje
Vrstvy sú definované v layers.idxsúbore v takom poradí, v ktorom by mali byť pridané do obrazu Docker. Tieto vrstvy sú uložené v hostiteľskom systéme po prvom stiahnutí, keďže sa nemenia. Na hostiteľovi sa načíta iba aktualizovaná vrstva aplikácie, čo prebieha rýchlejšie vďaka zmenšenej veľkosti. .
Súhrn obrazu so závislosťami extrahovanými do samostatných vrstiev
Vytvoríme finálny obraz v dvoch fázach pomocou metódy nazvanej . V prvej fáze extrahujeme závislosti a v druhej fáze skopírujeme extrahované závislosti do konečného obrazu.
Upravme náš Dockerfile pre viacstupňovú zostavu:
# 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"]Ukladáme túto konfiguráciu do samostatného súboru — Dockerfile2.
Zostavíme obraz Docker pomocou príkazu:
docker build -f Dockerfile2 -t usersignup:v1 .Po vykonaní tohto príkazu dostaneme takýto výstup:
Posielanie kontextu zostavy Docker daemon 20.41MB
Krok 1/12 : ZDROJ adoptopenjdk:14-jre-hotspot ako builder
14-jre-hotspot: Načítanie z knižnice/adoptopenjdk
.
.
Úspešne zostavené a9ebf6970841
Úspešne označené userssignup:v1Vidíme, že obraz Docker sa vytvára s identifikátorom obrazu, ktorý sa následne označuje.
Nakoniec spúšťame príkaz Dive, ako aj predtým, aby sme skontrolovali vrstvy vo vygenerovanom obraze Docker. Môžeme zadať identifikátor obrazu alebo tag ako vstup pre príkaz Dive:
dive userssignup:v1Ako vidno z výstupu, vrstva obsahujúca aplikáciu teraz zaberá len 11 KB, pričom závislosti sú uložené v samostatných vrstvách.

Extrahovanie interných závislostí na samostatných vrstvách
Môžeme ďalej zmenšiť veľkosť vrstvy aplikácie extrahovaním ktorýchkoľvek z našich vlastných závislostí do samostatnej vrstvy, namiesto balenia ich spolu s aplikáciou, ako je uvedené v ymlsúbore s názvom layers.idx:
- "závislosti":
- "BOOT-INF/lib/"
- "spring-boot-loader":
- "org/"
- "snapshot-dependencies":
- "custom-dependencies":
- "io/myorg/"
- "aplikácia":
- "BOOT-INF/classes/"
- "BOOT-INF/classpath.idx"
- "BOOT-INF/layers.idx"
- "META-INF/"V tomto súbore layers.idxsme pridali prispôsobenú závislosť s názvom, io.myorgobsahujúcou závislosti organizácie získané z verejného repozitára.
Záver
V tomto článku sa zameriame na využitie Cloud-Native Buildpacks na vytváranie obrazov kontajnerov priamo zo zdrojového kódu. Je to alternatíva k používaniu Docker na vytváranie obrazov kontajnerov tradičným spôsobom: najprv sa vytvorí čiastočne spustiteľný súbor JAR a potom sa zabali do obrazu kontajnera s uvedením pokynov v súbore Docker.
Taktiež sme sa zaoberali optimalizáciou nášho kontajnera, zahrňujúc funkciu vrstvenia, ktorá extrahuje závislosti do samostatných vrstiev, ktoré sa ukladajú v hostiteľskom prostredí, a tenká vrstva aplikácie sa nahráva počas plánovania do mechanizmov vykonávania kontajnerov.
Celý zdrojový kód použitý v článku nájdete na .
Príručka príkazov
Tu je stručný prehľad príkazov, ktoré sme použili v tomto článku na rýchle oboznámenie.
Čistenie kontextu:
docker system prune -aVytvorenie obrazu kontajnera pomocou súboru Docker:
docker build -f -t .Skladáme obraz kontajnera zo zdrojového kódu (bez Dockerfile):
mvn spring-boot:build-imageZobrazenie vrstiev závislostí. Pred zostavením JAR-súboru aplikácie sa uistite, že je funkcia vrstvenia povolená v spring-boot-maven-plugin:
java -Djarmode=layertools -jar application.jar listExtrahovanie vrstiev závislostí. Pred zostavením JAR-súboru aplikácie sa uistite, že je funkcia vrstvenia povolená v spring-boot-maven-plugin:
java -Djarmode=layertools -jar application.jar extractZobrazenie zoznamu obrazov kontajnerov
docker imagesZobrazenie vrstiev vo vnútri obrazu kontajnera (uistite sa, že je nainštalovaný nástroj na potápanie):
diveZdroj: habr.com
