ProHoster > Bloc > Administració > Creació d'imatges Docker optimitzades per a una aplicació Spring Boot
Creació d'imatges Docker optimitzades per a una aplicació Spring Boot
Els contenidors s'han convertit en el mitjà preferit per empaquetar una aplicació amb totes les dependències del seu programari i sistema operatiu i després lliurar-les a diferents entorns.
Aquest article tracta diferents maneres de contenidor una aplicació Spring Boot:
creant una imatge Docker mitjançant un fitxer Docker,
creant una imatge OCI des de la font mitjançant Cloud-Native Buildpack,
i optimització de la imatge en temps d'execució mitjançant la separació de parts del JAR en diferents capes mitjançant eines de diversos nivells.
Exemple de codi
Aquest article s'acompanya d'un exemple de codi de treball a GitHub .
Terminologia dels contenidors
Començarem amb la terminologia del contenidor utilitzada a l'article:
Imatge del contenidor: fitxer d'un format específic. Convertirem la nostra aplicació en una imatge de contenidor executant l'eina de compilació.
envàs: una instància executable de la imatge del contenidor.
Motor de contenidors: el procés dimoni responsable d'executar el contenidor.
Amfitrió del contenidor: l'ordinador amfitrió on s'executa el motor del contenidor.
Registre de contenidors: La ubicació general utilitzada per publicar i distribuir la imatge del contenidor.
Estàndard OCI: Iniciativa de contenidors oberts (OCI) és una estructura de govern oberta i lleugera formada dins de la Fundació Linux. L'OCI Image Specification defineix els estàndards de la indústria per a la imatge de contenidor i els formats d'execució per garantir que tots els motors de contenidors puguin executar imatges de contenidors creades per qualsevol eina de creació.
Per contenidoritzar una aplicació, emboliquem la nostra aplicació en una imatge de contenidor i publiquem aquesta imatge en un registre compartit. El temps d'execució del contenidor recupera aquesta imatge del registre, la desempaqueta i executa l'aplicació dins d'ella.
La versió 2.3 de Spring Boot proporciona connectors per crear imatges OCI.
estibador és la implementació de contenidors més utilitzada, i fem servir Docker als nostres exemples, de manera que totes les referències de contenidors posteriors d'aquest article es referiran a Docker.
Construir una imatge de contenidor de la manera tradicional
Crear imatges de Docker per a aplicacions Spring Boot és molt fàcil afegint unes quantes instruccions al fitxer Docker.
Primer creem un fitxer JAR executable i, com a part de les instruccions del fitxer Docker, copiem el fitxer JAR executable a la part superior de la imatge JRE base després d'aplicar la configuració necessària.
Creem la nostra aplicació Spring a Inicialització de primavera amb dependències web, lombokи actuator. També estem afegint un controlador de descans per proporcionar una API GETmètode.
Creació d'un Dockerfile
A continuació, contenitzem aquesta aplicació afegint-hi Dockerfile:
El nostre fitxer Docker conté una imatge base de adoptopenjdk, a sobre del qual copiem el nostre fitxer JAR i després obrim el port, 8080que escoltarà les peticions.
Construcció de l'aplicació
Primer heu de crear una aplicació amb Maven o Gradle. Aquí fem servir Maven:
mvn clean package
Això crea un fitxer JAR executable per a l'aplicació. Hem de convertir aquest JAR executable en una imatge Docker per executar-lo al motor Docker.
Creació d'una imatge de contenidor
A continuació, posem aquest fitxer JAR executable a la imatge de Docker executant l'ordre docker builddes del directori arrel del projecte que conté el Dockerfile creat anteriorment:
docker build -t usersignup:v1 .
Podem veure la nostra imatge a la llista mitjançant l'ordre:
docker images
La sortida de l'ordre anterior inclou la nostra imatge usersignupjuntament amb la imatge base, adoptopenjdkespecificat al nostre fitxer Docker.
REPOSITORY TAG SIZE
usersignup v1 249MB
adoptopenjdk 11-jre-hotspot 229MB
Visualitza les capes dins d'una imatge de contenidor
Mirem la pila de capes dins de la imatge. Farem servir инструмент immersió per veure aquestes capes:
dive usersignup:v1
Aquí hi ha part de la sortida de l'ordre Dive:
Com podem veure, la capa d'aplicació constitueix una part important de la mida de la imatge. Volem reduir la mida d'aquesta capa a les seccions següents com a part de la nostra optimització.
Creació d'una imatge de contenidor amb Buildpack
Paquets de muntatge (Paquets de construcció) és un terme general utilitzat per diverses ofertes de Platform as a Service (PAAS) per crear una imatge de contenidor a partir del codi font. Va ser llançat per Heroku el 2011 i des de llavors ha estat adoptat per Cloud Foundry, Google App Engine, Gitlab, Knative i diversos altres.
L'avantatge dels paquets de compilació al núvol
Un dels principals avantatges d'utilitzar Buildpack per crear imatges és això Els canvis de configuració d'imatge es poden gestionar de manera centralitzada (generador) i propagar-se a totes les aplicacions mitjançant el generador.
Els paquets de compilació estaven estretament connectats a la plataforma. Cloud-Native Buildpacks ofereix estandardització entre plataformes gràcies al format d'imatge OCI, que garanteix que el motor Docker pugui executar la imatge.
Utilitzant el connector Spring Boot
El connector Spring Boot crea imatges OCI des de la font mitjançant Buildpack. Les imatges es creen utilitzant bootBuildImagetasques (Gradle) o spring-boot:build-imageobjectius (Maven) i instal·lació local de Docker.
Podem personalitzar el nom de la imatge necessària per enviar-la al registre de Docker especificant-ne el nom image tag:
Utilitzem Maven per fer-ho build-imageobjectius per crear una aplicació i crear una imatge de contenidor. No estem utilitzant cap fitxer Dockerfile en aquest moment.
A la sortida ho veiem paketo Cloud-Native buildpacks'utilitza per crear una imatge OCI de treball. Com abans, podem veure la imatge llistada com a imatge de Docker executant l'ordre:
A continuació, executem el connector Jib mitjançant l'ordre Maven per crear l'aplicació i crear una imatge de contenidor. Com abans, no estem utilitzant cap fitxer Docker aquí:
Després d'executar l'ordre Maven anterior, obtenim la següent sortida:
[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
La sortida mostra que la imatge del contenidor s'ha creat i col·locat al registre.
Motivacions i tècniques per crear imatges optimitzades
Tenim dues raons principals per a l'optimització:
Productivitat: En un sistema d'orquestració de contenidors, es recupera una imatge de contenidor del registre d'imatges a l'amfitrió que executa el motor de contenidors. Aquest procés s'anomena planificació. L'extracció d'imatges grans del registre comporta temps de programació llargs en sistemes d'orquestració de contenidors i temps de construcció llargs en canalitzacions CI.
Безопасность: Les imatges més grans també tenen una àrea més gran de vulnerabilitats.
Una imatge de Docker consta d'una pila de capes, cadascuna de les quals representa una instrucció al nostre Dockerfile. Cada capa representa un delta dels canvis de la capa subjacent. Quan traiem una imatge de Docker del registre, s'extreu en capes i s'emmagatzema a la memòria cau a l'amfitrió.
Ús de Spring Boot "FAT JAR" a com a format d'embalatge predeterminat. Quan mirem el JAR gruixut, veiem que l'aplicació constitueix una part molt petita de tot el JAR. Aquesta és la part que canvia més sovint. La resta consisteix en les dependències de Spring Framework.
La fórmula d'optimització se centra en aïllar l'aplicació a un nivell separat de les dependències de Spring Framework.
La capa de dependència, que forma la major part del fitxer JAR gruixut, només es baixa una vegada i es guarda a la memòria cau al sistema amfitrió.
Només s'extreu una capa fina de l'aplicació durant les actualitzacions de l'aplicació i la programació de contenidors. com es mostra en aquest diagrama:
A les seccions següents, veurem com crear aquestes imatges optimitzades per a una aplicació Spring Boot.
Creació d'una imatge de contenidor optimitzada per a una aplicació Spring Boot mitjançant Buildpack
Spring Boot 2.3 admet la superposició extraient parts d'un fitxer JAR gruixut en capes separades. La funció de capes està desactivada de manera predeterminada i s'ha d'habilitar explícitament mitjançant el connector Spring Boot Maven:
Utilitzarem aquesta configuració per crear la nostra imatge de contenidor primer amb Buildpack i després amb Docker a les seccions següents.
Llencem build-imageObjectiu de Maven per crear imatges de contenidors:
mvn spring-boot:build-image
Si executem Dive per veure les capes de la imatge resultant, podem veure que la capa de l'aplicació (delineada en vermell) és molt més petita en el rang de kilobytes en comparació amb la que hem obtingut amb el format JAR gruixut:
Creació d'una imatge de contenidor optimitzada per a una aplicació Spring Boot mitjançant Docker
En lloc d'utilitzar un complement Maven o Gradle, també podem crear una imatge JAR de Docker en capes amb un fitxer Docker.
Quan fem servir Docker, hem de realitzar dos passos addicionals per extreure les capes i copiar-les a la imatge final.
El contingut del JAR resultant després de la construcció utilitzant Maven amb la capa activada serà així:
La sortida mostra un JAR addicional anomenat spring-boot-jarmode-layertoolsи layersfle.idxdossier. Aquest fitxer JAR addicional proporciona capacitats de processament en capes, tal com es descriu a la secció següent.
Extracció de dependències de capes individuals
Per veure i extreure capes del nostre JAR en capes, utilitzem la propietat del sistema -Djarmode=layertoolsper començar spring-boot-jarmode-layertoolsJAR en lloc de l'aplicació:
L'execució d'aquesta ordre produeix una sortida que conté les opcions d'ordres disponibles:
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
La sortida mostra les ordres list, extractи helpс helpsigui el predeterminat. Executem l'ordre amb listopció:
java -Djarmode=layertools -jar target/usersignup-0.0.1-SNAPSHOT.jar list
Veiem una llista de dependències que es poden afegir com a capes.
Capes per defecte:
Nom de la capa
Contingut
dependencies
qualsevol dependència la versió de la qual no conté SNAPSHOT
spring-boot-loader
Classes de càrrega JAR
snapshot-dependencies
qualsevol dependència la versió de la qual contingui SNAPSHOT
application
classes d'aplicació i recursos
Les capes es defineixen a layers.idxfitxer en l'ordre en què s'han d'afegir a la imatge de Docker. Aquestes capes s'emmagatzemen a la memòria cau a l'amfitrió després de la primera recuperació perquè no canvien. Només es baixa a l'amfitrió la capa d'aplicació actualitzada, que és més ràpida a causa de la mida reduïda .
Construir una imatge amb dependències extretes en capes separades
Construirem la imatge final en dues etapes utilitzant un mètode anomenat muntatge en diverses etapes . En el primer pas extreurem les dependències i en el segon pas copiarem les dependències extretes a la imatge final.
Modifiquem el nostre Dockerfile per a una construcció en diverses etapes:
# 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"]
Desem aquesta configuració en un fitxer separat - Dockerfile2.
Construïm la imatge de Docker mitjançant l'ordre:
docker build -f Dockerfile2 -t usersignup:v1 .
Després d'executar aquesta comanda, obtenim la següent sortida:
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
Podem veure que es crea una imatge de Docker amb un ID d'imatge i després s'etiqueta.
Finalment, executem l'ordre Dive com abans per inspeccionar les capes dins de la imatge Docker generada. Podem proporcionar una identificació d'imatge o una etiqueta com a entrada a l'ordre d'immersió:
dive userssignup:v1
Com podeu veure a la sortida, la capa que conté l'aplicació ara només té 11 KB i les dependències s'emmagatzemen a la memòria cau en capes separades.
Extracció de dependències internes de capes individuals
Podem reduir encara més la mida del nivell d'aplicació extraient qualsevol de les nostres dependències personalitzades en un nivell separat en lloc d'empaquetar-les amb l'aplicació declarant-les a ymlarxiu similar anomenat layers.idx:
En aquest fitxer layers.idxhem afegit una dependència personalitzada anomenada, io.myorgque conté dependències de l'organització recuperades d'un repositori compartit.
Sortida
En aquest article, vam analitzar l'ús de Cloud-Native Buildpacks per crear una imatge de contenidor directament des del codi font. Aquesta és una alternativa a l'ús de Docker per crear una imatge de contenidor de la manera habitual: primer creant un fitxer JAR executable gruixut i després empaquetant-lo en una imatge de contenidor especificant instruccions al fitxer Docker.
També vam analitzar l'optimització del nostre contenidor habilitant una funció de capes que agrupa les dependències en capes separades que s'emmagatzemen a la memòria cau a l'amfitrió i es carrega una capa fina de l'aplicació en el moment de la programació als motors d'execució del contenidor.
Podeu trobar tot el codi font utilitzat a l'article a Github .
Referència de comanda
Aquí teniu un resum ràpid de les ordres que hem utilitzat en aquest article.
Neteja de context:
docker system prune -a
Creació d'una imatge de contenidor mitjançant un fitxer Docker:
docker build -f <Docker file name> -t <tag> .
Construïm la imatge del contenidor a partir del codi font (sense Dockerfile):
mvn spring-boot:build-image
Visualitza les capes de dependència. Abans de crear el fitxer JAR de l'aplicació, assegureu-vos que la funció de capes estigui habilitada a spring-boot-maven-plugin:
java -Djarmode=layertools -jar application.jar list
Extracció de capes de dependència. Abans de crear el fitxer JAR de l'aplicació, assegureu-vos que la funció de capes estigui habilitada a spring-boot-maven-plugin: