Je travaille sur un projet avec des dépendances de ~ 200 Mo et je voudrais éviter les envois inutiles en raison de ma bande passante limitée.
Lorsque j'appuie sur mon fichier Docker (je l'attache dans un moment), j'ai toujours un téléchargement d'environ 200 Mo, même si je n'ai pas touché le fichier pom.xml:
FROM maven:3.6.0-jdk-8-slim
WORKDIR /app
ADD pom.xml /app
RUN mvn verify clean --fail-never
COPY ./src /app/src
RUN mvn package
ENV CONFIG_FOLDER=/app/config
ENV DATA_FOLDER=/app/data
ENV GOLDENS_FOLDER=/app/goldens
ENV DEBUG_FOLDER=/app/debug
WORKDIR target
CMD ["Java","-jar","-Dlogs=/app/logs", "myProject.jar"]
Ce fichier Dockerfile devrait créer un fatJAR de 200 Mo, y compris toutes les dépendances. C’est pourquoi le téléchargement de 200 Mo environ a lieu à chaque fois. Ce que je voudrais réaliser est de construire une couche avec toutes les dépendances et "d'indiquer" à la phase d'empaquetage de ne pas inclure les dépendances JAR dans le fatJAR mais de les rechercher dans un répertoire donné.
Je me demandais de construire un script qui exécute mvn dependency:copy-dependencies
avant le processus de construction, puis copiant le répertoire dans le conteneur; construire ensuite un fichier JAR "non gras" dans lequel toutes ces dépendances sont uniquement liées et non copiées.
Est-ce possible?
EDIT: J'ai découvert que le référentiel local Maven du conteneur se trouvait sous /root/.m2
. Alors j'ai fini de faire un script très simple comme ceci:
BuildDocker.sh
mvn verify -clean --fail-never
mv ~/.m2 ~/git/myProjectRepo/.m2
Sudo docker build -t myName/myProject:"$1"
Et édité Dockerfile comme:
# Use an official Python runtime as a parent image
FROM maven:3.6.0-jdk-8-slim
# Copy my Mavne Local Repository into the container thus creating a new layer
COPY ./.m2 /root/.m2
# Set the working directory to /app
WORKDIR /app
# Copy the pom.xml
ADD pom.xml /app
# Resolve and Download all dependencies: this will be done only if the pom.xml has any changes
RUN mvn verify clean --fail-never
# Copy source code and configs
COPY ./src /app/src
# create a ThinJAR
RUN mvn package
# Run the jar
...
Après le processus de construction, j’ai déclaré que /root/.m2
contient tous les répertoires que j’ai mais dès que je lance le JAR, j’obtiens:
Exception in thread "main" Java.lang.NoClassDefFoundError: org/Apache/log4j/Priority
at myProject.ThreeMeans.calculate(ThreeMeans.Java:17)
at myProject.ClusteringStartup.main(ClusteringStartup.Java:7)
Caused by: Java.lang.ClassNotFoundException: org.Apache.log4j.Priority
at Java.net.URLClassLoader.findClass(URLClassLoader.Java:382)
at Java.lang.ClassLoader.loadClass(ClassLoader.Java:424)
at Sun.misc.Launcher$AppClassLoader.loadClass(Launcher.Java:349)
at Java.lang.ClassLoader.loadClass(ClassLoader.Java:357)
... 2 more
Peut-être que je ne devrais pas l'exécuter à travers Java -jar
?
Si je comprends bien ce que vous souhaitez réaliser, le problème est d’éviter de créer un fichier fat jar contenant toutes les dépendances Maven à chaque construction de Docker (afin d’alléger la taille des couches de Docker après une reconstruction).
Si oui, vous pourriez être intéressé par le Spring Boot Thin Launcher , qui est également applicable aux projets autres que Spring-Boot. Une documentation complète est disponible dans le README.md
du dépôt GitHub correspondant: https://github.com/dsyer/spring-boot-thin-launcher#readme
Pour résumer, il suffit d’ajouter la déclaration du plugin suivante dans votre pom.xml
:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!--<version>${spring-boot.version}</version>-->
<dependencies>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-layout</artifactId>
<version>1.0.19.RELEASE</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
Idéalement, cette solution doit être combinée à une configuration Dockerfile
standard pour tirer parti du cache de Docker (voir ci-dessous pour un exemple typique).
L'archétype d'une Dockerfile
qui évite de télécharger à nouveau toutes les dépendances Maven à chaque construction si seuls les fichiers de code source (src/*
) ont été touchés est donné dans la référence suivante:
https://whitfin.io/speeding-up-maven-docker-builds/
Pour être plus précis, la Dockerfile
proposée est la suivante:
# our base build image
FROM maven:3.5-jdk-8 as maven
WORKDIR /app
# copy the Project Object Model file
COPY ./pom.xml ./pom.xml
# fetch all dependencies
RUN mvn dependency:go-offline -B
# copy your other files
COPY ./src ./src
# build for release
# NOTE: my-project-* should be replaced with the proper prefix
RUN mvn package && cp target/my-project-*.jar app.jar
# smaller, final base image
FROM openjdk:8u171-jre-Alpine
# OPTIONAL: copy dependencies so the thin jar won't need to re-download them
# COPY --from=maven /root/.m2 /root/.m2
# set deployment directory
WORKDIR /app
# copy over the built artifact from the maven image
COPY --from=maven /app/app.jar ./app.jar
# set the startup command to run your binary
CMD ["Java", "-jar", "/app/app.jar"]
Notez qu'il repose sur la fonction de construction à plusieurs étapes multi de Docker (présence de deux directives FROM
), ce qui implique que l'image finale sera beaucoup plus petite que l'image de base maven
.
(Si cette fonctionnalité ne vous intéresse pas pendant la phase de développement, vous pouvez supprimer les lignes FROM openjdk:8u171-jre-Alpine
et COPY --from=maven /app/app.jar ./app.jar
.)
Dans cette approche, les dépendances Maven sont extraites avec RUN mvn dependency:go-offline -B
avant la ligne COPY ./src ./src
(pour bénéficier du cache de Docker).
Notez cependant que l’objectif standard dependency:go-offline
n’est pas "parfait" car quelques dépendances/plug-ins dynamiques peuvent encore déclencher un nouveau téléchargement à l'étape mvn package
. S'il s'agit d'un problème pour vous (par exemple si vous vraiment envie de travailler hors ligne), vous pouvez regarder cette autre SO réponse qui suggère l’utilisation d’un plug-in dédié fournissant l’objectif de.qaware.maven:go-offline-maven-plugin:resolve-dependencies
.
En général, la construction de conteneurs Dockerfile fonctionne en couches et chaque fois que vous les construisez, ces couches sont disponibles dans catch et sont utilisées en l'absence de modifications . Idéalement, elles auraient dû fonctionner de la même manière.
Maven recherche généralement les dépendances par défaut dans le dossier .m2
situé dans le répertoire principal de l'utilisateur dans Ubuntu /home/username/
.
Si les fichiers JAR dépendants ne sont pas disponibles, il les télécharge vers .m2 et les utilise.
Vous pouvez maintenant compresser et copier ce dossier .m2
après une construction réussie et le déplacer dans le répertoire de base de l'utilisateur de Docker Container.
Faites-le avant d'exécuter la commande de construction
Remarque: Vous devrez peut-être remplacer le dossier .m2
existant dans le menu fixe.
Donc, votre fichier Docker serait quelque chose comme ça
FROM maven:3.6.0-jdk-8-slim
WORKDIR /app
COPY .m2.Zip /home/testuser/
ADD pom.xml /app
RUN mvn verify clean --fail-never
COPY ./src /app/src
RUN mvn package
...