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 standartas: Open 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 web, lombokи actuator. Taip pat pridedame poilsio valdiklį, kad galėtume teikti API GETmetodas.
Docker failo kūrimas
Tada šią programą sudedame į konteinerius pridėdami Dockerfile:
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:
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ų.
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:
Iš produkcijos tai matome paketo Cloud-Native buildpacknaudojamas kuriant veikiantį OCI vaizdą. Kaip ir anksčiau, vaizdą galime pamatyti kaip Docker vaizdą, vykdydami komandą:
Tada paleidžiame „Jib“ papildinį naudodami komandą „Maven“, kad sukurtume programą ir sukurtume konteinerio vaizdą. Kaip ir anksčiau, čia nenaudojame jokių Docker failų:
Į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:
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į:
Š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ą:
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:
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:
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 list, extractи helpс helpbūti numatytasis. Vykdykime komandą su listvariantas:
java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar list
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.
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:
Š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: