Skapa optimerade Docker-bilder för din Spring Boot-applikation

Containrar har blivit det föredragna sÀttet att paketera applikationen med alla mjukvaru- och operativsystemberoenden, och sedan leverera dem till olika miljöer.

Denna artikel behandlar olika sÀtt att containerisera en Spring Boot-applikation:

  • skapa en Docker-image med hjĂ€lp av en Dockerfil,
  • skapa en OCI-image frĂ„n kĂ€llkoden med hjĂ€lp av Cloud-Native Buildpack,
  • och optimera bilden vid körning genom att segmentera JAR-filer i olika lager med hjĂ€lp av flerlagersverktyg.

 Kodexempel

Denna artikel Ätföljs av ett exempel pÄ arbetskod pÄ GitHub .

Terminologi för containrar

Vi börjar med terminologin för containrar som anvÀnds i artikeln:

  • Container image: en fil av ett visst format. Vi konverterar vĂ„r applikation till en container image genom att köra ett byggverktyg.
  • Container: en körbar instans av containerbilden.
  • Container engine: en daemonprocess som ansvarar för att köra containern.
  • Container host: vĂ€rddatorn dĂ€r containerverket körs.
  • Container registry: en gemensam plats som anvĂ€nds för att publicera och distribuera containerbilder.
  • Standard OCI: Open Container Initiative (OCI) Ă€r en lĂ€ttviktig öppen styrstruktur som bildades inom ramen för Linux Foundation. OCI-specifikationen för containerbilder definierar branschstandarder för format av containerbilder och körmiljöer för att sĂ€kerstĂ€lla att alla containerverktyg kan köra containerbilder skapade av nĂ„got byggverktyg.

För att placera en applikation i en container kapslar vi in vÄr applikation i en container image och publicerar denna bild i en gemensam registry. Container runtime hÀmtar denna bild frÄn registryn, packar upp den och kör applikationen inuti.

Version 2.3 av Spring Boot erbjuder plugins för att skapa OCI-bilder.

Docker den mest anvÀnda implementeringen av containern, och vi anvÀnder Docker i vÄra exempel, sÄ alla efterföljande referenser till containern i denna artikel kommer att syfta pÄ Docker.

Bygga containerbilder pÄ det traditionella sÀttet

Att skapa Docker-bilder för Spring Boot-applikationer Àr mycket enkelt genom att lÀgga till nÄgra instruktioner i Docker-filen.

Först skapar vi en körbar JAR-fil och, som en del av instruktionerna i Docker-filen, kopierar vi den körbara JAR-filen över basbilden JRE efter att nödvÀndiga instÀllningar har anvÀnts.

LÄt oss skapa vÄr Spring-applikation pÄ Spring Initializr med beroenden web, lombokoch aktuator. Vi lÀgger ocksÄ till en restkontroller för att tillhandahÄlla API med GETmetod.

Skapa Docker-fil

Sedan placerar vi denna applikation i en behÄllare genom att lÀgga till Dockerfile:

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

VÄr Docker-fil innehÄller en basbild, frÄn adoptopenjdk, över vilken vi kopierar vÄr JAR-fil och sedan öppnar en port, 8080som kommer att lyssna pÄ förfrÄgningar.

Bygg applikationen

Först behöver vi skapa applikationen med Maven eller Gradle. HÀr anvÀnder vi Maven:

mvn clean package

Detta skapar den körbara JAR-filen för applikationen. Vi behöver konvertera denna körbara JAR till en Docker-bild för att köra i Docker-motorn.

Skapa behÄllarens bild

Sedan placerar vi den hÀr körbara JAR-filen i Docker-bilden genom att köra kommandot docker buildfrÄn rotkatalogen för projektet som innehÄller Docker-filen som skapades tidigare:

docker build -t usersignup:v1 .

Vi kan se vÄr bild i listan med kommandot:

docker images 

Resultatet av att köra det ovanstÄende kommandot inkluderar vÄr bild usersignuptillsammans med basbilden, adoptopenjdk, angiven i vÄr Docker-fil.

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

Visa lagren inuti behÄllarbilden

LÄt oss titta pÄ stapeln av lager inuti bilden. Vi kommer att anvÀnda verktyg  dive, för att visa dessa lager:

dive usersignup:v1

HÀr Àr en del av resultaten frÄn att köra Dive-kommandot: 

Skapa optimerade Docker-bilder för din Spring Boot-applikation

Som vi kan se utgör applikationslagret en betydande del av bildens storlek. Vi vill minska storleken pÄ detta lager i de följande avsnitten i vÄr optimering.

Skapa behÄllarbilder med hjÀlp av Buildpack

Byggpaket (Buildpacks) – Ă€r en allmĂ€n term som anvĂ€nds av olika erbjudanden av "Plattform som tjĂ€nst" (PAAS) för att skapa behĂ„llarbilder frĂ„n kĂ€llkoden. Det startades av Heroku 2011 och har sedan dess antagits av Cloud Foundry, Google App Engine, Gitlab, Knative och nĂ„gra andra.

Skapa optimerade Docker-bilder för din Spring Boot-applikation

Fördelar med molnbaserade byggpaket

En av de stora fördelarna med att anvÀnda Buildpack för att skapa bilder Àr att Med konfigurationsÀndringar kan man centralt hantera (builder) och distribuera till alla applikationer som anvÀnder builder.

Byggpaketen var nÀra kopplade till plattformen. Cloud-Native Buildpacks sÀkerstÀller standardisering mellan plattformar genom att stödja OCI-bildformatet, vilket garanterar att bilden kan köras pÄ Docker-motorn.

AnvÀnda Spring Boot-plugin

Spring Boot-pluginet skapar OCI-bilder frÄn kÀllkod med hjÀlp av Buildpack. Bilderna skapas med bootBuildImageuppgifter (Gradle) eller spring-boot:build-imagemÄl (Maven) och den lokala Docker-installationen.

Vi kan stÀlla in namnet pÄ bilden som behövs för att skicka till Docker-registret genom att ange namnet i image tag:

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

LÄt oss anvÀnda Maven för att köra build-imagemÄlet för att bygga applikationen och skapa containerbilden. Just nu anvÀnder vi inga Docker-filer.

mvn spring-boot:build-image

Resultatet kommer att se ut ungefÀr sÄ hÀr:

[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:build-image (default-cli) @ usersignup ---
[INFO] Bygger bild 'docker.io/pratikdas/usersignup:v1'
[INFO] 
[INFO]  > HĂ€mtar builder-bild 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%
.
.
.. [skapare]     LĂ€gger till etikett 'org.springframework.boot.version'
.. [skapare]     *** Bilder (c311fe74ec73):
.. [skapare]           docker.io/pratikdas/usersignup:v1
[INFO] 
[INFO] FramgÄngsrikt byggd bild 'docker.io/pratikdas/usersignup:v1'

FrÄn utdata ser vi att paketo Cloud-Native buildpackanvÀnds för att skapa en fungerande OCI-bild. Precis som tidigare kan vi se bilden angiven som Docker-bild genom att köra kommandot:

docker images 

Slutsats:

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

Skapa containerbild med Jib

Jib Àr en bildskapande plugin frÄn Google som erbjuder ett alternativt sÀtt att skapa en containerbild frÄn kÀllkod.

Vi konfigurerar jib-maven-plugini pom.xml:

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

DÀrefter kör vi Jib-pluginet med hjÀlp av Maven-kommandot för att bygga applikationen och skapa containerbilden. Som tidigare anvÀnder vi inga Docker-filer hÀr:

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

Efter att ha kört ovanstÄende Maven-kommando fÄr vi följande utdata:

[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

Utdatans visar att containermedlet har skapats och lagts i registret.

Motivationer och metoder för att skapa optimerade bilder

Vi har tvÄ huvudsakliga skÀl för optimering:

  • Performance: i systemet för orkestrering av containrar hĂ€mtas containermedlet frĂ„n bildregistret till den vĂ€rd dĂ€r containermekanismen körs. Denna process kallas schemalĂ€ggning. Att hĂ€mta stora bilder frĂ„n registret leder till lĂ„ng schemalĂ€ggningstid i system för orkestrering av containrar och lĂ„ng byggtid i CI-pipelines.
  • SĂ€kerhet: stora bilder har ocksĂ„ en större yta för sĂ€kerhetsrisker.

Dockerbilden bestÄr av en stapel av lager, dÀr varje lager representerar en instruktion i vÄr Dockerfile. Varje lager representerar delta förÀndringar av det underliggande lagret. NÀr vi hÀmtar en Dockerbild frÄn registret hÀmtas den i lager och cachas pÄ vÀrden.

Spring Boot anvĂ€nder "tjock JAR" som standardpaketeringsformat. NĂ€r vi granskar den tjocka JAR-filen ser vi att applikationen utgör en mycket liten del av hela JAR-filen. Det Ă€r den del som oftast Ă€ndras. ÅterstĂ„ende del bestĂ„r av beroenden frĂ„n Spring Framework.

Optimeringsformeln fokuserar pÄ att isolera applikationen pÄ en egen nivÄ frÄn beroenden av Spring Framework.

Lagret med beroenden som utgör huvuddelen av den tjocka JAR-filen laddas endast en gÄng och cachas i vÀrdsystemet.

Endast det tunna lagret av applikationen hÀmtas under applikationsuppdateringar och schemalÀggning av containrar, som visas i detta diagram:

Skapa optimerade Docker-bilder för din Spring Boot-applikation

I de följande avsnitten kommer vi att titta pÄ hur man skapar dessa optimerade bilder för Spring Boot-applikationen.

Skapa en optimerad containerbild för Spring Boot-applikationen med hjÀlp av Buildpack

Spring Boot 2.3 stöder flerlagersstruktur genom att extrahera delar av den tjocka JAR-filen i separata lager. Flerlagringsfunktionen Àr inaktiverad som standard och mÄste aktiveras explicit med hjÀlp av Spring Boot Maven-plugin:

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

Vi kommer att anvÀnda denna konfiguration för att skapa vÄr containerbild först med Buildpack och sedan med Docker i de följande avsnitten.

LÄt oss köra build-imageMaven-mÄl för att skapa en containerbild:

mvn spring-boot:build-image

Om vi kör Dive för att se lagren i den resulterande bilden kommer vi att se att applikationslagret (markerat i rött) Àr mycket mindre, inom kilobytes, jÀmfört med vad vi fick med hjÀlp av det tjocka JAR-formatet:

Skapa optimerade Docker-bilder för din Spring Boot-applikation

Skapa en optimerad containerbild för en Spring Boot-applikation med Docker

IstÀllet för att anvÀnda Maven- eller Gradle-plugin kan vi ocksÄ skapa en flerlagers JAR Docker-bild med en Docker-fil.

NÀr vi anvÀnder Docker mÄste vi utföra tvÄ ytterligare steg för att extrahera lagren och kopiera dem till den slutliga bilden.

InnehÄllet i den resulterande JAR-filen efter byggande med Maven med aktiverad lagringsfunktion kommer att se ut pÄ följande sÀtt:

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

I utdata visas en ytterligare JAR med namnet spring-boot-jarmode-layertoolsoch layersfle.idxfil. Denna ytterligare JAR-fil ger möjlighet för flerlagershantering, som beskrivs i nÀsta avsnitt.

Extrahera beroenden pÄ separata lager

För att visa och extrahera lager frÄn vÄr flerlagers JAR anvÀnder vi systemegenskapen -Djarmode=layertoolsför att köra spring-boot-jarmode-layertoolsJAR istÀllet för applikationen:

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

Att köra detta kommando ger utdata som innehÄller tillgÀngliga kommandoparametrar:

AnvÀndning:
  java -Djarmode=layertools -jar usersignup-0.0.1-SNAPSHOT.jar

TillgÀngliga kommandon:
  list     Lista lager frÄn jar-filen som kan extraheras
  extract  Extraherar lager frÄn jar-filen för bildskapande
  help     HjÀlp om nÄgot kommando

Utdata visar kommandon list, extractoch helpmed helpvara som standard. LÄt oss köra kommandot med listalternativet:

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

Vi ser en lista med beroenden som kan lÀggas till som lager.

Standardlager:

Lagnamn

InnehÄll

dependencies

nÄgot beroende vars version inte innehÄller SNAPSHOT

spring-boot-loader

JAR-laddarens klasser

snapshot-dependencies

nÄgot beroende vars version innehÄller SNAPSHOT

application

applikationsklasser och resurser

Skikten definieras i layers.idxfilen i den ordning de ska lÀggas till i Docker-avbildningen. Dessa skikt cachas pÄ vÀrden efter den första hÀmtningen, eftersom de inte Àndras. Endast det uppdaterade lagret av applikationen laddas upp till vÀrden, vilket gÄr snabbare pÄ grund av den minskade storleken. .

Byggandet av avbildningen med beroenden hÀmtade i separata skikt.

Vi kommer att bygga den slutliga avbildningen i tvÄ steg, med en metod som kallas multistep-bygge . I det första steget hÀmtar vi beroenden och i det andra steget kopierar vi de hÀmtade beroendena till den slutliga avbildningen.

LÄt oss modifiera vÄr Docker-fil för multistep-bygge:

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

Vi sparar denna konfiguration i en separat fil — Dockerfile2.

Vi bygger Docker-avbildningen med kommandot:

docker build -f Dockerfile2 -t usersignup:v1 .

Efter att ha kört detta kommando fÄr vi följande utdata:

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

Vi ser att Docker-avbildningen skapas med ett avbildnings-ID och sedan taggas.

Till sist kör vi Dive-kommandot, precis som tidigare, för att kontrollera skikten inne i den genererade Docker-avbildningen. Vi kan ange avbildnings-ID eller tagg som indata för Dive-kommandot:

dive userssignup:v1

Som framgÄr av utdata tar lagret som innehÄller applikationen nu bara 11 KB, och beroendena cachas i separata skikt. 

Skapa optimerade Docker-bilder för din Spring Boot-applikation

HĂ€mtar interna beroenden i separata skikt.

Vi kan ytterligare minska storleken pÄ applikationslagret genom att hÀmta nÄgra av vÄra anpassade beroenden i ett separat lager istÀllet för att packa dem tillsammans med applikationen genom att deklarera dem i ymlen sÄdan fil med namnet 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/"

I denna fil layers.idxhar vi lagt till ett anpassat beroende med namnet, io.myorgsom innehÄller organisationsberoenden hÀmtade frÄn det gemensamma arkivet.

Utdata

I den hÀr artikeln har vi granskat anvÀndningen av Cloud-Native Buildpacks för att skapa en container-image direkt frÄn kÀllkoden. Detta Àr ett alternativ till att anvÀnda Docker för att skapa en container-image pÄ det traditionella sÀttet: först skapas en stor JAR-exekverbar fil, som sedan packas i en container-image med instruktioner i en Docker-fil.

Vi har ocksÄ undersökt optimeringen av vÄr container genom att inkludera ett lager-funktion som extraherar beroenden i separata nivÄer, som cachas pÄ vÀrden, medan det tunna applikationslagret laddas under schemalÀggningen i behÄllarens körningsmekanismer.

Du kan hitta all kÀllkod som anvÀndes i artikeln pÄ Github .

Kommandoguide

HÀr Àr en sammanfattning av de kommandon vi anvÀnde i den hÀr artikeln för snabb referens.

Rensa kontext:

docker system prune -a

Skapa en container-image med Docker-filen:

docker build -f  -t  .

Bygger container-images frÄn kÀllkoden (utan Dockerfile):

mvn spring-boot:build-image

Visa beroendens lager. Innan du bygger JAR-filen för applikationen, se till att lager-funktionen Àr aktiverad i spring-boot-maven-plugin:

java -Djarmode=layertools -jar application.jar list

Extrahera beroendens lager. Innan du bygger JAR-filen för applikationen, se till att lager-funktionen Àr aktiverad i spring-boot-maven-plugin:

 java -Djarmode=layertools -jar application.jar extract

Visa listan över container-images

docker images

Visa lagren inne i container-imagen (se till att verktyget för djupdykning Àr installerat):

dive

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster