A konténerek lettek az egyik előnyben részesített eszköz az alkalmazások becsomagolására, annak összes szoftver- és operációs rendszer-függőségével együtt, majd azokat különböző környezetekbe szállítják.
Ez a cikk a Spring Boot alkalmazás konténerbe helyezésének különböző módjait ismerteti:
Docker-kép létrehozása Docker-fájl segítségével,
OCI-kép létrehozása a forrásból a Cloud-Native Buildpack használatával,
és futásidejű képoptimalizálás a JAR részeinek különböző rétegekre történő szétválasztásával többrétegű eszközök segítségével.
Kezdjük a cikkben használt konténerterminológiával:
Konténer kép: meghatározott formátumú fájl. Alkalmazásunkat konténerképpé alakítjuk a build eszköz futtatásával.
Konténer: A tárolókép végrehajtható példánya.
Konténer motor: A tároló futtatásáért felelős démon folyamat.
Konténergazda: Az a gazdagép, amelyen a tárolómotor fut.
Tároló nyilvántartás: A tárolókép közzétételéhez és terjesztéséhez használt általános hely.
OCI szabvány: Open Container Initiative (OCI) egy könnyű, nyitott irányítási struktúra, amelyet a Linux Foundation keretein belül alakítottak ki. Az OCI Image Specification iparági szabványokat határoz meg a konténerkép- és futásidejű formátumokra, hogy biztosítsa, hogy minden konténermotor futtatni tudja a bármilyen összeállítási eszközzel létrehozott konténerképeket.
Egy alkalmazás konténerbe helyezéséhez az alkalmazásunkat egy tárolóképbe csomagoljuk, és közzétesszük a képet egy megosztott beállításjegyzékben. A tároló futtatókörnyezete lekéri ezt a képfájlt a rendszerleíró adatbázisból, kicsomagolja, és futtatja benne az alkalmazást.
A Spring Boot 2.3-as verziója bővítményeket biztosít az OCI-képek létrehozásához.
Dokkmunkás a leggyakrabban használt tároló-megvalósítás, és példáinkban a Dockert használjuk, így ebben a cikkben minden további tárolóreferencia a Dockerre fog hivatkozni.
Konténerimázs felépítése hagyományos módon
A Spring Boot alkalmazásokhoz való Docker-képek létrehozása nagyon egyszerű, ha néhány utasítást ad hozzá a Docker-fájlhoz.
Először létrehozunk egy végrehajtható JAR-fájlt, és a Docker-fájl utasításainak részeként a végrehajtható JAR-fájlt a szükséges beállítások alkalmazása után az alap JRE-kép tetejére másoljuk.
Hozzuk létre a tavaszi alkalmazásunkat Tavaszi inicializálás függőségekkel web, lombokи actuator. Hozzáadunk egy pihenővezérlőt is, amellyel API-t biztosítunk GETmódszer.
Dockerfile létrehozása
Ezután hozzáadásával konténerbe helyezzük ezt az alkalmazást Dockerfile:
A Docker fájlunk egy alapképet tartalmaz adoptopenjdk, amelyre másoljuk a JAR fájlunkat, majd megnyitjuk a portot, 8080amely meghallgatja a kéréseket.
Az alkalmazás felépítése
Először létre kell hoznia egy alkalmazást a Maven vagy a Gradle segítségével. Itt a Mavent használjuk:
mvn clean package
Ezzel létrehoz egy végrehajtható JAR fájlt az alkalmazáshoz. Ezt a végrehajtható JAR-t Docker-képpé kell konvertálnunk, hogy a Docker-motoron futhasson.
Tárolókép létrehozása
Ezután ezt a végrehajtható JAR-fájlt a Docker-képfájlba helyezzük a parancs futtatásával docker builda korábban létrehozott Docker-fájlt tartalmazó projekt gyökérkönyvtárából:
docker build -t usersignup:v1 .
Képünket a listában a következő paranccsal láthatjuk:
docker images
A fenti parancs kimenete tartalmazza a képünket usersignupaz alapképpel együtt, adoptopenjdka Docker fájlunkban megadott.
REPOSITORY TAG SIZE
usersignup v1 249MB
adoptopenjdk 11-jre-hotspot 229MB
Rétegek megtekintése egy tárolóképen belül
Nézzük meg a képen belüli rétegek halmazát. Használni fogjuk szerszám merülés a rétegek megtekintéséhez:
dive usersignup:v1
Íme egy része a Dive parancs kimenetének:
Amint látjuk, az alkalmazási réteg a képméret jelentős részét teszi ki. Ennek a rétegnek a méretét szeretnénk csökkenteni az optimalizálás részeként a következő szakaszokban.
Tárolókép létrehozása a Buildpack használatával
Összeszerelési csomagok (Építőcsomagok) egy általános kifejezés, amelyet a Platform as a Service (PAAS) különféle ajánlatai használnak a forráskódból konténerkép létrehozására. A Heroku 2011-ben indította el, és azóta a Cloud Foundry, a Google App Engine, a Gitlab, a Knative és még sokan mások is elfogadták.
A felhőalapú csomagok előnyei
A Buildpack képek készítésére való használatának egyik fő előnye az A képkonfiguráció módosításai központilag (builder) kezelhetők, és a builder segítségével minden alkalmazásra továbbíthatók.
Az építési csomagok szorosan összekapcsolódtak a platformmal. A Cloud-Native Buildpacks szabványosítást biztosít a platformok között az OCI képformátum támogatásával, amely biztosítja, hogy a kép futtatható legyen a Docker motorral.
A Spring Boot bővítmény használata
A Spring Boot beépülő modul OCI-képeket készít a forrásból a Buildpack használatával. A képek felhasználásával készülnek bootBuildImagefeladatok (Gradle) ill spring-boot:build-imagecélok (Maven) és a helyi Docker telepítés.
Testreszabhatjuk a Docker rendszerleíró adatbázisba küldéshez szükséges kép nevét a név megadásával image tag:
A kimenetből ezt látjuk paketo Cloud-Native buildpackműködő OCI-kép létrehozására szolgál. Mint korábban, a képet Docker-képként láthatjuk a parancs futtatásával:
Ezután a Maven paranccsal futtatjuk a Jib beépülő modult az alkalmazás elkészítéséhez és egy tárolókép létrehozásához. Mint korábban, itt sem használunk Docker-fájlokat:
A fenti Maven parancs végrehajtása után a következő kimenetet kapjuk:
[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
A kimenet azt mutatja, hogy a tárolókép létrejött és el lett helyezve a rendszerleíró adatbázisban.
Optimalizált képek készítésének motivációi és technikái
Két fő okunk van az optimalizálás mellett:
termelékenység: Egy konténer-rendezési rendszerben a rendszer egy tárolóképet kér le a rendszerkép-nyilvántartásból a tárolómotort futtató gazdagépre. Ezt a folyamatot tervezésnek nevezzük. A rendszerleíró adatbázisból történő nagyméretű képek lehívása hosszú ütemezési időt eredményez a konténer-hangszerelési rendszerekben, és hosszú felépítési időt a CI-folyamatokban.
biztonság: A nagyobb képeken nagyobb a sebezhetőségi terület is.
A Docker-kép rétegek kötegéből áll, amelyek mindegyike egy utasítást jelent a Docker-fájlunkban. Minden réteg az alatta lévő réteg változásainak deltáját képviseli. Amikor kihúzunk egy Docker-képet a rendszerleíró adatbázisból, az rétegenként lekerül, és a gazdagépen tárolódik.
Spring Boot használat "fat JAR" be mint az alapértelmezett csomagolási formátum. Ha megnézzük a vastag JAR-t, azt látjuk, hogy az alkalmazás a teljes JAR nagyon kis részét teszi ki. Ez az a rész, amely leggyakrabban változik. A fennmaradó rész a tavaszi keretrendszer függőségeiből áll.
Az optimalizálási képlet az alkalmazásnak a Spring Framework függőségektől való elkülönítésére összpontosít.
A függőségi réteg, amely a vastag JAR-fájl nagy részét képezi, csak egyszer töltődik le, és gyorsítótárazódik a gazdagépen.
Az alkalmazásfrissítések és a tárolóütemezés során az alkalmazásnak csak egy vékony rétege kerül kihúzásra. amint az ezen a diagramon látható:
A következő szakaszokban megvizsgáljuk, hogyan lehet létrehozni ezeket az optimalizált képeket egy Spring Boot alkalmazáshoz.
Optimalizált tárolókép létrehozása egy Spring Boot alkalmazáshoz a Buildpack használatával
A Spring Boot 2.3 támogatja a rétegezést azáltal, hogy egy vastag JAR-fájl egyes részeit külön rétegekbe bontja ki. A rétegezési funkció alapértelmezés szerint le van tiltva, és kifejezetten engedélyezni kell a Spring Boot Maven beépülő modul segítségével:
Ezt a konfigurációt fogjuk használni a konténerkép elkészítéséhez először a Buildpack, majd a Docker segítségével a következő szakaszokban.
Indítsuk el build-imageMaven cél a tárolókép létrehozásához:
mvn spring-boot:build-image
Ha a Dive-t futtatjuk, hogy lássuk a rétegeket az eredményül kapott képen, láthatjuk, hogy az alkalmazási réteg (pirossal körvonalazva) sokkal kisebb a kilobyte-os tartományban ahhoz képest, amit a fat JAR formátum használatával kaptunk:
Optimalizált tárolókép létrehozása egy Spring Boot alkalmazáshoz a Docker segítségével
Maven vagy Gradle beépülő modul használata helyett Docker fájllal is létrehozhatunk rétegzett Docker JAR képet.
Amikor a Dockert használjuk, két további lépést kell végrehajtanunk a rétegek kibontásához és a végső képbe másolásához.
Az eredményül kapott JAR tartalma a Maven használatával történő felépítést követően a rétegezés engedélyezésével így fog kinézni:
A kimeneten egy további JAR látható spring-boot-jarmode-layertoolsи layersfle.idxfájlt. Ez a kiegészítő JAR-fájl többrétegű feldolgozási képességeket biztosít a következő részben leírtak szerint.
Függőségek kinyerése az egyes rétegekből
A réteges JAR rétegek megtekintéséhez és kinyeréséhez a rendszertulajdonságot használjuk -Djarmode=layertoolskezdetnek spring-boot-jarmode-layertoolsJAR alkalmazás helyett:
A parancs futtatása a rendelkezésre álló parancsbeállításokat tartalmazó kimenetet eredményez:
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
A kimenet a parancsokat mutatja list, extractи helpс helplegyen az alapértelmezett. Futtassuk a parancsot listválasztási lehetőség:
java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar list
Látjuk a fóliákként hozzáadható függőségek listáját.
Alapértelmezett rétegek:
Réteg neve
Tartalom
dependencies
minden olyan függőséget, amelynek verziója nem tartalmazza a SNAPSHOT-ot
spring-boot-loader
JAR Loader osztályok
snapshot-dependencies
minden olyan függőséget, amelynek verziója tartalmazza a SNAPSHOT-ot
application
alkalmazási osztályok és források
A rétegek a következőben vannak meghatározva layers.idxfájlokat abban a sorrendben, ahogyan hozzá kell adni őket a Docker-képhez. Ezek a rétegek az első lekérés után gyorsítótárban vannak a gazdagépben, mert nem változnak. Csak a frissített alkalmazásréteg töltődik le a gazdagépre, ami a csökkentett méret miatt gyorsabb .
Kép felépítése külön rétegekbe bontott függőségekből
A végső képet két lépésben építjük fel az úgynevezett módszerrel többlépcsős összeszerelés . Első lépésben kibontjuk a függőségeket, a második lépésben pedig a kinyert függőségeket bemásoljuk a végső képbe.
Módosítsuk a Docker-fájlunkat egy többlépcsős összeállításhoz:
# 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"]
Ezt a konfigurációt egy külön fájlba mentjük - Dockerfile2.
A Docker-képet a következő paranccsal építjük fel:
docker build -f Dockerfile2 -t usersignup:v1 .
A parancs futtatása után a következő kimenetet kapjuk:
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
Láthatjuk, hogy egy Docker-kép létrejön egy képazonosítóval, majd megcímkézi.
Végül a korábbiak szerint futtatjuk a Dive parancsot, hogy megvizsgáljuk a létrehozott Docker-kép rétegeit. Megadhatunk egy képazonosítót vagy címkét a Dive parancs bemeneteként:
dive userssignup:v1
Amint a kimeneten látható, az alkalmazást tartalmazó réteg már csak 11 KB, és a függőségek külön rétegekben vannak gyorsítótárazva.
Belső függőségek kinyerése az egyes rétegekről
Tovább csökkenthetjük az alkalmazásszint méretét, ha bármelyik egyéni függőségünket külön rétegbe bontjuk, ahelyett, hogy az alkalmazásba csomagolnák, deklarálva ymlhasonló nevű fájl layers.idx:
Ebben a fájlban layers.idxhozzáadtunk egy egyéni függőséget, melynek neve io.myorgamely egy megosztott tárolóból lekért szervezeti függőséget tartalmaz.
Teljesítmény
Ebben a cikkben megvizsgáltuk, hogy a Cloud-Native Buildpacks segítségével közvetlenül a forráskódból hozhatunk létre konténerképet. Ez egy alternatíva a Docker használatához egy tárolókép létrehozásához a szokásos módon: először hozzon létre egy vastag végrehajtható JAR-fájlt, majd csomagolja be egy tárolóképbe a Docker-fájl utasításainak megadásával.
Megvizsgáltuk a tárolónk optimalizálását is egy rétegezési funkció engedélyezésével, amely a függőségeket különálló rétegekbe húzza, amelyek a gazdagépen tárolódnak, és az alkalmazás egy vékony rétege betöltődik ütemezési időben a tároló végrehajtó motorjaiba.
A cikkben használt összes forráskódot megtalálja a címen GitHub .
Parancs hivatkozás
Íme egy gyors összefoglaló a cikkben használt parancsokról.
Kontextus törlése:
docker system prune -a
Tárolókép létrehozása Docker-fájl segítségével:
docker build -f <Docker file name> -t <tag> .
A tárolóképet a forráskódból építjük fel (Dockerfile nélkül):
mvn spring-boot:build-image
Tekintse meg a függőségi rétegeket. Az alkalmazás JAR-fájljának elkészítése előtt győződjön meg arról, hogy a rétegezési funkció engedélyezve van a spring-boot-maven-plugin-ban:
java -Djarmode=layertools -jar application.jar list
Függőségi rétegek kinyerése. Az alkalmazás JAR-fájljának létrehozása előtt győződjön meg arról, hogy a rétegezési funkció engedélyezve van a spring-boot-maven-plugin-ban: