Vytváření optimalizovaných obrazů Docker pro aplikaci Spring Boot

Kontejnerové technologie se staly preferovaným prostředkem pro balení aplikace se všemi jejími závislostmi na softwaru a operačním systému a následné doručení do různých prostředí.

V tomto článku se zabýváme různými způsoby kontejnerizace aplikace Spring Boot:

  • vytvoření obrazu Docker pomocí Dockerfile,
  • vytvoření obrazu OCI ze zdrojového kódu pomocí Cloud-Native Buildpack,
  • a optimalizace obrazu za běhu rozdělením JAR částí na různé úrovně pomocí vícestupňových nástrojů.

 Příklad kódu

Tento článek je doprovázen příkladem funkčního kódu. na GitHubu .

Terminologie kontejnerů

Začneme s terminologií kontejnerů, která se v článku používá:

  • Obraz kontejneru (Container image): soubor určitého formátu. Převedeme naši aplikaci na obraz kontejneru spuštěním nástroje pro sestavení.
  • Kontejner: spustitelný exemplář obrazu kontejneru.
  • Motor kontejneru (Container engine): procesní démon zodpovědný za spuštění kontejneru.
  • Hostitel kontejneru (Container host): hostitelský počítač, na kterém běží motor kontejneru.
  • Registr kontejnerů (Container registry): společná lokalita používaná k publikování a distribuci obrazu kontejneru.
  • Standard OCI: Open Container Initiative (OCI) — je lehká otevřená struktura správy, která byla vytvořena v rámci Linux Foundation. Specifikace obrazů OCI definuje průmyslové standardy pro formáty obrazů kontejnerů a prostředí pro běh, aby bylo zajištěno, že všechny motory kontejnerů mohou spouštět obrazy kontejnerů vytvořené jakýmkoli nástrojem pro sestavení.

Abychom umístili aplikaci do kontejneru, zabalíme naši aplikaci do obrazu kontejneru a publikujeme tento obraz na společném registru. Prostředí běhu kontejneru vytáhne tento obraz z registru, rozbalí ho a spustí aplikaci uvnitř něj.

Verze 2.3 Spring Boot poskytuje pluginy pro vytváření obrazů OCI.

Docker — nejčastěji používaná implementace kontejneru, přičemž v našich příkladech používáme Docker, takže všechny další odkazy na kontejner v tomto článku budou znamenat Docker.

Vytváření obrazu kontejneru tradičním způsobem

Vytvářet obrazy Docker pro aplikace Spring Boot je velmi snadné, přidáním několika příkazů do Dockerfile.

Nejprve vytvoříme spustitelný JAR soubor a jako součást pokynů pro Docker přeneseme spustitelný JAR soubor na základní obraz JRE po aplikaci potřebných nastavení.

Vytvořme naši aplikaci Spring na Spring Initializr s závislostmi web, lomboka actuator. Přidáme také rest kontroler, aby poskytoval API s GETmetodou.

Vytvoření Docker souboru

Poté vložíme tuto aplikaci do kontejneru, přidání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 soubor obsahuje základní obraz, z adoptopenjdk, na který přenášíme náš JAR soubor, a pak otevřeme port, 8080který bude naslouchat požadavkům.

Sestavení aplikace

Nejprve musíme vytvořit aplikaci pomocí Maven nebo Gradle. Zde používáme Maven:

mvn clean package

To vytvoří spustitelný JAR soubor aplikace. Musíme tento spustitelný JAR převést na Docker obraz pro běh v Docker enginu.

Vytvoření kontejnerového obrazu

Poté přeneseme tento spustitelný JAR do Docker obrazu pomocí příkazu docker buildz kořenového adresáře projektu, který obsahuje dříve vytvořený Docker soubor:

docker build  -t usersignup:v1 .

Můžeme vidět náš obraz v seznamu pomocí příkazu:

docker images 

Výsledek provedení výše uvedeného příkazu zahrnuje náš obraz usersignupspolu se základním obrazem, adoptopenjdk, uvedeným v našem Docker souboru.

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

Prohlížení vrstev uvnitř obrazu kontejneru

Podívejme se na zásobník vrstev uvnitř obrazu. Použijeme nástroj  dive, abychom prozkoumali tyto vrstvy:

dive usersignup:v1

Zde je část výsledků provedení příkazu Dive: 

Vytváření optimalizovaných obrazů Docker pro aplikaci Spring Boot

Jak vidíme, aplikační úroveň tvoří značnou část velikosti obrazu. Chceme snížit velikost této vrstvy v následujících sekcích v rámci naší optimalizace.

Vytvoření kontejnerového obrazu pomocí Buildpack

Buildpacky (Buildpacks) – je to obecný termín používaný různými nabídkami „Platforma jako služba“ (PAAS) k vytvoření kontejnerového obrazu z původního kódu. Byl spuštěn Heroku v roce 2011 a od té doby byl přijat Cloud Foundry, Google App Engine, Gitlab, Knative a některé další.

Vytváření optimalizovaných obrazů Docker pro aplikaci Spring Boot

Výhoda cloudových buildpacků

Jednou z hlavních výhod použití Buildpack pro vytváření obrazů je to, že Změny konfigurace obrazu mohou být spravovány centrálně (builder) a distribuovány na všechny aplikace, které používají builder.

Sestavovací balíčky byly úzce spojeny s platformou. Cloud-Native Buildpacks poskytují standardizaci mezi platformami tím, že podporují formát obrazu OCI, který zaručuje, že obraz může být spuštěn na enginu Docker.

Použití pluginu Spring Boot

Plugin Spring Boot vytváří obrazy OCI z původního kódu pomocí Buildpacku. Obrazy jsou vytvářeny pomocí bootBuildImageúkolů (Gradle) nebo spring-boot:build-imagecílů (Maven) a lokální instalace Dockeru.

Můžeme nastavit název obrazu, který bude nutný pro odeslání do registru Docker, uvedením názvu v image tag:

org.springframework.boot
  spring-boot-maven-plugin
  
    
      docker.io/pratikdas/${project.artifactId}:v1

Pojďme použít Maven pro provedení build-imagecíle pro vytváření aplikace a vytváření obrazu kontejneru. V tuto chvíli nepoužíváme žádné Docker soubory.

mvn spring-boot:build-image

Výsledek bude přibližně takový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ýstupních dat vidíme, že paketo Cloud-Native buildpackje používán pro vytváření funkčního obrazu OCI. Jak bylo dříve, můžeme vidět obraz označený jako obraz Docker, provedením příkazu:

docker images 

Výstup:

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

Vytváření obrazu kontejneru pomocí Jib

Jib je plugin pro vytváření obrazů od Google, který poskytuje alternativní metodu pro vytváření obrazu kontejneru z původního kódu.

Nastavujeme jib-maven-pluginv pom.xml:

      com.google.cloud.tools
        jib-maven-plugin
        2.5.2

Dále spouštíme plugin Jib pomocí příkazu Maven, abychom vytvořili aplikaci a vytvořili obraz kontejneru. Jak bylo dříve, zde nepoužíváme žádné Docker soubory:

mvn compile jib:build -Dimage=/usersignup:v1

Po provedení uvedeného příkazu Maven dostáváme následující 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 obraz kontejneru byl vytvořen a nahrán do registru.

Motivace a metody pro vytváření optimalizovaných obrazů

Máme dva hlavní důvody pro optimalizaci:

  • Výkon: v systému orchestrace kontejnerů se obraz kontejneru stahuje z registru obrazů na hostiteli, na kterém je spuštěn mechanismus kontejneru. Tento proces se nazývá plánování. Stahování velkých obrazů z registru vede k dlouhé době plánování v systémech orchestrace kontejnerů a dlouhé době sestavování v CI pipeline.
  • Bezpečnost: velké obrazy také mají větší oblast pro zranitelnosti.

Obraz Docker se skládá z vrstvy, přičemž každá vrstva představuje instrukci v našem Dockerfile. Každá vrstva představuje deltu změn podřízené vrstvy. Když stahujeme obraz Docker z registru, stahuje se po vrstvách a ukládá se do cache na hostiteli.

Spring Boot používá "tlustý JAR" jako výchozí formát balení. Když se podíváme na tlustý JAR, vidíme, že aplikace tvoří velmi malou část celého JAR. Je to část, která se mění nejčastěji. Zbývající část se skládá ze závislostí Spring Framework. Optimalizační formule se zaměřuje na izolaci aplikace na samostatné úrovni od závislostí Spring Framework.

Vrstva závislostí, která tvoří hlavní část tlustého JAR souboru, se načítá pouze jednou a ukládá se do cache v hostitelském systému.

Pouze tenká vrstva aplikace se stahuje během aktualizací aplikace a plánování kontejnerů,

jak je znázorněno na tomto diagramu: V následujících sekcích se podíváme na to, jak vytvářet tyto optimalizované obrazy pro aplikaci Spring Boot.

Vytváření optimalizovaných obrazů Docker pro aplikaci Spring Boot

Vytváření optimalizovaného obrazu kontejneru pro aplikaci Spring Boot pomocí Buildpack

Spring Boot 2.3 podporuje víceúrovňovost tím, že extrahuje části tlustého JAR souboru do samostatných vrstev. Funkce vrstvení je ve výchozím nastavení deaktivována a musí být explicitně povolena pomocí pluginu Spring Boot Maven:

Spring Boot 2.3 podporuje vícestupňovost tím, že extrahuje části tlustého JAR souboru do samostatných vrstev. Funkce vrstvení je ve výchozím nastavení vypnuta a musí být výslovně zapnuta pomocí pluginu Spring Boot Maven:

org.springframework.boot
  spring-boot-maven-plugin
  
    
      true

Tuto konfiguraci využijeme k vytvoření našeho kontejnerového obrazu nejprve pomocí Buildpack, a poté pomocí Dockeru v následujících kapitolách.

Spusťme build-imagecíl Maven pro vytvoření kontejnerového obrazu:

mvn spring-boot:build-image

Pokud spustíme Dive, abychom viděli vrstvy v výsledném obrazu, zjistíme, že úroveň aplikace (označená červeně) je mnohem menší v řádu kilobajtů ve srovnání s tím, co jsme získali použitím tlustého formátu JAR:

Vytváření optimalizovaných obrazů Docker pro aplikaci Spring Boot

Vytvoření optimalizovaného kontejnerového obrazu pro aplikaci Spring Boot pomocí Dockeru

Místo použití pluginu Maven nebo Gradle můžeme také vytvořit vícevrstvý obraz JAR Docker pomocí souboru Docker.

Když používáme Docker, musíme vykonat dva další kroky pro extrakci vrstev a jejich zkopírování do konečného obrazu.

Obsah získaného JAR po sestavení pomocí Maven se zapnutou funkcí vrstvení bude vypadat následovně:

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

Ve výstupu se zobrazuje další JAR s názvem spring-boot-jarmode-layertoolsa layersfle.idxsoubor. Tento dodatečný JAR soubor poskytuje možnost vícevrstvého zpracování, jak je popsáno v následující kapitole.

Extrahování závislostí na samostatných vrstvách

Abychom si prohlédli a extrahovali vrstvy z našeho vícevrstvého JAR, použijeme systémovou vlastnost -Djarmode=layertoolspro spuštění spring-boot-jarmode-layertoolsJAR místo aplikace:

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

Spuštění tohoto příkazu poskytne výstup obsahující dostupné pří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 příkazy list, extracta helps helpbýt ve výchozím nastavení. Pojďme spustit příkaz s listmožností:

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

Vidíme seznam závislostí, které lze přidat jako vrstvy.

Výchozí vrstvy:

Název vrstvy

Obsah

závislosti

jakákoli závislost, jejíž verze neobsahuje SNAPSHOT

spring-boot-loader

třídy načítače JAR

snapshot-dependencies

jakákoli závislost, jejíž verze obsahuje SNAPSHOT

application

třídy aplikací a zdroje

Vrstvy jsou definovány v layers.idxsouboru v pořadí, v jakém by měly být přidány do obrazu Docker. Tyto vrstvy jsou po prvním načtení kešovány na hostiteli, protože se nemění. Na hostitele se nahrává pouze aktualizovaná vrstva aplikace, což probíhá rychleji díky zmenšené velikosti. .

Sestavení obrazu se závislostmi extrahovanými do samostatných vrstev.

Sestavíme finální obraz ve dvou krocích pomocí metody zvané vícefázové sestavení . V prvním kroku extrahujeme závislosti a ve druhém kroku zkopírujeme extrahované závislosti do finálního obrazu.

Upravíme náš soubor Docker pro vícefázové sestavení:

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

Ukládáme tuto konfiguraci do samostatného souboru — Dockerfile2.

Sestavíme obraz Docker pomocí příkazu:

docker build -f Dockerfile2 -t usersignup:v1 .

Po provedení tohoto příkazu dostaneme následující výstup:

Odesílání kontextu sestavení do Daemon Docker  20,41 MB
Krok 1/12 : Z FROM adoptopenjdk:14-jre-hotspot jako builder
14-jre-hotspot: Stahování z knihovny/adoptopenjdk
.
.
Úspěšně postaveno a9ebf6970841
Úspěšně označeno userssignup:v1

Vidíme, že obraz Docker je vytvořen s ID obrazu a poté označen.

Nakonec spustíme příkaz Dive, jako dříve, abychom zkontrolovali vrstvy uvnitř vygenerovaného obrazu Docker. Můžeme specifikovat ID obrazu nebo tag jako vstup pro příkaz Dive:

dive userssignup:v1

Jak je patrné z výstupu, vrstva obsahující aplikaci nyní zabírá pouze 11 KB a závislosti jsou uloženy v samostatných vrstvách. 

Vytváření optimalizovaných obrazů Docker pro aplikaci Spring Boot

Extrakce vnitřních závislostí do samostatných vrstev.

Dále můžeme zmenšit velikost vrstvy aplikace extrakčními jakýmikoliv našimi uživatelskými závislostmi do samostatné vrstvy místo toho, abychom je balili spolu s aplikací, když je deklarujeme v ymlpodobném souboru s názvem 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 souboru layers.idxjsme přidali přizpůsobenou závislost s názvem, io.myorgkterý obsahuje závislosti organizace získané z veřejného repozitáře.

Výstup

V tomto článku jsme prozkoumali použití Cloud-Native Buildpacks k vytvoření kontejnerového obrazu přímo ze zdrojového kódu. Je to alternativa k použití Dockeru pro obvyklé vytváření kontejnerového obrazu: nejprve se vytvoří silný spustitelný soubor JAR a poté se zabalí do kontejnerového obrazu, přičemž se pokyny uvedou v souboru Docker.

Také jsme se zabývali optimalizací našeho kontejneru, včetně funkce vrstvení, která extrahuje závislosti do samostatných úrovní, jež jsou cachovány na hostiteli, zatímco tenká vrstvá aplikace je načítána během plánování v mechanismech provádění kontejnerů.

Celý zdrojový kód použitý v článku můžete najít na Github .

Příručka příkazů

Zde je stručný přehled příkazů, které jsme použili v tomto článku pro rychlou orientaci.

Úklid kontextu:

docker system prune -a

Vytvoření kontejnerového obrazu pomocí souboru Docker:

docker build -f  -t  .

Sestavení kontejnerového obrazu ze zdrojového kódu (bez Dockerfile):

mvn spring-boot:build-image

Zobrazení vrstev závislostí. Před sestavením JAR-souboru aplikace se ujistěte, že funkce vrstvení je povolena v spring-boot-maven-plugin:

java -Djarmode=layertools -jar application.jar list

Extrakce vrstev závislostí. Před sestavením JAR-souboru aplikace se ujistěte, že funkce vrstvení je povolena v spring-boot-maven-plugin:

 java -Djarmode=layertools -jar application.jar extract

Zobrazení seznamu kontejnerových obrazů

docker images

Zobrazení vrstev uvnitř kontejnerového obrazu (ujistěte se, že máte nainstalovaný nástroj pro ponoření):

dive

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster