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  .
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: À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.
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Ä  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 packageDetta 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 229MBVisa lagren inuti behÄllarbilden
LÄt oss titta pÄ stapeln av lager inuti bilden. Vi kommer att anvÀnda   för att visa dessa lager:
dive usersignup:v1HĂ€r Ă€r en del av resultaten frĂ„n att köra Dive-kommandot:Â

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
() â Ă€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.

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}:v1LÄ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-imageResultatet 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 257MBSkapa 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.2DÀ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:v1Efter 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% completeUtdatans 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  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:

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
trueVi 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-imageOm 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 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.idxI 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.jarAtt 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 kommandoUtdata 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 listdependencies
spring-boot-loader
snapshot-dependencies
applicationVi 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  . 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:v1Vi 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:v1Som framgĂ„r av utdata tar lagret som innehĂ„ller applikationen nu bara 11 KB, och beroendena cachas i separata skikt.Â

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Ä  .
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 -aSkapa en container-image med Docker-filen:
docker build -f -t .Bygger container-images frÄn kÀllkoden (utan Dockerfile):
mvn spring-boot:build-imageVisa 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 listExtrahera 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 extractVisa listan över container-images
docker imagesVisa lagren inne i container-imagen (se till att verktyget för djupdykning Àr installerat):
diveKĂ€lla: habr.com
