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

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 na GitHub .

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: Open Container Initiative (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.

Docker 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 Spring Initializr 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 package

To 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      229MB

Prehľ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 nástroj  dive, aby sme si tieto vrstvy prezreli:

dive usersignup:v1

Tu sú niektoré výsledky vykonania príkazu Dive: 

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

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

Buildpacky (Buildpacks) - 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ší.

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

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}:v1

Poď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-image

Vý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                  257MB

Vytvá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.2

Potom 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:v1

Po 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 "hrubý JAR" v 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:

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

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
  
    
      true

Tú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-image

Ak 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:

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

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.idx

Vo 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.jar

Vykonanie 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 command

Vý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 list
dependencies
spring-boot-loader
snapshot-dependencies
application

Vidí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 viacstupňová zostava . 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:v1

Vidí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:v1

Ako 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. 

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

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 Github .

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 -a

Vytvorenie 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-image

Zobrazenie 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 list

Extrahovanie 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 extract

Zobrazenie zoznamu obrazov kontajnerov

docker images

Zobrazenie vrstiev vo vnútri obrazu kontajnera (uistite sa, že je nainštalovaný nástroj na potápanie):

dive

Zdroj: habr.com

Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster