Exemple de travail.
Ce n'est pas un tutoriel de démarrage à ressort. C'est la réponse mise à jour à une question sur la façon d'exécuter une build Maven dans un conteneur Docker.
Question postée il y a 4 ans.
1. Générez une application
Utilisez l'initialiseur de printemps pour générer une application de démonstration
https://start.spring.io/
Extraire l'archive zip localement
2. Créez un Dockerfile
#
# Build stage
#
FROM maven:3.6.0-jdk-11-slim AS build
COPY src /home/app/src
COPY pom.xml /home/app
RUN mvn -f /home/app/pom.xml clean package
#
# Package stage
#
FROM openjdk:11-jre-slim
COPY --from=build /home/app/target/demo-0.0.1-SNAPSHOT.jar /usr/local/lib/demo.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/local/lib/demo.jar"]
Remarque
- Cet exemple utilise une génération en plusieurs étapes . La première étape est utilisée pour construire le code. La deuxième étape ne contient que le jar construit et un JRE pour l'exécuter (notez comment le jar est copié entre les étapes).
3. Construisez l'image
docker build -t demo .
4. Exécutez l'image
$ docker run --rm -it demo:latest
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.3.RELEASE)
2019-02-22 17:18:57.835 INFO 1 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication v0.0.1-SNAPSHOT on f4e67677c9a9 with PID 1 (/usr/local/bin/demo.jar started by root in /)
2019-02-22 17:18:57.837 INFO 1 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2019-02-22 17:18:58.294 INFO 1 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.711 seconds (JVM running for 1.035)
Divers
Lisez la documentation du concentrateur Docker sur la façon dont la version Maven peut être optimisée pour utiliser un référentiel local pour mettre en cache les fichiers JAR.
Mise à jour (2019-02-07)
Cette question a maintenant 4 ans et pendant ce temps, il est juste de dire que la création d'applications à l'aide de Docker a subi des changements importants.
Option 1: construction en plusieurs étapes
Ce nouveau style vous permet de créer des images plus légères qui n'encapsulent pas vos outils de construction et votre code source.
L'exemple ici utilise à nouveau l' image de base officielle de maven pour exécuter la première étape de la construction en utilisant une version souhaitée de Maven. La deuxième partie du fichier définit la façon dont le pot construit est assemblé dans l'image de sortie finale.
FROM maven:3.5-jdk-8 AS build
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml clean package
FROM gcr.io/distroless/java
COPY --from=build /usr/src/app/target/helloworld-1.0.0-SNAPSHOT.jar /usr/app/helloworld-1.0.0-SNAPSHOT.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/app/helloworld-1.0.0-SNAPSHOT.jar"]
Remarque:
- J'utilise l' image de base sans distraction de Google , qui s'efforce de fournir juste assez de temps d'exécution pour une application java.
Option 2: flèche
Je n'ai pas utilisé cette approche mais semble digne d'être enquêtée car elle vous permet de créer des images sans avoir à créer des choses désagréables comme Dockerfiles :-)
https://github.com/GoogleContainerTools/jib
Le projet dispose d'un plugin Maven qui intègre l'empaquetage de votre code directement dans votre workflow Maven.
Réponse originale (incluse pour être complète, mais écrite il y a longtemps)
Essayez d'utiliser les nouvelles images officielles, il y en a une pour Maven
https://registry.hub.docker.com/_/maven/
L'image peut être utilisée pour exécuter Maven au moment de la génération pour créer une application compilée ou, comme dans les exemples suivants, pour exécuter une build Maven dans un conteneur.
Exemple 1 - Maven s'exécutant dans un conteneur
La commande suivante exécute votre build Maven dans un conteneur:
docker run -it --rm \
-v "$(pwd)":/opt/maven \
-w /opt/maven \
maven:3.2-jdk-7 \
mvn clean install
Remarques:
- L'avantage de cette approche est que tous les logiciels sont installés et exécutés dans le conteneur. Seulement besoin de docker sur la machine hôte.
- Voir Dockerfile pour cette version
Exemple 2 - Utiliser Nexus pour mettre en cache des fichiers
Exécutez le conteneur Nexus
docker run -d -p 8081:8081 --name nexus sonatype/nexus
Créez un fichier "settings.xml":
<settings>
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://nexus:8081/content/groups/public/</url>
</mirror>
</mirrors>
</settings>
Maintenant, exécutez Maven liant au conteneur nexus, de sorte que les dépendances soient mises en cache
docker run -it --rm \
-v "$(pwd)":/opt/maven \
-w /opt/maven \
--link nexus:nexus \
maven:3.2-jdk-7 \
mvn -s settings.xml clean install
Remarques:
- Un avantage de l'exécution de Nexus en arrière-plan est que d'autres référentiels tiers peuvent être gérés via l'URL d'administration de manière transparente pour les builds Maven exécutés dans des conteneurs locaux.
mavenCentral()
dans mes dépendances gradle parmaven {url "http://nexus:8081..."
et je reçois maintenant juste des problèmes de résolution.Il peut y avoir de nombreuses façons.
L'exemple donné est celui du projet maven.
1. Utilisation de Dockerfile dans le projet maven
Utilisez la structure de fichiers suivante:
Demo └── src | ├── main | │ ├── java | │ └── org | │ └── demo | │ └── Application.java | │ | └── test | ├──── Dockerfile ├──── pom.xml
Et mettez à jour le Dockerfile comme:
FROM java:8 EXPOSE 8080 ADD /target/demo.jar demo.jar ENTRYPOINT ["java","-jar","demo.jar"]
Accédez au dossier du projet et tapez la commande suivante, vous serez capable de créer une image et d'exécuter cette image:
$ mvn clean $ mvn install $ docker build -f Dockerfile -t springdemo . $ docker run -p 8080:8080 -t springdemo
Obtenir la vidéo sur Spring Boot avec Docker
2. Utilisation des plugins Maven
Ajouter le plugin maven donné dans
pom.xml
<plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>0.4.5</version> <configuration> <imageName>springdocker</imageName> <baseImage>java</baseImage> <entryPoint>["java", "-jar", "/${project.build.finalName}.jar"]</entryPoint> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> </configuration> </plugin>
Accédez au dossier du projet et tapez la commande suivante, vous pourrez créer une image et exécuter cette image:
$ mvn clean package docker:build $ docker images $ docker run -p 8080:8080 -t <image name>
Dans le premier exemple, nous créons Dockerfile et fournissons une image de base et ajoutons un jar, après cela, nous exécuterons la commande docker pour créer une image avec un nom spécifique, puis exécuterons cette image.
Alors que dans le deuxième exemple, nous utilisons le plugin maven dans lequel nous fournissons
baseImage
etimageName
que nous n'avons donc pas besoin de créer Dockerfile ici .. après l'empaquetage du projet maven, nous obtiendrons l'image docker et nous avons juste besoin d'exécuter cette image.la source
En règle générale, vous devez créer un JAR volumineux à l' aide de Maven (un JAR qui contient à la fois votre code et toutes les dépendances).
Ensuite, vous pouvez écrire un Dockerfile qui correspond à vos besoins (si vous pouvez créer un gros JAR, vous n'aurez besoin que d' un système d' exploitation de base, comme CentOS, et de la JVM).
C'est ce que j'utilise pour une application Scala (qui est basée sur Java).
FROM centos:centos7 # Prerequisites. RUN yum -y update RUN yum -y install wget tar # Oracle Java 7 WORKDIR /opt RUN wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/7u71-b14/server-jre-7u71-linux-x64.tar.gz RUN tar xzf server-jre-7u71-linux-x64.tar.gz RUN rm -rf server-jre-7u71-linux-x64.tar.gz RUN alternatives --install /usr/bin/java java /opt/jdk1.7.0_71/bin/java 1 # App USER daemon # This copies to local fat jar inside the image ADD /local/path/to/packaged/app/appname.jar /app/appname.jar # What to run when the container starts ENTRYPOINT [ "java", "-jar", "/app/appname.jar" ] # Ports used by the app EXPOSE 5000
Cela crée une image basée sur CentOS avec Java7. Une fois démarré, il exécutera votre fichier jar d'application.
La meilleure façon de le déployer est via le registre Docker, c'est comme un Github pour les images Docker.
Vous pouvez créer une image comme celle-ci:
# current dir must contain the Dockerfile docker build -t username/projectname:tagname .
Vous pouvez ensuite pousser une image de cette manière:
docker push username/projectname # this pushes all tags
Une fois l'image sur le registre Docker, vous pouvez la récupérer de n'importe où dans le monde et l'exécuter.
Consultez le Guide de l'utilisateur de Docker pour plus d'informations.
Quelque chose à garder à l'esprit :
Vous pouvez également extraire votre référentiel dans une image et créer le fichier jar dans le cadre de l'exécution du conteneur, mais ce n'est pas une bonne approche, car le code pourrait changer et vous pourriez finir par utiliser une version différente de l'application sans préavis.
Construire un gros pot élimine ce problème.
la source
RUN wget -O {project.build.finalname}.jar
Mais je veux télécharger le fichier ci-dessus à partir de nexus.Voici ma contribution.
Je n'essaierai pas de lister tous les outils / bibliothèques / plugins qui existent pour tirer parti de Docker avec Maven. Certaines réponses l'ont déjà fait.
au lieu de, je me concentrerai sur la typologie des applications et la manière Dockerfile.
Dockerfile
est vraiment un concept simple et important de Docker (toutes les images connues / publiques en dépendent) et je pense qu'essayer d'éviter de comprendre et d'utiliserDockerfile
s n'est pas nécessairement la meilleure façon d'entrer dans le monde de Docker.L'ancrage d'une application dépend de l'application elle-même et de l'objectif à atteindre
1) Pour les applications que nous voulons continuer à exécuter sur un serveur Java installé / autonome (Tomcat, JBoss, etc ...)
La route est plus difficile et ce n'est pas la cible idéale car cela ajoute de la complexité (il faut gérer / maintenir le serveur) et c'est moins évolutif et moins rapide que les serveurs embarqués en termes de build / déploiement / annulation de déploiement.
Mais pour les applications héritées, cela peut être considéré comme une première étape.
Généralement, l'idée ici est de définir une image Docker pour le serveur et de définir une image par application à déployer.
Les images docker pour les applications produisent le WAR / EAR attendu, mais celles-ci ne sont pas exécutées en tant que conteneur et l'image de l'application serveur déploie les composants produits par ces images en tant qu'applications déployées.
Pour les applications volumineuses (des millions de lignes de codes) avec beaucoup de trucs hérités, et si difficiles à migrer vers une solution intégrée à démarrage à ressort complet, c'est vraiment une belle amélioration.
Je ne détaillerai pas plus cette approche car il s'agit de cas d'utilisation mineurs de Docker mais je voulais exposer l'idée globale de cette approche car je pense que pour les développeurs confrontés à ces cas complexes, il est bon de savoir que certaines portes sont ouvertes à intégrer Docker.
2) Pour les applications qui embarquent / bootstrap le serveur elles-mêmes (Spring Boot avec serveur intégré: Tomcat, Netty, Jetty ...)
C'est la cible idéale avec Docker . J'ai spécifié Spring Boot parce que c'est un très bon framework pour le faire et qui a également un très haut niveau de maintenabilité, mais en théorie, nous pourrions utiliser n'importe quel autre moyen Java pour y parvenir.
Généralement, l'idée ici est de définir une image Docker par application à déployer.
Les images docker pour les applications produisent un JAR ou un ensemble de fichiers JAR / classes / configuration et ceux-ci démarrent une JVM avec l'application (commande java) lorsque nous créons et démarrons un conteneur à partir de ces images.
Pour les nouvelles applications ou applications pas trop complexes à migrer, cette méthode doit être privilégiée par rapport aux serveurs autonomes, car c'est la manière standard et la manière la plus efficace d'utiliser les conteneurs.
Je détaillerai cette approche.
Ancrer une application maven
1) sans botte à ressort
L'idée est de créer un fat jar avec Maven (le plugin d'assemblage maven et l'aide du plugin maven shadow pour cela) qui contient à la fois les classes compilées de l'application et les dépendances maven nécessaires.
Ensuite, nous pouvons identifier deux cas:
si l'application est une application de bureau ou autonome (qui n'a pas besoin d'être déployée sur un serveur): nous pourrions spécifier comme
CMD/ENTRYPOINT
dans l'Dockerfile
exécution java de l'application:java -cp .:/fooPath/* -jar myJar
si l'application est une application serveur, par exemple Tomcat, l'idée est la même: obtenir un gros pot de l'application et exécuter une JVM dans le
CMD/ENTRYPOINT
. Mais ici avec une différence importante: nous devons inclure des bibliothèques logiques et spécifiques (org.apache.tomcat.embed
bibliothèques et quelques autres) qui démarrent le serveur intégré lorsque l'application principale est lancée.Nous avons un guide complet sur le site Web heroku .
Pour le premier cas (application autonome), c'est un moyen simple et efficace d'utiliser Docker.
Pour le deuxième cas (application serveur), cela fonctionne mais ce n'est pas simple, peut être sujet aux erreurs et n'est pas un modèle très extensible car vous ne placez pas votre application dans le cadre d'un framework mature tel que Spring Boot qui en fait beaucoup de ces choses pour vous et fournit également un haut niveau d'extension.
Mais cela présente un avantage: vous disposez d'une grande liberté car vous utilisez directement l'API Tomcat intégrée.
2) avec Spring Boot
Enfin, nous y voilà.
C'est à la fois simple, efficace et très bien documenté.
Il existe en fait plusieurs approches pour créer une application Maven / Spring Boot à exécuter sur Docker.
Les exposer tous serait long et peut-être ennuyeux.
Le meilleur choix dépend de vos besoins.
Mais quelle que soit la manière, la stratégie de construction en termes de couches docker ressemble à la même chose.
Nous voulons utiliser une construction en plusieurs étapes: une s'appuyant sur Maven pour la résolution des dépendances et pour la construction et une autre s'appuyant sur JDK ou JRE pour démarrer l'application.
Étape de construction (image Maven):
À propos de cela,
mvn dependency:resolve-plugins
enchaîné àmvn dependency:resolve
peut faire le travail mais pas toujours.Pourquoi ? Parce que ces plugins et l'
package
exécution pour empaqueter le fat jar peuvent dépendre de différents artefacts / plugins et même pour un même artefact / plugin, ceux-ci peuvent toujours extraire une version différente. Donc, une approche plus sûre, bien que potentiellement plus lente, consiste à résoudre les dépendances en exécutant exactement lamvn
commande utilisée pour empaqueter l'application (qui extraira exactement les dépendances dont vous avez besoin) mais en ignorant la compilation source et en supprimant le dossier cible pour accélérer le traitement et empêcher toute détection de changement de couche indésirable pour cette étape.Étape d'exécution (image JDK ou JRE):
Voici deux exemples.
a) Un moyen simple sans cache pour les dépendances maven téléchargées
Dockerfile:
########Maven build stage######## FROM maven:3.6-jdk-11 as maven_build WORKDIR /app #copy pom COPY pom.xml . #resolve maven dependencies RUN mvn clean package -Dmaven.test.skip -Dmaven.main.skip -Dspring-boot.repackage.skip && rm -r target/ #copy source COPY src ./src # build the app (no dependency download here) RUN mvn clean package -Dmaven.test.skip # split the built app into multiple layers to improve layer rebuild RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar ########JRE run stage######## FROM openjdk:11.0-jre WORKDIR /app #copy built app layer by layer ARG DOCKER_PACKAGING_DIR=/app/target/docker-packaging COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/lib /app/lib COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/classes /app/classes COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/META-INF /app/META-INF #run the app CMD java -cp .:classes:lib/* \ -Djava.security.egd=file:/dev/./urandom \ foo.bar.MySpringBootApplication
Inconvénient de cette solution? Toute modification dans le pom.xml signifie recréer la couche entière qui télécharge et stocke les dépendances maven. Cela n'est généralement pas acceptable pour les applications avec de nombreuses dépendances (et Spring Boot extrait de nombreuses dépendances), dans l'ensemble si vous n'utilisez pas de gestionnaire de référentiel maven pendant la construction de l'image.
b) Un moyen plus efficace avec le cache pour les dépendances maven téléchargées
L'approche est ici la même mais les téléchargements de dépendances maven sont mis en cache dans le cache du constructeur du docker.
L'opération de cache repose sur buildkit (api expérimentale de docker).
Pour activer buildkit, la variable env DOCKER_BUILDKIT = 1 doit être définie (vous pouvez le faire où vous le souhaitez: .bashrc, ligne de commande, fichier json du démon docker ...).
Dockerfile:
# syntax=docker/dockerfile:experimental ########Maven build stage######## FROM maven:3.6-jdk-11 as maven_build WORKDIR /app #copy pom COPY pom.xml . #copy source COPY src ./src # build the app (no dependency download here) RUN --mount=type=cache,target=/root/.m2 mvn clean package -Dmaven.test.skip # split the built app into multiple layers to improve layer rebuild RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar ########JRE run stage######## FROM openjdk:11.0-jre WORKDIR /app #copy built app layer by layer ARG DOCKER_PACKAGING_DIR=/app/target/docker-packaging COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/lib /app/lib COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/classes /app/classes COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/META-INF /app/META-INF #run the app CMD java -cp .:classes:lib/* \ -Djava.security.egd=file:/dev/./urandom \ foo.bar.MySpringBootApplication
la source