Erstellt optimiséiert Docker Biller fir eng Fréijoersboot Applikatioun

Container sinn de bevorzugte Mëttel ginn fir eng Applikatioun mat all senge Software- a Betribssystemabhängegkeeten ze packen an se dann an verschidden Ëmfeld ze liwweren.

Dësen Artikel deckt verschidde Weeër fir eng Spring Boot Applikatioun ze containeriséieren:

  • en Docker-Bild bauen mat engem Dockerfile,
  • en OCI Bild aus der Quell ze bauen mat Cloud-Native Buildpack,
  • a Bildoptimiséierung bei der Runtime andeems JAR Deeler op verschidden Niveauen trennen mat Layer Tools.

 Code Beispill

Dësen Artikel gëtt vun engem Aarbechtscode Beispill begleet op GitHub .

Container Terminologie

Mir fänken un mat der Containerterminologie déi am ganzen Artikel benotzt gëtt:

  • Container Bild: e Fichier vun engem spezifesche Format. Mir konvertéieren eis Applikatioun an e Containerbild andeems mir de Build-Tool lafen.
  • Container: Eng ausführbar Instanz vum Containerbild.
  • Container Motor: Den Daemonprozess verantwortlech fir de Container ze lafen.
  • Container Host: D'Hostmaschinn op där de Containermotor leeft.
  • Container Registry: Déi allgemeng Plaz benotzt fir d'Containerbild ze publizéieren an ze verdeelen.
  • OCI StandardOpen Container Initiative (OCI) ass e liichte Open-Source Management Kader geformt vun der Linux Foundation. D'OCI Image Spezifikatioun definéiert Industriestandards fir Containerbildformater an d'Runtime fir sécherzestellen datt all Containermotoren Containerbiller kënne lafen, déi vun all Build-Tool erstallt ginn.

Fir eng Applikatioun ze containeriséieren, wéckele mir eis Applikatioun an engem Containerbild a publizéieren dat Bild an den ëffentleche Registry. De Container Runtime recuperéiert dëst Bild aus dem Registry, packt et aus a leeft d'Applikatioun dran.

Versioun 2.3 vum Spring Boot bitt Plugins fir OCI Biller ze bauen.

Docker ass déi meescht benotzt Container Implementatioun, a mir benotzen Docker an eise Beispiller, sou datt all spéider Container Referenzen an dësem Artikel op Docker bezéien.

Baut e Containerbild op déi traditionell Manéier

Docker Biller fir Spring Boot Uwendungen bauen ass ganz einfach andeems Dir e puer Instruktiounen op Är Dockerfile bäidréit.

Mir erstellen als éischt en ausführbaren JAR an, als Deel vun den Dockerfile Instruktiounen, kopéieren den ausführbare JAR uewen um Basis JRE Bild nodeems Dir déi néideg Personnalisatiounen ugewannt hutt.

Loosst eis eis Fréijoersapplikatioun erstellen Fréijoer Initializr mat Ofhängegkeeten weblombokи actuator. Mir addéieren och e Rescht Controller fir eng API ze bidden GETMethod.

Erstellt eng Dockerfile

Mir setzen dës Applikatioun dann an engem Container andeems mir addéieren Dockerfile:

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

Eis Docker Datei enthält e Basisbild vun adoptopenjdk, uewen op deem mir eis JAR Datei kopéieren an dann den Hafen opmaachen, 8080déi no Ufroe lauschteren.

Applikatioun Assemblée

Als éischt musst Dir eng Applikatioun erstellen mat Maven oder Gradle. Hei benotze mir Maven:

mvn clean package

Dëst erstellt eng ausführbar JAR Datei fir d'Applikatioun. Mir mussen dësen ausführbare JAR an en Docker-Bild konvertéieren fir op den Docker-Motor ze lafen.

Erstellt e Containerbild

Mir setzen dann dës ausführbar JAR-Datei an den Docker-Bild andeems Dir de Kommando ausféiert docker buildaus dem Root Verzeechnes vum Projet mat der Dockerfile déi virdru erstallt gouf:

docker build  -t usersignup:v1 .

Mir kënnen eist Bild an der Lëscht mam Kommando gesinn:

docker images 

D'Ausgab vum uewe genannte Kommando enthält eist Bild usersignupzesumme mam Basisbild, adoptopenjdkan eisem Dockerfile spezifizéiert.

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

Kuckt Schichten an engem Containerbild

Loosst eis de Stack vu Schichten am Bild kucken. Mir wäerten benotzen Instrument  tauchen, fir dës Schichten ze gesinn:

dive usersignup:v1

Hei ass en Deel vun der Ausgab vum Dive Kommando: 

Erstellt optimiséiert Docker Biller fir eng Fréijoersboot Applikatioun

Wéi mir kënne gesinn, mécht d'Applikatiounsschicht e groussen Deel vun der Bildgréisst aus. Mir wëllen d'Gréisst vun dëser Schicht an de folgende Sektiounen reduzéieren als Deel vun eiser Optimisatioun.

Baut e Containerbild mat Buildpack

Assemblée Pakete (Buildpacks) ass e generesche Begrëff, dee vu verschiddene Plattform als Service (PAAS) Offere benotzt gëtt fir e Containerbild aus Quellcode ze kreéieren. Et gouf vum Heroku am 2011 gestart an ass zënterhier vun Cloud Foundry, Google App Engine, Gitlab, Knative an e puer anerer ugeholl ginn.

Erstellt optimiséiert Docker Biller fir eng Fréijoersboot Applikatioun

De Virdeel vu Cloud Build Packagen

Ee vun den Haaptvirdeeler vum Buildpack ze benotzen fir Biller ze bauen ass dat Bildkonfiguratiounsännerunge kënnen zentral geréiert ginn (Builder) a propagéiert op all Uwendungen mat dem Builder.

D'Build Packagen waren enk mat der Plattform gebonnen. Cloud-Native Buildpacks bidden Standardiséierung iwwer Plattformen andeems se den OCI Bildformat ënnerstëtzen, wat garantéiert datt d'Bild vum Docker-Motor lafen kann.

Benotzt de Spring Boot Plugin

De Spring Boot Plugin baut OCI Biller aus der Quell mat Buildpack. Biller ginn erstallt mat bootBuildImageAufgaben (Gradle) oder spring-boot:build-imageZil (Maven) a lokal Docker Installatioun.

Mir kënnen den Numm vum Bild personaliséiere wat mir brauchen fir an d'Docker Registry ze drécken andeems Dir den Numm spezifizéiert image tag:

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <image>
      <name>docker.io/pratikdas/${project.artifactId}:v1</name>
    </image>
  </configuration>
</plugin>

Loosst eis Maven benotzen fir auszeféieren build-imageZiler fir eng Applikatioun ze kreéieren an e Containerbild ze kreéieren. Mir benotzen de Moment keng Dockerfiles.

mvn spring-boot:build-image

D'Resultat wäert eppes wéi dat sinn:

[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'

Vun der Ausgab gesi mir dat paketo Cloud-Native buildpackbenotzt fir en funktionéierend OCI Bild ze kreéieren. Wéi virdru kënne mir d'Bild als Docker-Bild gesinn andeems Dir de Kommando ausféiert:

docker images 

Fazit:

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

Erstellt e Container Bild mat Jib

Jib ass en Image Authoring Plugin vu Google deen eng alternativ Method ubitt fir e Containerbild aus der Quell ze kreéieren.

Konfiguréieren jib-maven-pluginan pom.xml:

      <plugin>
        <groupId>com.google.cloud.tools</groupId>
        <artifactId>jib-maven-plugin</artifactId>
        <version>2.5.2</version>
      </plugin>

Als nächst lafen mir de Jib Plugin mam Maven Kommando fir d'Applikatioun ze bauen an d'Containerbild ze kreéieren. Wéi virdru benotze mir keng Dockerfiles hei:

mvn compile jib:build -Dimage=<docker registry name>/usersignup:v1

Nodeems Dir de uewe genannte Maven Kommando ausgefouert hutt, kréie mir déi folgend Ausgab:

[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

D'Ausgab weist datt d'Containerbild erstallt an am Registry gesat gouf.

Motivatioune a Methoden fir optimiséiert Biller ze kreéieren

Mir hunn zwee Haaptgrënn fir ze optimiséieren:

  • Produktivitéit: An engem Container-Orchestratiounssystem gëtt e Containerbild aus dem Bildregister op den Host gezunn, deen de Containermotor leeft. Dëse Prozess gëtt Planung genannt. Grouss Biller aus der Registry zéien resultéiert a laange Fuerplangzäiten a Containerorchestratiounssystemer a laange Bauzäiten an CI Pipelines.
  • Sécherheet: grouss Biller hunn och e grousst Gebitt fir Schwachstelle.

En Docker-Bild besteet aus engem Stack vu Schichten, jidderee representéiert eng Ausso an eiser Dockerfile. All Layer duerstellt den Delta vun Ännerungen an der Basisdaten Layer. Wa mir e Docker-Bild aus der Registry zéien, gëtt et a Schichten gezunn an um Host gespäichert.

Fréijoer Boot benotzt "Fett JAR" an als Standardverpackungsformat. Wa mir e fett JAR kucken, gesi mir datt d'Applikatioun e ganz klengen Deel vum ganze JAR ass. Dëst ass deen Deel deen am meeschten ännert. De Rescht besteet aus Fréijoer Framework Ofhängegkeeten.

D'Optimiséierungsformel ass zentréiert ronderëm d'Isolatioun vun der Applikatioun op engem getrennten Niveau vu Spring Framework Ofhängegkeeten.

D'Ofhängegkeetsschicht, déi de gréissten Deel vun der décke JAR-Datei bildt, gëtt nëmmen eemol erofgelueden an am Hostsystem cache.

Nëmmen eng dënn Schicht vun der App gëtt wärend Appupdates a Containerplang gezunn, wéi an dësem Diagramm gewisen:

Erstellt optimiséiert Docker Biller fir eng Fréijoersboot Applikatioun

An de folgende Sektioune wäerte mir kucken wéi Dir dës optimiséiert Biller fir eng Spring Boot Applikatioun erstellt.

Baut en optiméiert Containerbild fir eng Fréijoersbootapplikatioun mat Buildpack

Spring Boot 2.3 ënnerstëtzt Schichten andeems Deeler vun enger décker JAR Datei an getrennte Schichten extrahéiert ginn. D'Schichtfunktioun ass par défaut deaktivéiert a muss explizit aktivéiert ginn mat dem Spring Boot Maven Plugin:

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <layers>
      <enabled>true</enabled>
    </layers>
  </configuration> 
</plugin>

Mir benotze dës Konfiguratioun fir eis Containerbild als éischt mat Buildpack ze bauen an dann mat Docker an de folgende Sektiounen.

Loosst eis lafen build-imageMaven Zil fir Containerbild ze kreéieren:

mvn spring-boot:build-image

Wa mir Dive lafen fir d'Schichten am entsteet Bild ze gesinn, kënne mir gesinn datt d'Applikatiounsschicht (rout ëmkreest) vill méi kleng ass am Kilobyte Beräich am Verglach mat deem wat mir mam décke JAR Format kruten:

Erstellt optimiséiert Docker Biller fir eng Fréijoersboot Applikatioun

Baut en optiméiert Containerbild fir eng Fréijoersboot Applikatioun mat Docker

Amplaz e Maven oder Gradle Plugin ze benotzen, kënne mir och e Layer Docker JAR Bild mat enger Docker Datei erstellen.

Wa mir Docker benotzen, musse mir zwee extra Schrëtt huelen fir d'Schichten ze extrahieren an se an dat lescht Bild ze kopéieren.

Den Inhalt vum resultéierende JAR nom Bau mam Maven mat Layer aktivéiert wäert esou ausgesinn:

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

D'Ausgab weist en zousätzleche JAR genannt spring-boot-jarmode-layertoolsи layersfle.idxFichier. Dës zousätzlech JAR-Datei bitt Schichtenfäegkeeten, wéi an der nächster Sektioun beschriwwen.

Ofhängegkeeten op eenzel Schichten extrahéieren

Fir Schichten aus eisem Layer JAR ze gesinn an ze extrahieren, benotze mir d'Systemeigenschaften -Djarmode=layertoolsfir Start spring-boot-jarmode-layertoolsJAR amplaz Applikatioun:

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

Dëse Kommando auszeféieren produzéiert en Ausgang mat de verfügbare Kommandooptiounen:

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

D'Ausgab weist d'Befehle listextractи helpс helpde Standard sinn. Loosst eis de Kommando lafen mat listOptioun:

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

Mir gesinn eng Lëscht vun Ofhängegkeeten déi als Schichten hinzugefügt kënne ginn.

Schichten par défaut:

Layer Numm

Inhalt

dependencies

all Ofhängegkeet deenen hir Versioun keng SNAPSHOT enthält

spring-boot-loader

JAR Loader Klassen

snapshot-dependencies

all Ofhängegkeet deem seng Versioun SNAPSHOT enthält

application

Applikatioun Klassen a Ressourcen

Schichten sinn definéiert an layers.idxDatei an der Uerdnung déi se an d'Docker Bild bäigefüügt ginn. Dës Schichten ginn no der éischter Erhuelung um Host gespäichert well se net änneren. Nëmmen déi aktualiséiert Applikatiounsschicht gëtt op den Host erofgelueden, wat méi séier ass wéinst der reduzéierter Gréisst .

Baut e Bild mat Ofhängegkeeten an getrennte Schichten extrahéiert

Mir bauen d'Finale Bild an zwee Schrëtt mat enger Method genannt Multi-Etapp Assemblée . Am éischte Schrëtt wäerte mir d'Ofhängegkeeten extrahéieren an am zweete Schrëtt kopéiere mir déi extrahéiert Ofhängegkeeten an d'Finale.

Loosst eis eis Dockerfile fir e Multi-Stage Build änneren:

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

Mir späicheren dës Konfiguratioun an enger separater Datei - Dockerfile2.

Mir bauen den Docker Bild mam Kommando:

docker build -f Dockerfile2 -t usersignup:v1 .

Nodeems Dir dëse Kommando ausgefouert hutt, kréie mir déi folgend Ausgab:

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

Mir kënne gesinn datt den Docker-Bild mat enger Bild-ID erstallt gëtt an dann gezeechent gëtt.

Endlech lafe mir den Dive Kommando wéi virdru fir d'Schichten am generéierten Docker Bild ze kontrolléieren. Mir kënnen e Bild ID oder Tag als Input fir den Dive Kommando ubidden:

dive userssignup:v1

Wéi Dir aus der Ausgab kënnt gesinn, ass d'Schicht mat der Applikatioun elo nëmmen 11 KB an d'Ofhängegkeete ginn an getrennten Schichten cache. 

Erstellt optimiséiert Docker Biller fir eng Fréijoersboot Applikatioun

Extrait intern Ofhängegkeeten op getrennten Schichten

Mir kënnen d'Gréisst vun der Applikatiounsschicht weider reduzéieren andeems ee vun eise personaliséierten Ofhängegkeeten an eng separat Schicht extrahéiert anstatt se mat der Applikatioun ze packen andeems se se an deklaréieren ymlähnlech Datei genannt 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/"

An dësem Fichier layers.idxmir hunn eng personaliséiert Ofhängegkeet bäigefüügt genannt, io.myorgenthale Organisatioun Ofhängegkeeten aus dem gemeinsame Repository zréckgewonnen.

Konklusioun

An dësem Artikel hu mir d'Benotze vu Cloud-Native Buildpacks gekuckt fir e Containerbild direkt vun der Quell ze bauen. Dëst ass eng Alternativ fir Docker ze benotzen fir e Containerbild op déi üblech Manéier ze kreéieren: als éischt gëtt eng déck ausführbar JAR Datei erstallt an dann an e Containerbild verpackt andeems Dir d'Instruktiounen an der Dockerfile spezifizéiert.

Mir hunn och gekuckt fir eise Container ze optimiséieren andeems Dir eng Layer Feature enthält, déi Ofhängegkeeten an getrennte Schichten extrahéiert, déi um Host gespäichert sinn an eng dënn Uwendungsschicht gëtt bei der Zäitplangzäit an den Ausféierungsmotoren vum Container gelueden.

Dir kënnt all Quellcode fannen am Artikel benotzt op Github .

Kommando Referenz

Hei ass e Resumé vun de Kommandoen déi mir an dësem Artikel benotzt hunn fir eng séier Referenz.

Kontext Clearing:

docker system prune -a

Baut e Containerbild mat engem Dockerfile:

docker build -f <Docker file name> -t <tag> .

Baut Containerbild vun der Quell (ouni Dockerfile):

mvn spring-boot:build-image

View Ofhängegkeet Schichten. Ier Dir d'Applikatioun Jar Datei baut, gitt sécher datt d'Schichtfunktioun am Spring-boot-maven-Plugin aktivéiert ass:

java -Djarmode=layertools -jar application.jar list

Extrait Ofhängegkeet Schichten. Ier Dir d'Applikatioun Jar Datei baut, gitt sécher datt d'Schichtfunktioun am Spring-boot-maven-Plugin aktivéiert ass:

 java -Djarmode=layertools -jar application.jar extract

Eng Lëscht vu Container Biller kucken

docker images

Kuckt op der lénker Säit am Containerbild (vergewëssert Iech datt den Tauchinstrument installéiert ass):

dive <image ID or image tag>

Source: will.com