Izrada optimiziranih Docker slika za Spring Boot aplikaciju

Kontejneri su postali preferirani način pakiranja aplikacije sa svim zavisnostima softvera i operativnog sistema, a zatim njihove isporuke u različite okruženja.

U ovom članku razmatramo različite metode kontejnerizacije Spring Boot aplikacije:

  • kreiranje Docker slike pomoću Docker datoteke,
  • kreiranje OCI slike iz izvornog koda pomoću Cloud-Native Buildpack,
  • i optimizacija slike tokom izvršavanja deljenjem JAR delova na različite slojeve koristeći višesložne alate.

 Primer koda

Ovaj članak dolazi s primerom radnog koda na GitHubu .

Terminologija kontejnera

Započinjemo s terminologijom kontejnera korišćenom u članku:

  • Slika kontejnera (Container image): datoteka određenog formata. Pretvaramo našu aplikaciju u sliku kontejnera pokretanjem alata za izgradnju.
  • Kontejner: izvršni primer slike kontejnera.
  • Kontejnerski motor (Container engine): proces-demon koji je odgovoran za pokretanje kontejnera.
  • Kontejner domaćin (Container host): računar domaćin na kojem radi kontejnerski mehanizam.
  • Kontejnerski registar (Container registry): zajednička lokacija koja se koristi za objavljivanje i deljenje slike kontejnera.
  • Standard OCI: Open Container Initiative (OCI) — to je olakšana otvorena struktura upravljanja formirana unutar Linux Foundation. Specifikacija OCI slika definiše industrijske standarde za formate slika kontejnera i okruženja za izvršavanje kako bi se osiguralo da svi mehanizmi kontejnera mogu pokretati slike kontejnera kreirane bilo kojim alatom za izgradnju.

Da bismo stavili aplikaciju u kontejner, obavijamo našu aplikaciju u sliku kontejnera i objavljujemo ovu sliku u zajedničkom registru. Okruženje izvršavanja kontejnera preuzima ovu sliku iz registra, rasklapajući je i pokrećući aplikaciju unutar nje.

Verzija 2.3 Spring Boot pruža dodatke za kreiranje OCI slika.

Docker — najčešće korišćena implementacija kontejnera, i koristimo Docker u našim primerima, tako da će sve daljnje reference na kontejner u ovom članku značiti Docker.

Kreiranje slike kontejnera na tradicionalni način

Veoma je lako kreirati Docker slike za Spring Boot aplikacije dodavanjem nekoliko instrukcija u Docker datoteku.

Prvo stvaramo JAR izvršni fajl i, kao deo Docker fajl instrukcija, kopiramo JAR izvršni fajl preko osnovne JRE slike nakon primene potrebnih postavki.

Hajde da napravimo našu Spring aplikaciju na Spring Initializr s zavisnostima web, lomboki actuator. Takođe dodajemo rest kontroler da bismo omogućili API sa GETmetodom.

Kreiranje Docker fajla

Zatim stavljamo ovu aplikaciju u kontejner, dodajući Dockerfile:

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

Naš Docker fajl sadrži osnovnu sliku, iz adoptopenjdk, preko koje kopiramo naš JAR fajl, a zatim otvaramo port, 8080koji će slušati zahtjeve.

Izgradnja aplikacije

Prvo treba izgraditi aplikaciju koristeći Maven ili Gradle. Ovde koristimo Maven:

mvn clean package

To stvara izvršni JAR fajl aplikacije. Moramo pretvoriti ovaj izvršni JAR u Docker sliku za rad u Docker motoru.

Kreiranje kontejnerske slike

Zatim stavljamo ovaj JAR izvršni fajl u Docker sliku izvršavanjem komande docker buildiz korenskog direktorijuma projekta, koji sadrži prethodno kreirani Docker fajl:

docker build -t usersignup:v1 .

Možemo da vidimo našu sliku na listi koristeći komandu:

docker images 

Rezultat izvršenja gorenavedene komande uključuje našu sliku usersignupzajedno sa osnovnom slikom, adoptopenjdk, koja je navedena u našem Docker fajlu.

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

Pregled slojeva unutar slike kontejnera

Pogledajmo složenu strukturu slojeva unutar slike. Uzećemo alat  dive, da bismo pregledali te slojeve:

dive usersignup:v1

Evo dela rezultata izvršenja dive komande: 

Izrada optimiziranih Docker slika za Spring Boot aplikaciju

Kao što vidimo, aplikativni nivo zauzima značajan deo veličine slike. Želimo smanjiti veličinu ovog sloja u sledećim segmentima kao deo naše optimizacije.

Kreiranje kontejnerske slike pomoću Buildpack-a

Buildpack-ovi (Buildpacks) - to je opšti termin korišćen u različitim „Platforma kao usluga“ (PAAS) ponudama za kreiranje kontejnerske slike iz izvornog koda. Pokrenuo ga je Heroku 2011. godine i od tada su ga prihvatili Cloud Foundry, Google App Engine, Gitlab, Knative i mnogi drugi.

Izrada optimiziranih Docker slika za Spring Boot aplikaciju

Prednost oblaka Buildpack-a

Jedna od ključnih prednosti korišćenja Buildpack-a za kreiranje slika je to što S konfiguracijama slika moguće je upravljati centralizirano (builder) i distribuirati ih na sve aplikacije koje koriste builder.

Gradbeni paketi su bili usko povezani s platformom. Cloud-Native Buildpacks osigurava standardizaciju između platformi, podržavajući OCI format slika koji jamči da se slika može pokretati na Docker motoru.

Korištenje Spring Boot plugina

Spring Boot plugin stvara OCI slike iz izvornog koda pomoću Buildpacka. Slike se stvaraju koristeći bootBuildImagezadaće (Gradle) ili spring-boot:build-imageciljevi (Maven) i lokalno instaliran Docker.

Možemo postaviti ime slike potrebne za slanje u Docker registru tako da navedemo ime u image tag:

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

Iskoristimo Maven za izvršavanje build-imagecilja za izgradnju aplikacije i stvaranje slike kontejnera. Trenutno ne koristimo nikakve Docker datoteke.

mvn spring-boot:build-image

Rezultat će otprilike izgledati ovako:

[INFO] --- spring-boot-maven-plugin:2.3.3.RELEASE:build-image (default-cli) @ usersignup ---
[INFO] Gradnja slike 'docker.io/pratikdas/usersignup:v1'
[INFO] 
[INFO]  > Preuzimanje builder slike 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 0%
.
.
.. [creator]     Dodavanje oznake 'org.springframework.boot.version'
.. [creator]     *** Slike (c311fe74ec73):
.. [creator]           docker.io/pratikdas/usersignup:v1
[INFO] 
[INFO] Uspješno izgrađena slika 'docker.io/pratikdas/usersignup:v1'

Iz izlaznih podataka vidimo da paketo Cloud-Native buildpackse koristi za stvaranje funkcionalne OCI slike. Kao i prije, možemo vidjeti sliku navedenu kao Docker sliku izvršavanjem naredbe:

docker images 

Izvještaj:

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

Stvaranje slike kontejnera pomoću Jiba

Jib je plugin za izgradnju slika od Googlea koji pruža alternativnu metodu za stvaranje slike kontejnera iz izvornog koda.

Konfiguriramo jib-maven-pluginu pom.xml:

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

Zatim pokrećemo Jib plugin pomoću Maven naredbe kako bismo izgradili aplikaciju i stvorili sliku kontejnera. Kao i prije, ovdje ne koristimo nikakve Docker datoteke:

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

Nakon izvršavanja gornje Maven naredbe, dobivamo sljedeći izlaz:

[INFO] Kontenjerizacija aplikacije na pratikdas/usersignup:v1...
.
.
[INFO] Ulazna tačka kontenjera postavljena na [java, -cp, /app/resources:/app/classes:/app/libs/*, io.pratik.users.UsersignupApplication]
[INFO] 
[INFO] Izgrađen i poslan imidž kao pratikdas/usersignup:v1
[INFO] Izvršavanje zadataka:
[INFO] [==============================] 100.0% potpuno završeno

Izlaz pokazuje da je imidž kontenjera kreiran i pohranjen u registar.

Motivacije i metode za kreiranje optimiziranih imidža

Imamo dva glavna razloga za optimizaciju:

  • Performanse: u sistemu orkestracije kontejnera, imidž kontejnera se preuzima iz registra imidža na hostu na kojem se pokreće mehanizam kontejnera. Ovaj proces se naziva planiranje. Preuzimanje imidža velike veličine iz registra dovodi do dužeg vremena planiranja u sistemima orkestracije kontejnera i dugog vremena izgradnje u CI cevovodima.
  • Sigurnost: imidži velike veličine takođe imaju veće područje za ranjivosti.

Imidž Docker-a sastoji se od stoga slojeva, od kojih svaki predstavlja nalog u našem Dockerfile-u. Svaki sloj predstavlja delta promena donjeg sloja. Kada preuzmemo imidž Docker-a iz registra, on se preuzima slojevito i kešira na hostu.

Spring Boot koristi "debeli JAR" kao podrazumevani format pakovanja. Kada gledamo debeli JAR, vidimo da aplikacija čini vrlo mali deo celokupnog JAR-a. To je deo koji se najčešće menja. Ostatak se sastoji od zavisnosti Spring Framework-a.

Formula optimizacije fokusira se na izolaciju aplikacije na zasebnom nivou od zavisnosti Spring Framework-a.

Sloj zavisnosti, koji čini većinu debelog JAR fajla, se učitava samo jednom i kešira u sistemu hosta.

Samo tanki sloj aplikacije se povlači tokom ažuriranja aplikacije i planiranja kontejnera, kao što je prikazano na ovom dijagramu:

Izrada optimiziranih Docker slika za Spring Boot aplikaciju

U sledećim odeljcima razmotrićemo kako kreirati ove optimizovane imidže za aplikaciju Spring Boot.

Kreiranje optimizovanog imidža kontejnera za aplikaciju Spring Boot pomoću Buildpack-a

Spring Boot 2.3 podržava višeslojnost preuzimanjem delova debelog JAR fajla u posebne slojeve. Funkcija naslojavanja je po defaultu isključena i mora biti eksplicitno uključena pomoću Spring Boot Maven plugina:

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

Ova konfiguracija će se koristiti za izradu našeg kontejnerskog imidža prvo putem Buildpack-a, a zatim putem Dockera u sljedećim odjeljcima.

Pokrenimo build-imageMaven cilj za izradu kontejnerskog imidža:

mvn spring-boot:build-image

Ako pokrenemo Dive da bismo vidjeli slojeve u rezultantnom imidžu, vidjet ćemo da je sloj aplikacije (označen crvenom) znatno manji, u rasponu kilobajta, u usporedbi s onim što smo dobili korištenjem debelog JAR formata:

Izrada optimiziranih Docker slika za Spring Boot aplikaciju

Izrada optimiziranog kontejnerskog imidža za Spring Boot aplikaciju pomoću Dockera

Umjesto korištenja Maven ili Gradle plugina, također možemo stvoriti višeslojni JAR Docker imidž s Docker fajlom.

Kada koristimo Docker, trebamo izvesti dva dodatna koraka za vađenje slojeva i kopiranje u konačni imidž.

Sadržaj rezultantnog JAR-a nakon izgradnje pomoću Maven-a s uključenim slojevanjem izgledat će ovako:

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

U izlaznim podacima se prikazuje dodatni JAR pod nazivom spring-boot-jarmode-layertoolsi layersfle.idxfajl. Ovaj dodatni JAR fajl omogućuje višeslojnu obradu, kako je opisano u sljedećem odjeljku.

Vađenje zavisnosti u odvojenim slojevima

Da bismo pregledali i izdvojili slojeve iz našeg višeslojnog JAR-a, koristimo sistemsku osobinu -Djarmode=layertoolsza pokretanje spring-boot-jarmode-layertoolsJAR umjesto aplikacije:

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

Izvršavanje ove komande daje izlaz koji sadrži dostupne opcije komande:

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

Izlaz pokazuje komande list, extracti helps helptrebaju biti po defaultu. Pokrenimo komandu s listopcijom:

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

Vidimo popis zavisnosti koje se mogu dodati kao slojevi.

Slojevi po defaultu:

Ime sloja

Sadržaj

zavisnosti

svaka zavisnost čija verzija ne sadrži SNAPSHOT

spring-boot-loader

Klase učitavača JAR-a

snapshot-dependencies

svaka zavisnost čija verzija sadrži SNAPSHOT

application

klase aplikacija i resursi

Slojevi su definirani u layers.idxdatoteci u redoslijedu u kojem trebaju biti dodani u Docker sliku. Ovi se slojevi predmemoriraju na hostu nakon prvog preuzimanja, jer se ne mijenjaju. Na host se učitava samo ažurirani sloj aplikacije, što se događa brže zbog smanjene veličine .

Izgradnja slike s ovisnostima koje se preuzimaju u odvojene slojeve

Izgradit ćemo finalnu sliku u dva koraka, koristeći metodu nazvanu višestepena izgradnja . U prvom koraku preuzet ćemo ovisnosti, a u drugom koraku kopirati preuzete ovisnosti u konačnu sliku.

Modificirajmo naš Docker datoteku za višestepenu izgradnju:

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

Spremamo ovu konfiguraciju u odvojenu datoteku — Dockerfile2.

Gradimo Docker sliku koristeći naredbu:

docker build -f Dockerfile2 -t usersignup:v1 .

Nakon izvršavanja ove naredbe dobivamo sljedeći izlaz:

Slanje konteksta izgradnje Docker demonu  20.41MB
Korak 1/12 : FROM adoptopenjdk:14-jre-hotspot as builder
14-jre-hotspot: Preuzimanje iz library/adoptopenjdk
.
.
Uspješno izgrađeno a9ebf6970841
Uspješno označeno userssignup:v1

Vidimo da se Docker slika stvara s identifikatorom slike, a zatim se označava.

Na kraju pokrećemo naredbu Dive, kao i prije, da provjerimo slojeve unutar generirane Docker slike. Možemo navesti identifikator slike ili oznaku kao ulaz za naredbu Dive:

dive userssignup:v1

Kao što se može vidjeti iz izlaza, sloj koji sadrži aplikaciju sada zauzima samo 11 KB, a ovisnosti se predmemoriraju u odvojenim slojevima. 

Izrada optimiziranih Docker slika za Spring Boot aplikaciju

Preuzimanje unutarnjih ovisnosti u odvojenim slojevima

Dodatno možemo smanjiti veličinu sloja aplikacije, preuzimajući sve naše korisničke ovisnosti u odvojenom sloju umjesto da ih pakiramo zajedno s aplikacijom, kao što je deklarirano u ymlsličnom dokumentu pod nazivom 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/"

U ovom dokumentu layers.idxdodali smo prilagođenu ovisnost pod nazivom, io.myorgsadrži ovisnosti organizacije preuzete iz zajedničkog repozitorija.

Izvještaj

U ovom članku razmatramo korištenje Cloud-Native Buildpacksa za izradu kontejnerske slike izravno iz izvornog koda. To je alternativa korištenju Dockera za izradu kontejnerske slike na uobičajen način: prvo se stvara debela izvršna datoteka JAR, a zatim se pakira u kontejnersku sliku, navodeći upute u Docker datoteci.

Također smo razmatrali optimizaciju našeg kontejnera uključivanjem funkcije slojevitosti, koja izvlači ovisnosti u zasebne slojeve koji se keširaju na hostu, dok se tanki sloj aplikacije učitava tijekom planiranja u mehanizmima izvođenja kontejnera.

Cijeli izvorni kod korišten u članku možete pronaći na Github .

Vodič za naredbe

Evo sažetak naredbi koje smo koristili u ovom članku za brzo upoznavanje.

Čišćenje konteksta:

docker system prune -a

Izrada kontejnerske slike pomoću Docker datoteke:

docker build -f  -t  .

Skupljamo kontejnersku sliku iz izvornog koda (bez Dockerfilea):

mvn spring-boot:build-image

Pregled slojeva ovisnosti. Prije izrade JAR datoteke aplikacije, provjerite je li funkcija slojevitosti uključena u spring-boot-maven-plugin:

java -Djarmode=layertools -jar application.jar list

Izvlačenje slojeva ovisnosti. Prije izrade JAR datoteke aplikacije, provjerite je li funkcija slojevitosti uključena u spring-boot-maven-plugin:

 java -Djarmode=layertools -jar application.jar extract

Pregled popisa kontejnerskih slika

docker images

Pregled slojeva unutar kontejnerske slike (provjerite je li alat za uranjanje instaliran):

dive

Izvor: habr.com

Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere 🔥 Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere | ProHoster