Optimalizált Docker képek létrehozása tavaszi rendszerindításhoz

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.

 Kódpélda

Ezt a cikket működő kódpélda kíséri a GitHubon .

Konténer terminológia

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ányOpen 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 weblombokи 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:

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

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: 

Optimalizált Docker képek létrehozása tavaszi rendszerindításhoz

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.

Optimalizált Docker képek létrehozása tavaszi rendszerindításhoz

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:

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

Használjuk a Mavent build-imagealkalmazás létrehozásának és tárolókép létrehozásának céljai. Jelenleg nem használunk Docker-fájlokat.

mvn spring-boot:build-image

Az eredmény valami ilyesmi lesz:

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

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:

docker images 

Következtetés:

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

Tárolókép létrehozása a Jib használatával

A Jib egy képlétrehozó bővítmény a Google-tól, amely alternatív módszert kínál a konténerkép forráskódból történő létrehozására.

Konfigurálás jib-maven-pluginpom.xml-ben:

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

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:

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

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ó:

Optimalizált Docker képek létrehozása tavaszi rendszerindításhoz

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:

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

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 Docker képek létrehozása tavaszi rendszerindításhoz

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:

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

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:

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

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 listextractи 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
dependencies
spring-boot-loader
snapshot-dependencies
application

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. 

Optimalizált Docker képek létrehozása tavaszi rendszerindításhoz

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:

- "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/"

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:

 java -Djarmode=layertools -jar application.jar extract

A konténerképek listájának megtekintése

docker images

Nézze meg a bal oldali nézetet a konténer képén belül (győződjön meg arról, hogy a merülőeszköz telepítve van):

dive <image ID or image tag>

Forrás: will.com