J'essaie d'utiliser Docker pour automatiser les builds Maven. Le projet que je veux construire prend près de 20 minutes pour télécharger toutes les dépendances, j'ai donc essayé de construire une image docker qui mettrait en cache ces dépendances, mais il ne semble pas l'enregistrer. Mon Dockerfile est
FROM maven:Alpine
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
ADD pom.xml /usr/src/app
RUN mvn dependency:go-offline
L'image se construit et télécharge tout. Cependant, l'image résultante est de la même taille que la base maven:Alpine
image, il ne semble donc pas avoir mis en cache les dépendances dans l'image. Lorsque j'essaie d'utiliser l'image pour mvn compile
il passe par les 20 minutes complètes de tout retélécharger.
Est-il possible de créer une image maven qui met en cache mes dépendances afin qu'elles n'aient pas à télécharger à chaque fois que j'utilise l'image pour effectuer une génération?
J'exécute les commandes suivantes:
docker build -t my-maven .
docker run -it --rm --name my-maven-project -v "$PWD":/usr/src/mymaven -w /usr/src/mymaven my-maven mvn compile
Ma compréhension est que tout ce que RUN
fait pendant le processus de construction du docker devient une partie de l'image résultante.
Généralement, il n'y a aucun changement dans pom.xml
fichier mais juste quelques autres modifications du code source lorsque vous essayez de démarrer la construction de l'image Docker. Dans de telles circonstances, vous pouvez le faire:
Pour info:
FROM maven:3-jdk-8
ENV HOME=/home/usr/app
RUN mkdir -p $HOME
WORKDIR $HOME
# 1. add pom.xml only here
ADD pom.xml $HOME
# 2. start downloading dependencies
RUN ["/usr/local/bin/mvn-entrypoint.sh", "mvn", "verify", "clean", "--fail-never"]
# 3. add all source code and start compiling
ADD . $HOME
RUN ["mvn", "package"]
EXPOSE 8005
CMD ["Java", "-jar", "./target/dist.jar"]
La clé est donc:
ajouter pom.xml
fichier.
puis mvn verify --fail-never
it, il téléchargera les dépendances maven.
ajoutez ensuite tout votre fichier source et lancez votre compilation (mvn package
).
Lorsqu'il y a des changements dans votre pom.xml
fichier ou si vous exécutez ce script pour la première fois, docker fera 1 -> 2 -> 3. Lorsqu'il n'y a aucun changement dans pom.xml
fichier, docker sautera l'étape 1、2 et fera directement 3.
Cette astuce simple peut être utilisée dans de nombreuses autres circonstances de gestion de paquets (gradle 、 fil 、 npm 、 pip).
Modifier:
Vous devriez également envisager d'utiliser mvn dependency:resolve
ou mvn dependency:go-offline
en conséquence, comme le suggèrent d'autres commentaires et réponses.
Il s'avère que l'image que j'utilise comme base a une image parent qui définit
VOLUME "$USER_HOME_DIR/.m2"
Le résultat est que lors de la construction, tous les fichiers sont écrits dans $USER_HOME_DIR/.m2
, mais comme il devrait s'agir d'un volume, aucun de ces fichiers n'est conservé avec l'image du conteneur.
Actuellement, dans Docker, il n'y a aucun moyen de désenregistrer cette définition de volume, il serait donc nécessaire de créer une image maven distincte, plutôt que d'utiliser l'image maven officielle.
@Kim est le plus proche, mais il n'est pas encore tout à fait là. Je ne pense pas que l'ajout de --fail-never
est correct, même si le travail est fait.
La commande verify
provoque l'exécution de nombreux plugins, ce qui est un problème (pour moi) - je ne pense pas qu'ils devraient être exécutés quand tout ce que je veux c'est installer des dépendances! J'ai également une construction multi-modules et une sous-construction javascript, ce qui complique encore la configuration.
Mais exécuter uniquement verify
ne suffit pas, car si vous exécutez install
dans les commandes suivantes, il y aura plus de plugins utilisés - ce qui signifie plus de dépendances à télécharger - maven refuse de les télécharger autrement. Lecture pertinente: Maven: Introduction to the Build Lifecycle
Vous devez essentiellement trouver quelles propriétés désactivent chaque plugin et les ajouter un par un, afin qu'elles ne cassent pas votre build.
WORKDIR /srv
# cache Maven dependencies
ADD cli/pom.xml /srv/cli/
ADD core/pom.xml /srv/core/
ADD parent/pom.xml /srv/parent/
ADD rest-api/pom.xml /srv/rest-api/
ADD web-admin/pom.xml /srv/web-admin/
ADD pom.xml /srv/
RUN mvn -B clean install -DskipTests -Dcheckstyle.skip -Dasciidoctor.skip -Djacoco.skip -Dmaven.gitcommitid.skip -Dspring-boot.repackage.skip -Dmaven.exec.skip=true -Dmaven.install.skip -Dmaven.resources.skip
# cache YARN dependencies
ADD ./web-admin/package.json ./web-admin/yarn.lock /srv/web-admin/
RUN yarn --non-interactive --frozen-lockfile --no-progress --cwd /srv/web-admin install
# build the project
ADD . /srv
RUN mvn -B clean install
mais certains plugins ne sont pas si faciles à ignorer - je ne suis pas un expert en maven (donc je ne sais pas pourquoi il ignore l'option cli - ce pourrait être un bug), mais le suivant fonctionne comme prévu pour org.codehaus.mojo:exec-maven-plugin
<project>
<properties>
<maven.exec.skip>false</maven.exec.skip>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.3.2</version>
<executions>
<execution>
<id>yarn install</id>
<goals>
<goal>exec</goal>
</goals>
<phase>initialize</phase>
<configuration>
<executable>yarn</executable>
<arguments>
<argument>install</argument>
</arguments>
<skip>${maven.exec.skip}</skip>
</configuration>
</execution>
<execution>
<id>yarn run build</id>
<goals>
<goal>exec</goal>
</goals>
<phase>compile</phase>
<configuration>
<executable>yarn</executable>
<arguments>
<argument>run</argument>
<argument>build</argument>
</arguments>
<skip>${maven.exec.skip}</skip>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
veuillez noter le <skip>${maven.exec.skip}</skip>
- d'autres plugins récupèrent cela dans les paramètres cli mais pas celui-ci (ni -Dmaven.exec.skip=true
ni -Dexec.skip=true
travailler seul)
J'espère que cela t'aides
Similaire avec la réponse @Kim mais j'utilise dependency:resolve
commande mvn. Voici donc mon Dockerfile complet:
FROM maven:3.5.0-jdk-8-Alpine
WORKDIR /usr/src/app
# First copy only the pom file. This is the file with less change
COPY ./pom.xml .
# Download the package and make it cached in docker image
RUN mvn -B -f ./pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve
# Copy the actual code
COPY ./ .
# Then build the code
RUN mvn -B -f ./pom.xml -s /usr/share/maven/ref/settings-docker.xml package
# The rest is same as usual
EXPOSE 8888
CMD ["Java", "-jar", "./target/YOUR-APP.jar"]
Après quelques jours de difficultés, j'ai réussi à faire cette mise en cache plus tard en utilisant un contrainer intermédiaire, et je voudrais résumer mes résultats ici car ce sujet est si utile et apparaît fréquemment dans la page d'accueil de la recherche Google:
#docker build -t dependencies .
From ubuntu
COPY pom.xml pom.xml
RUN mvn dependency:go-offline -B --fail-never
From dependencies as intermediate
From Tomcat
RUN git pull repo.git (whatsoever)
RUN mvn package
L'idée est de conserver toutes les dépendances dans une image différente que Maven peut utiliser immédiatement
Cela pourrait être d'autres scénarios que je n'ai pas encore rencontrés, mais cette solution me soulage un peu du téléchargement de 3 Go de déchets chaque fois que je ne peux pas imaginer pourquoi Java est devenu une grosse baleine dans le monde maigre d'aujourd'hui
Je ne pense pas que les autres réponses ici soient optimales. Par exemple, le mvn verify
answer exécute les phases suivantes et fait bien plus que simplement résoudre les dépendances:
valider - valider le projet est correct et toutes les informations nécessaires sont disponibles
compile - compile le code source du projet
test - testez le code source compilé en utilisant un cadre de test unitaire approprié. Ces tests ne devraient pas exiger que le code soit empaqueté ou déployé
package - prenez le code compilé et empaquetez-le dans son format distribuable, tel qu'un JAR.
vérifier - effectuer des vérifications sur les résultats des tests d'intégration pour s'assurer que les critères de qualité sont respectés
Toutes ces phases et leurs objectifs associés n'ont pas besoin d'être exécutés si vous souhaitez uniquement résoudre les dépendances.
Si vous souhaitez uniquement résoudre les dépendances, vous pouvez utiliser le dependency:go-offline
objectif:
FROM maven:3-jdk-12
WORKDIR /tmp/example/
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src/ src/
RUN mvn package
De Docker v18.03
à partir de là, vous pouvez utiliser BuildKit au lieu des volumes mentionnés dans les autres réponses. Il permet de monter des caches qui peuvent persister entre les builds et vous pouvez éviter de télécharger le contenu du .m2/repository
à chaque fois.
En supposant que le Dockerfile se trouve à la racine de votre projet:
# syntax = docker/dockerfile:1.0-experimental
FROM maven:3.6.0-jdk-11-slim AS build
COPY . /home/build
RUN mkdir /home/.m2
WORKDIR /home/.m2
USER root
RUN --mount=type=cache,target=/root/.m2 mvn -f /home/build/pom.xml clean compile
target=/root/.m2
monte le cache à l'endroit spécifié dans l'image maven Dockerfile docs .
Pour la construction, vous pouvez exécuter la commande suivante:
DOCKER_BUILDKIT=1 docker build --rm --no-cache .
Plus d'informations sur BuildKit peuvent être trouvées ici .
Il existe deux façons de mettre en cache les dépendances maven:
Exécutez "mvn verify" dans le cadre d'une exécution de conteneur, PAS de build, et assurez-vous de monter .m2 à partir d'un volume.
C'est efficace, mais cela ne fonctionne pas bien avec la génération de cloud et plusieurs esclaves de construction
Utilisez un "conteneur de cache de dépendances" et mettez-le à jour régulièrement. Voici comment:
une. Créez un Dockerfile qui copie le pom et créez des dépendances hors ligne:
FROM maven:3.5.3-jdk-8-Alpine
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline
b. Construisez-le périodiquement (par exemple tous les soirs) en tant que "Deps: latest"
c. Créez un autre Dockerfile pour réellement construire le système par commit (utilisez de préférence plusieurs étapes) - et assurez-vous qu'il s'agit de FROM Deps.
En utilisant ce système, vous aurez des versions rapides et reconstructibles avec un cache généralement assez bon.
J'ai eu ce problème il y a peu de temps. Il existe de nombreuses solutions sur le Web, mais celle qui a fonctionné pour moi consiste simplement à monter un volume pour le répertoire des modules maven:
mkdir /opt/myvolumes/m2
puis dans le Dockerfile:
...
VOLUME /opt/myvolumes/m2:/root/.m2
...
Il existe de meilleures solutions, mais pas aussi simples.
Ce billet de blog fait un effort supplémentaire pour vous aider à tout mettre en cache:
https://keyholesoftware.com/2015/01/05/caching-for-maven-docker-builds/