Optimizuotų „Docker“ vaizdų kūrimas „Spring Boot“ programai

Konteineriai tapo pageidaujama priemone supakuoti programą su visomis programinės įrangos ir operacinės sistemos priklausomybėmis ir pristatyti jas į skirtingas aplinkas.

Šiame straipsnyje aprašomi įvairūs „Spring Boot“ programos talpinimo būdai:

  • sukurti „Docker“ vaizdą naudojant „Docker“ failą,
  • sukurti OCI vaizdą iš šaltinio naudojant „Cloud-Native Buildpack“,
  • ir vaizdo optimizavimas vykdymo metu, atskiriant JAR dalis į skirtingus sluoksnius naudojant kelių pakopų įrankius.

 Kodo pavyzdys

Prie šio straipsnio pridedamas darbo kodo pavyzdys „GitHub“. .

Konteinerio terminija

Pradėsime nuo straipsnyje naudojamos konteinerio terminijos:

  • Konteinerio vaizdas: konkretaus formato failas. Mes konvertuosime savo programą į konteinerio vaizdą paleisdami kūrimo įrankį.
  • Konteineris: konteinerio vaizdo vykdomasis pavyzdys.
  • Konteinerio variklis: demono procesas, atsakingas už konteinerio paleidimą.
  • Konteinerio šeimininkas: pagrindinis kompiuteris, kuriame veikia konteinerio variklis.
  • Konteinerių registras: bendroji vieta, naudojama sudėtinio rodinio vaizdui paskelbti ir platinti.
  • OCI standartasOpen Container Initiative (OCI) yra lengva, atvira valdymo struktūra, suformuota Linux fonde. OCI vaizdo specifikacija apibrėžia konteinerio vaizdų ir vykdymo laiko formatų pramonės standartus, siekiant užtikrinti, kad visi konteinerių varikliai galėtų paleisti konteinerio vaizdus, ​​sukurtus bet kuriuo kūrimo įrankiu.

Norėdami sudėti programą, supakuojame programą į konteinerio vaizdą ir paskelbiame tą vaizdą bendrame registre. Sudėtinio rodinio vykdymo laikas nuskaito šį vaizdą iš registro, jį išpakuoja ir jame paleidžia programą.

„Spring Boot“ 2.3 versijoje pateikiami papildiniai, skirti kurti OCI vaizdus.

dokininkas yra dažniausiai naudojamas konteinerio diegimas, o savo pavyzdžiuose naudojame „Docker“, todėl visos tolesnės šio straipsnio nuorodos į konteinerį bus susijusios su „Docker“.

Sukurkite konteinerio vaizdą tradiciniu būdu

Sukurti „Spring Boot“ programoms skirtus „Docker“ vaizdus labai paprasta, į „Docker“ failą įtraukus kelias instrukcijas.

Pirmiausia sukuriame vykdomąjį JAR failą ir, vykdydami Docker failo instrukcijas, nukopijuojame vykdomąjį JAR failą ant pagrindinio JRE vaizdo, pritaikę reikiamus nustatymus.

Sukurkime savo pavasario programą Pavasario inicijavimas su priklausomybėmis weblombokи actuator. Taip pat pridedame poilsio valdiklį, kad galėtume teikti API GETmetodas.

Docker failo kūrimas

Tada šią programą sudedame į konteinerius pridėdami Dockerfile:

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

Mūsų Docker faile yra pagrindinis vaizdas iš adoptopenjdk, ant kurio nukopijuojame JAR failą ir atidarome prievadą, 8080kuri išklausys prašymus.

Programos kūrimas

Pirmiausia turite sukurti programą naudodami Maven arba Gradle. Čia mes naudojame Maven:

mvn clean package

Tai sukuria vykdomąjį JAR failą programai. Turime konvertuoti šį vykdomąjį JAR į „Docker“ vaizdą, kad jis veiktų „Docker“ variklyje.

Sukuriamas konteinerio vaizdas

Tada įdėjome šį vykdomąjį JAR failą į Docker vaizdą, vykdydami komandą docker buildiš projekto šakninio katalogo, kuriame yra anksčiau sukurtas Dockerfile:

docker build  -t usersignup:v1 .

Savo vaizdą galime pamatyti sąraše naudodami komandą:

docker images 

Aukščiau pateiktos komandos išvestyje yra mūsų vaizdas usersignupkartu su pagrindiniu vaizdu, adoptopenjdknurodyta mūsų Docker faile.

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

Peržiūrėkite sluoksnius konteinerio vaizde

Pažvelkime į sluoksnių krūvą vaizdo viduje. Mes naudosime įrankis  nardyti norėdami peržiūrėti šiuos sluoksnius:

dive usersignup:v1

Štai dalis komandos Dive išvesties: 

Optimizuotų „Docker“ vaizdų kūrimas „Spring Boot“ programai

Kaip matome, taikymo sluoksnis sudaro didelę vaizdo dydžio dalį. Tolesniuose skyriuose norime sumažinti šio sluoksnio dydį kaip optimizavimo dalį.

Konteinerio vaizdo kūrimas naudojant „Buildpack“.

Surinkimo paketai (Buildpacks) yra bendras terminas, naudojamas įvairiuose platformos kaip paslaugos (PAAS) pasiūlymuose, siekiant sukurti konteinerio vaizdą iš šaltinio kodo. Ją 2011 m. pristatė „Heroku“, o nuo to laiko jį priėmė „Cloud Foundry“, „Google App Engine“, „Gitlab“, „Knative“ ir keletas kitų.

Optimizuotų „Docker“ vaizdų kūrimas „Spring Boot“ programai

Debesų kūrimo paketų pranašumas

Vienas iš pagrindinių „Buildpack“ naudojimo vaizdams kurti pranašumų yra tas Vaizdo konfigūracijos pakeitimus galima valdyti centralizuotai (builder) ir perduoti visoms programoms naudojant kūrimo priemonę.

Konstrukcijos paketai buvo tvirtai sujungti su platforma. „Cloud-Native Buildpacks“ teikia standartizaciją visose platformose, palaikydama OCI vaizdo formatą, kuris užtikrina, kad vaizdą gali paleisti „Docker“ variklis.

„Spring Boot“ papildinio naudojimas

„Spring Boot“ papildinys sukuria OCI vaizdus iš šaltinio, naudodamas „Buildpack“. Vaizdai kuriami naudojant bootBuildImageužduotis (Gradle) arba spring-boot:build-imagetikslai („Maven“) ir vietinis „Docker“ diegimas.

Galime tinkinti vaizdo, reikalingo perkelti į „Docker“ registrą, pavadinimą, nurodydami pavadinimą 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>

Tam naudokime Maven build-imageprogramos kūrimo ir konteinerio vaizdo kūrimo tikslai. Šiuo metu nenaudojame jokių Dockerfiles.

mvn spring-boot:build-image

Rezultatas bus maždaug toks:

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

Iš produkcijos tai matome paketo Cloud-Native buildpacknaudojamas kuriant veikiantį OCI vaizdą. Kaip ir anksčiau, vaizdą galime pamatyti kaip Docker vaizdą, vykdydami komandą:

docker images 

Išvada:

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

Konteinerio vaizdo kūrimas naudojant „Jib“.

„Jib“ yra „Google“ vaizdų kūrimo papildinys, suteikiantis alternatyvų konteinerio vaizdo kūrimo iš šaltinio kodo metodą.

Tinkinkite jib-maven-pluginpom.xml:

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

Tada paleidžiame „Jib“ papildinį naudodami komandą „Maven“, kad sukurtume programą ir sukurtume konteinerio vaizdą. Kaip ir anksčiau, čia nenaudojame jokių Docker failų:

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

Įvykdę aukščiau pateiktą Maven komandą, gauname tokią išvestį:

[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

Išvestis rodo, kad konteinerio vaizdas buvo sukurtas ir įtrauktas į registrą.

Optimizuotų vaizdų kūrimo motyvacijos ir technikos

Turime dvi pagrindines optimizavimo priežastis:

  • Našumas: sudėtinio rodinio orkestravimo sistemoje konteinerio vaizdas nuskaitomas iš vaizdų registro į pagrindinį kompiuterį, kuriame veikia konteinerio variklis. Šis procesas vadinamas planavimu. Ištraukus didelius vaizdus iš registro, konteinerių orkestravimo sistemose užtrunka ilgai, o CI vamzdynuose – ilgas kūrimo laikas.
  • saugumas: didesniuose vaizduose taip pat yra didesnė pažeidžiamumo sritis.

„Docker“ vaizdas susideda iš krūvos sluoksnių, kurių kiekvienas yra mūsų Docker failo instrukcija. Kiekvienas sluoksnis reiškia pagrindinio sluoksnio pokyčių delta. Kai iš registro ištraukiame „Docker“ vaizdą, jis ištraukiamas sluoksniais ir saugomas pagrindinio kompiuterio talpykloje.

„Spring Boot“ naudojimas "riebalų JAR" viduje kaip numatytąjį pakuotės formatą. Kai žiūrime į storą JAR, matome, kad programa sudaro labai nedidelę viso JAR dalį. Tai dalis, kuri dažniausiai keičiasi. Likusią dalį sudaro „Spring Framework“ priklausomybės.

Optimizavimo formulės tikslas – atskirti programą nuo „Spring Framework“ priklausomybių atskiru lygiu.

Priklausomybės sluoksnis, kuris sudaro didžiąją storio JAR failo dalį, atsisiunčiamas tik vieną kartą ir saugomas pagrindinio kompiuterio talpykloje.

Atnaujinus programą ir planuojant sudėtinį rodinį, ištraukiamas tik plonas programos sluoksnis. kaip parodyta šioje diagramoje:

Optimizuotų „Docker“ vaizdų kūrimas „Spring Boot“ programai

Tolesniuose skyriuose apžvelgsime, kaip sukurti šiuos optimizuotus „Spring Boot“ programos vaizdus.

Optimizuoto „Spring Boot“ programos konteinerio vaizdo sukūrimas naudojant „Buildpack“.

„Spring Boot 2.3“ palaiko sluoksniavimą, ištraukdama storo JAR failo dalis į atskirus sluoksnius. Sluoksniavimo funkcija pagal numatytuosius nustatymus yra išjungta ir turi būti aiškiai įjungta naudojant „Spring Boot Maven“ papildinį:

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

Šią konfigūraciją naudosime kurdami konteinerio vaizdą pirmiausia naudodami „Buildpack“, o paskui naudodami „Docker“ kituose skyriuose.

Paleidžiame build-image„Maven“ tikslas kuriant sudėtinio rodinio vaizdą:

mvn spring-boot:build-image

Jei paleisime Dive, kad pamatytume sluoksnius gautame vaizde, pamatytume, kad taikymo sluoksnis (pažymėtas raudonai) yra daug mažesnis kilobaitų diapazone, palyginti su tuo, ką gavome naudodami storą JAR formatą:

Optimizuotų „Docker“ vaizdų kūrimas „Spring Boot“ programai

Optimizuoto „Spring Boot“ programos konteinerio vaizdo sukūrimas naudojant „Docker“.

Užuot naudoję „Maven“ arba „Gradle“ papildinį, taip pat galime sukurti sluoksniuotą „Docker JAR“ vaizdą su „Docker“ failu.

Kai naudojame „Docker“, turime atlikti du papildomus veiksmus, kad ištrauktume sluoksnius ir nukopijuotume juos į galutinį vaizdą.

Sukūrus naudojant Maven, kai įjungtas sluoksniavimas, gauto JAR turinys atrodys taip:

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švestis rodo papildomą JAR pavadinimą spring-boot-jarmode-layertoolsи layersfle.idxfailą. Šis papildomas JAR failas suteikia daugiasluoksnio apdorojimo galimybes, kaip aprašyta kitame skyriuje.

Priklausomybių iš atskirų sluoksnių ištraukimas

Norėdami peržiūrėti ir išgauti sluoksnius iš mūsų daugiasluoksnio JAR, naudojame sistemos ypatybę -Djarmode=layertoolspradžiai spring-boot-jarmode-layertoolsJAR vietoj programos:

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

Vykdant šią komandą gaunama išvestis su galimomis komandos parinktimis:

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

Išvestis rodo komandas listextractи helpс helpbūti numatytasis. Vykdykime komandą su listvariantas:

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

Matome priklausomybių, kurias galima pridėti kaip sluoksnius, sąrašą.

Numatytieji sluoksniai:

Sluoksnio pavadinimas

Turinys

dependencies

bet kokia priklausomybė, kurios versijoje nėra SNAPSHOT

spring-boot-loader

JAR krautuvų klasės

snapshot-dependencies

bet kokia priklausomybė, kurios versijoje yra SNAPSHOT

application

taikomųjų programų klases ir išteklius

Sluoksniai apibrėžti layers.idxfailą tokia tvarka, kokia jie turėtų būti įtraukti į „Docker“ vaizdą. Šie sluoksniai yra talpykloje pagrindiniame kompiuteryje po pirmojo gavimo, nes jie nesikeičia. Į pagrindinį kompiuterį atsisiunčiamas tik atnaujintas programos sluoksnis, kuris yra greitesnis dėl sumažinto dydžio .

Vaizdo kūrimas su priklausomybėmis, išskirtomis į atskirus sluoksnius

Mes sukursime galutinį vaizdą dviem etapais, naudodami metodą, vadinamą kelių etapų surinkimas . Pirmuoju žingsniu išgausime priklausomybes, o antrajame – nukopijuosime ištrauktas priklausomybes į galutinį vaizdą.

Pakeiskime savo „Dockerfile“, kad būtų galima sukurti kelių etapų:

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

Šią konfigūraciją išsaugome atskirame faile - Dockerfile2.

Sukuriame „Docker“ vaizdą naudodami komandą:

docker build -f Dockerfile2 -t usersignup:v1 .

Paleidę šią komandą gauname tokią išvestį:

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

Matome, kad Docker vaizdas sukuriamas su vaizdo ID ir tada pažymimas.

Galiausiai, kaip ir anksčiau, paleidžiame komandą Dive, kad patikrintume sluoksnius sugeneruoto Docker vaizdo viduje. Galime pateikti vaizdo ID arba žymą kaip nardymo komandos įvestį:

dive userssignup:v1

Kaip matote išvestyje, sluoksnis, kuriame yra programa, dabar yra tik 11 KB, o priklausomybės saugomos atskiruose sluoksniuose. 

Optimizuotų „Docker“ vaizdų kūrimas „Spring Boot“ programai

Vidinių priklausomybių nuo atskirų sluoksnių ištraukimas

Galime dar labiau sumažinti taikomosios programos pakopos dydį, išskirdami bet kokias pasirinktines priklausomybes į atskirą pakopą, o ne supakuodami jas kartu su programa, deklaruodami jas ymlpanašus failas pavadintas 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/"

Šiame faile layers.idxpridėjome tinkintą priklausomybę pavadinimu, io.myorgkuriame yra organizacijos priklausomybės, gautos iš bendrinamos saugyklos.

Produkcija

Šiame straipsnyje apžvelgėme, kaip naudoti „Cloud-Native Buildpacks“, kad sukurtume konteinerio vaizdą tiesiai iš šaltinio kodo. Tai alternatyva „Docker“ naudojimui kuriant sudėtinio rodinio vaizdą įprastu būdu: pirmiausia sukurkite storą vykdomąjį JAR failą, o tada supakuokite jį į konteinerio vaizdą, nurodydami instrukcijas Docker faile.

Taip pat siekėme optimizuoti savo sudėtinį rodinį, įgalindami sluoksniavimo funkciją, kuri ištraukia priklausomybes į atskirus sluoksnius, kurie saugomi pagrindinio kompiuterio talpykloje, o plonas programos sluoksnis įkeliamas planuojant sudėtinio rodinio vykdymo variklius.

Visą straipsnyje naudojamą šaltinio kodą galite rasti adresu GitHub .

Komandos nuoroda

Štai trumpas šiame straipsnyje naudotų komandų aprašymas.

Konteksto išvalymas:

docker system prune -a

Sudėtinio rodinio vaizdo kūrimas naudojant Docker failą:

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

Sukuriame konteinerio vaizdą iš šaltinio kodo (be Dockerfile):

mvn spring-boot:build-image

Peržiūrėkite priklausomybės sluoksnius. Prieš kurdami programos JAR failą, įsitikinkite, kad sluoksniavimo funkcija įjungta Spring-boot-maven-plugin:

java -Djarmode=layertools -jar application.jar list

Priklausomybės sluoksnių ištraukimas. Prieš kurdami programos JAR failą, įsitikinkite, kad sluoksniavimo funkcija įjungta Spring-boot-maven-plugin:

 java -Djarmode=layertools -jar application.jar extract

Peržiūrėkite sudėtinio rodinio vaizdų sąrašą

docker images

Vaizdas kairėje konteinerio vaizdo viduje (įsitikinkite, kad nardymo įrankis yra įdiegtas):

dive <image ID or image tag>

Šaltinis: www.habr.com