Стварэнне аптымізаваных вобразаў Docker для прыкладання Spring Boot
Кантэйнеры сталі пераважным сродкам упакоўкі прыкладання з усімі залежнасцямі праграмнага забеспячэння і аперацыйнай сістэмы, а затым дастаўкі іх у розныя асяроддзі.
У гэтым артыкуле разглядаюцца розныя спосабы кантэйнерызацыі прыкладання Spring Boot:
стварэнне выявы Docker з дапамогай файла Docker,
стварэнне выявы OCI з зыходнага кода з дапамогай Cloud-Native Buildpack,
і аптымізацыя выявы падчас выканання шляхам падзелу частак JAR на розныя ўзроўні з дапамогай шматузроўневых інструментаў.
Прыклад кода
Гэты артыкул суправаджаецца прыкладам працоўнага кода на GitHub .
Тэрміналогія кантэйнераў
Мы пачнем з тэрміналогіі кантэйнераў, якая выкарыстоўваецца ў артыкуле:
Выява кантэйнера (Container image): файл вызначанага фармату. Мы канвертуем наша дадатак у вобраз кантэйнера, запусціўшы інструмент зборкі.
кантэйнер: выкананы экзэмпляр вобраза кантэйнера.
Рухавічок кантэйнера (Container engine): працэс-дэман, які адказвае за запуск кантэйнера.
Хост кантэйнера (Container host): хост-кампутар, на якім працуе механізм кантэйнера.
Рэестр кантэйнераў (Container registry): агульнае размяшчэнне, якое выкарыстоўваецца для публікацыі і распаўсюджвання выявы кантэйнера.
Стандарт OCI: Ініцыятыва адкрытага кантэйнера (OCI) – гэта аблегчаная адкрытая структура кіравання, сфармаваная ў рамках Linux Foundation. Спецыфікацыя выяў OCI вызначае галіновыя стандарты для фарматаў выяў кантэйнераў і асяроддзі выканання, каб гарантаваць, што ўсе механізмы кантэйнераў могуць запускаць выявы кантэйнераў, створаныя любой прыладай зборкі.
Каб змясціць прыкладанне ў кантэйнер, мы заключаем наша дадатак у вобраз кантэйнера і публікуем гэты вобраз у агульны рэестр. Асяроддзе выканання кантэйнера здабывае гэтую выяву з рэестра, распакоўвае яго і запускае прыкладанне ўсярэдзіне яго.
Версія 2.3 Spring Boot дае плагіны для стварэння вобразаў OCI.
Докер - Найбольш часта выкарыстоўваецца рэалізацыя кантэйнера, і мы выкарыстоўваем Docker ў нашых прыкладах, таму ўсе наступныя спасылкі на кантэйнер у гэтым артыкуле будуць азначаць Docker.
Пабудова выявы кантэйнера традыцыйным спосабам
Ствараць выявы Docker для прыкладанняў Spring Boot вельмі лёгка, дадаўшы некалькі інструкцый у файл Docker.
Спачатку мы ствараем выкананы файл JAR і, як частка інструкцый файла Docker, капіяваны выкананы файл JAR па-над базавай выявай JRE пасля ўжывання неабходных налад.
Давайце створым наша дадатак Spring на Spring Initializr з залежнасцямі web, lombokи actuator. Мы таксама дадаем rest кантролер, каб даць API з GETметадам.
Стварэнне файла Docker
Затым мы змяшчае гэта дадатак у кантэйнер, дадаючы Dockerfile:
Наш файл Docker змяшчае базавую выяву, з adoptopenjdk, па-над якім мы капіюем наш файл JAR, а затым адчыняны порт, 8080які будзе праслухоўваць запыты.
Зборка прыкладання
Спачатку трэба стварыць дадатак з дапамогай Maven ці Gradle. Тут мы выкарыстоўваем Maven:
mvn clean package
Гэта стварае выкананы JAR-файл прыкладання. Нам трэба пераўтварыць гэты выкананы JAR у выяву Docker для працы ў рухавічку Docker.
Стварэнне выявы кантэйнера
Затым мы змяшчаем гэты выкананы файл JAR у выяву Docker, выканаўшы каманду docker buildз каранёвага каталога праекта, які змяшчае файл Docker, створаны раней:
docker build -t usersignup:v1 .
Мы можам убачыць наша выява ў спісе з дапамогай каманды:
docker images
Вынік выканання вышэйпаказанай каманды ўключае ў сябе наш лад usersignupразам з базавай выявай, adoptopenjdk, указаным у нашым файле Docker.
REPOSITORY TAG SIZE
usersignup v1 249MB
adoptopenjdk 11-jre-hotspot 229MB
Прагляд пластоў ўнутры выявы кантэйнера
Давайце паглядзім на чарку пластоў ўнутры выявы. Мы будзем выкарыстоўваць інструмент dive, каб прагледзець гэтыя пласты:
dive usersignup:v1
Вось частка вынікаў выканання каманды Dive:
Як мы бачым, прыкладны ўзровень складае значную частку памеру выявы. Мы хочам зменшыць памер гэтага пласта ў наступных раздзелах у рамках нашай аптымізацыі.
Стварэнне выявы кантэйнера з дапамогай Buildpack
Зборачныя пакеты (Buildpacks) — гэта агульны тэрмін, які выкарыстоўваецца рознымі прапановамі «Платформа як паслуга» (PAAS) для стварэння выявы кантэйнера з зыходнага кода. Ён быў запушчаны Heroku ў 2011 годзе і з таго часу быў прыняты Cloud Foundry, Google App Engine, Gitlab, Knative і некаторымі іншымі.
Перавага хмарных зборачных пакетаў
Адным з асноўных пераваг выкарыстання Buildpack для стварэння вобразаў з'яўляецца тое, што зменамі канфігурацыі выявы можна кіраваць цэнтралізавана (builder) і распаўсюджваць на ўсе прыкладанні, якія выкарыстоўваюць builder.
Зборачныя пакеты былі цесна звязаны з платформай. Cloud-Native Buildpacks забяспечваюць стандартызацыю паміж платформамі, падтрымліваючы фармат выявы OCI, які гарантуе, што выява можа запускацца рухавічком Docker.
Выкарыстанне плагіна Spring Boot
Убудова Spring Boot стварае выявы OCI з зыходнага кода з дапамогай Buildpack. Выявы ствараюцца з выкарыстаннем bootBuildImageзадачы (Gradle) або spring-boot:build-imageмэты (Maven) і лакальнай усталёўкі Docker.
Мы можам наладзіць імя выявы, неабходнага для адпраўкі ў рэестр Docker, паказаўшы імя ў image tag:
Давайце скарыстаемся Maven для выканання build-imageмэты па стварэнні прыкладання і стварэнню ладу кантэйнера. Цяпер мы не выкарыстоўваем ніякіх файлаў Docker.
З выходных дадзеных мы бачым, што paketo Cloud-Native buildpackвыкарыстоўваецца для стварэння якая працуе выявы OCI. Як і раней, мы можам убачыць выяву, паказаны як выява Docker, выканаўшы каманду:
Далей мы запускаем убудова Jib з дапамогай каманды Maven, каб пабудаваць прыкладанне і стварыць вобраз кантэйнера. Як і раней, тут мы не выкарыстоўваем ніякіх файлаў Docker:
Пасля выканання паказанай вышэй каманды Maven мы атрымліваем наступную выснову:
[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
Выходныя дадзеныя паказваюць, што выява кантэйнера створаны і змешчаны ў рэестр.
Матывацыі і метады стварэння аптымізаваных малюнкаў
У нас ёсць дзве асноўныя прычыны для аптымізацыі:
Proizvoditelnost: у сістэме аркестроўкі кантэйнераў вобраз кантэйнера здабываецца з рэестра вобразаў на хост, на якім запушчаны механізм кантэйнера. Гэты працэс называецца планаваннем. Выманне вобразаў вялікага памеру з рэестра прыводзіць да працяглага часу планавання ў сістэмах аркестроўкі кантэйнераў і працяглага часу зборкі ў канвеерах CI.
бяспеку: выявы вялікага памеру таксама маюць вялікую вобласць для ўразлівасцяў.
Выява Docker складаецца з стэка пластоў, кожны з якіх уяўляе інструкцыю ў нашым Dockerfile. Кожны пласт уяўляе сабой дэльту змен ніжэйлеглага пласта. Калі мы здабываем выяву Docker з рэестра, ён здабываецца пластамі і кэшуецца на хасце.
Spring Boot выкарыстоўвае «тоўсты JAR» у якасці фармату упакоўкі па змаўчанні. Калі мы праглядаем тоўсты JAR, мы бачым, што прыкладанне складае вельмі маленькую частку ўсяго JAR. Гэта частка, якая мяняецца часцей за ўсё. Пакінутая частка складаецца з залежнасцяў Spring Framework.
Формула аптымізацыі сканцэнтравана вакол ізаляцыі дадатку на асобным узроўні ад залежнасцяў Spring Framework.
Пласт залежнасцяў, які фармуе асноўную частку тоўстага JAR-файла, загружаецца толькі адзін раз і кэшуецца ў хост-сістэме.
Толькі тонкі пласт прыкладання выцягваецца падчас абнаўленняў прыкладання і планавання кантэйнераў, як паказана на гэтай дыяграме:
У наступных раздзелах мы разгледзім, як ствараць гэтыя аптымізаваныя вобразы для прыкладання Spring Boot.
Стварэнне аптымізаванай выявы кантэйнера для прыкладання Spring Boot з дапамогай Buildpack
Spring Boot 2.3 падтрымлівае шматузроўневасць шляхам вымання частак тоўстага JAR-файла ў асобныя пласты. Функцыя напластавання па змаўчанні адключаная, і яе неабходна відавочна ўлучыць з дапамогай убудовы Spring Boot Maven:
Мы будзем выкарыстоўваць гэтую канфігурацыю для стварэння нашай выявы кантэйнера спачатку з дапамогай Buildpack, а затым з дапамогай Docker ў наступных раздзелах.
Давайце запусцім build-imageмэта Maven для стварэння выявы кантэйнера:
mvn spring-boot:build-image
Калі мы запусцім Dive, каб убачыць пласты ў выніковым адлюстраванні, мы ўбачым, што ўзровень прыкладання (абведзены чырвоным) нашмат менш у дыяпазоне кілабайт у параўнанні з тым, што мы атрымалі з выкарыстаннем тоўстага фармату JAR:
Стварэнне аптымізаванай выявы кантэйнера для прыкладання Spring Boot з дапамогай Docker
Замест выкарыстання плагіна Maven або Gradle мы таксама можам стварыць шматузроўневую выяву JAR Docker з файлам Docker.
Калі мы выкарыстоўваем Docker, нам трэба выканаць два дадатковых кроку для вымання пластоў і капіяванні іх у канчатковую выяву.
Змесціва атрыманага JAR пасля зборкі з дапамогай Maven з уключанай функцыяй напластавання будзе выглядаць наступным чынам:
У выходных дадзеных адлюстроўваецца дадатковы JAR з імем spring-boot-jarmode-layertoolsи layersfle.idxфайл. Гэты дадатковы JAR-файл дае магчымасць шматузроўневай апрацоўкі, як апісана ў наступным раздзеле.
Выманне залежнасцяў на асобных пластах
Каб прагледзець і атрымаць пласты з нашага шматузроўневага JAR, мы выкарыстоўваем сістэмнае ўласцівасць -Djarmode=layertoolsдля запуску spring-boot-jarmode-layertoolsJAR замест прыкладання:
Выкананне гэтай каманды дае выснову, якая змяшчае даступныя параметры каманды:
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
Выснова паказвае каманды list, extractи helpс helpбыць па змаўчанні. Давайце запусцім каманду з listопцыяй:
java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar list
Мы бачым спіс залежнасцяў, якія можна дадаць як пласты.
Пласты па змаўчанні:
Імя пласта
Змест
dependencies
любая залежнасць, версія якой не змяшчае SNAPSHOT
spring-boot-loader
Класы загрузніка JAR
snapshot-dependencies
любая залежнасць, версія якой змяшчае SNAPSHOT
application
класы прыкладанняў і рэсурсы
Пласты вызначаны ў layers.idxфайле ў тым парадку, у якім яны павінны быць дададзены ў вобраз Docker. Гэтыя пласты кэшуюцца ў хасце пасля першага вымання, паколькі яны не змяняюцца. На хост загружаецца толькі абноўлены ўзровень прыкладання, што адбываецца хутчэй з-за паменшанага памеру .
Пабудова выявы з залежнасцямі, вынятымі ў асобныя пласты
Мы пабудуем фінальную выяву ў два этапы, выкарыстоўваючы метад, званы шматэтапнай зборкай . На першым этапе мы атрымаем залежнасці, а на другім этапе мы скапіюем вынятыя залежнасці ў канчатковы вобраз .
Давайце мадыфікуем наш файл Docker для шматэтапнай зборкі:
# 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"]
Захоўваем гэтую канфігурацыю ў асобным файле - Dockerfile2.
Збіраны выява Docker з дапамогай каманды:
docker build -f Dockerfile2 -t usersignup:v1 .
Пасля выканання гэтай каманды мы атрымліваем такую выснову:
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
Мы бачым, што выява Docker ствараецца з ідэнтыфікатарам малюнка, а затым тэгуецца.
Нарэшце, мы запускаем каманду Dive, як і раней, каб праверыць пласты ўнутры згенераванай выявы Docker. Мы можам паказаць ідэнтыфікатар выявы або тэг у якасці ўваходных дадзеных для каманды Dive:
dive userssignup:v1
Як відаць з выходных дадзеных, узровень, які змяшчае прыкладанне, зараз займае ўсяго 11 КБ, а залежнасці кэшуюцца ў асобных пластах.
Выманне ўнутраных залежнасцяў на асобных пластах
Мы можам дадаткова паменшыць памер ўзроўню прыкладання, здабываючы любыя з нашых карыстацкіх залежнасцяў у асобны ўзровень замест таго, каб пакаваць іх разам з дадаткам, абвясціўшы іх у ymlпадобным файле з імем layers.idx:
У гэтым файле layers.idxмы дадалі наладжваемую залежнасць з імем, io.myorgякія змяшчаюць залежнасці арганізацыі, атрыманыя з агульнага рэпазітара.
Выснова
У гэтым артыкуле мы разгледзелі выкарыстанне Cloud-Native Buildpacks для стварэння выявы кантэйнера непасрэдна з зыходнага кода. Гэта альтэрнатыва выкарыстанню Docker для стварэння выявы кантэйнера звычайным спосабам: спачатку ствараецца тоўсты выкананы файл JAR, а затым пакуецца яго ў выяву кантэйнера, паказаўшы інструкцыі ў файле Docker.
Мы таксама разгледзелі аптымізацыю нашага кантэйнера, уключыўшы функцыю напластавання, якая здабывае залежнасці ў асобныя ўзроўні, якія кэшуюцца на хасце, а тонкі пласт прыкладання загружаецца падчас планавання ў механізмах выканання кантэйнера.
Вы можаце знайсці ўвесь зыходны код, выкарыстаны ў артыкуле на Github .
Даведнік каманд
Вось кароткі выклад каманд, якія мы выкарыстоўвалі ў гэтым артыкуле для хуткага азнаямлення.
Ачыстка кантэксту:
docker system prune -a
Стварэнне выявы кантэйнера з дапамогай файла Docker:
docker build -f <Docker file name> -t <tag> .
Збіраны выява кантэйнера з зыходнага кода (без Dockerfile):
mvn spring-boot:build-image
Прагляд пластоў залежнасцяў. Перад зборкай JAR-файла прыкладання пераканайцеся, што функцыя напластавання ўключана ў spring-boot-maven-plugin:
java -Djarmode=layertools -jar application.jar list
Выманне пластоў залежнасцяў. Перад зборкай JAR-файла прыкладання пераканайцеся, што функцыя напластавання ўключана ў spring-boot-maven-plugin: