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 .
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: — 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.
— 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 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 packageTo 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 229MBPregled slojeva unutar slike kontejnera
Pogledajmo složenu strukturu slojeva unutar slike. Uzećemo da bismo pregledali te slojeve:
dive usersignup:v1Evo dela rezultata izvršenja dive komande:

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
() - 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.

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}:v1Iskoristimo Maven za izvršavanje build-imagecilja za izgradnju aplikacije i stvaranje slike kontejnera. Trenutno ne koristimo nikakve Docker datoteke.
mvn spring-boot:build-imageRezultat ć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 257MBStvaranje 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.2Zatim 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:v1Nakon 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šenoIzlaz 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 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:

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
trueOva 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-imageAko 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 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.idxU 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.jarIzvrš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 commandIzlaz pokazuje komande list, extracti helps helptrebaju biti po defaultu. Pokrenimo komandu s listopcijom:
java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar listdependencies
spring-boot-loader
snapshot-dependencies
applicationVidimo 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 . 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:v1Vidimo 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:v1Kao što se može vidjeti iz izlaza, sloj koji sadrži aplikaciju sada zauzima samo 11 KB, a ovisnosti se predmemoriraju u odvojenim slojevima.

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 .
Vodič za naredbe
Evo sažetak naredbi koje smo koristili u ovom članku za brzo upoznavanje.
Čišćenje konteksta:
docker system prune -aIzrada kontejnerske slike pomoću Docker datoteke:
docker build -f -t .Skupljamo kontejnersku sliku iz izvornog koda (bez Dockerfilea):
mvn spring-boot:build-imagePregled 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 listIzvlač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 extractPregled popisa kontejnerskih slika
docker imagesPregled slojeva unutar kontejnerske slike (provjerite je li alat za uranjanje instaliran):
diveIzvor: habr.com
