web-dev-qa-db-fra.com

Comment mettre en cache les dépendances maven dans Docker

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?

4
L. Don

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

Utiliser le mécanisme de cache de Docker pour un projet Java/Maven

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

2
ErikMD

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
...
1
MyTwoCents