Arbeitsbeispiel.
Dies ist kein Spring Boot-Tutorial. Dies ist die aktualisierte Antwort auf eine Frage zum Ausführen eines Maven-Builds in einem Docker-Container.
Frage ursprünglich vor 4 Jahren gepostet.
1. Generieren Sie eine Anwendung
Verwenden Sie den Federinitialisierer, um eine Demo-App zu erstellen
https://start.spring.io/
Extrahieren Sie das Zip-Archiv lokal
2. Erstellen Sie eine Docker-Datei
#
# 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"]
Hinweis
- In diesem Beispiel wird ein mehrstufiger Build verwendet . In der ersten Phase wird der Code erstellt. Die zweite Stufe enthält nur das erstellte Glas und eine JRE, um es auszuführen (beachten Sie, wie das Glas zwischen den Stufen kopiert wird).
3. Erstellen Sie das Image
docker build -t demo .
4. Führen Sie das Image aus
$ 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)
Sonstiges
Lesen Sie in der Docker-Hub-Dokumentation, wie der Maven-Build für die Verwendung eines lokalen Repositorys zum Zwischenspeichern von Jars optimiert werden kann.
Update (07.02.2019)
Diese Frage ist jetzt 4 Jahre alt und in dieser Zeit kann man mit Recht sagen, dass die Erstellung von Anwendungen mit Docker erhebliche Änderungen erfahren hat.
Option 1: Mehrstufiger Aufbau
Mit diesem neuen Stil können Sie leichtere Bilder erstellen, die Ihre Build-Tools und Ihren Quellcode nicht enthalten.
In diesem Beispiel wird erneut das offizielle Maven- Basis-Image verwendet, um die erste Phase des Builds mit einer gewünschten Version von Maven auszuführen. Der zweite Teil der Datei definiert, wie das erstellte Glas zum endgültigen Ausgabebild zusammengesetzt wird.
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"]
Hinweis:
- Ich verwende das verzweifelte Basis-Image von Google , das gerade genug Laufzeit für eine Java-App bieten soll.
Option 2: Ausleger
Ich habe diesen Ansatz nicht verwendet, aber er scheint eine Untersuchung wert zu sein, da Sie damit Bilder erstellen können, ohne böse Dinge wie Dockerfiles erstellen zu müssen :-)
https://github.com/GoogleContainerTools/jib
Das Projekt verfügt über ein Maven-Plugin, das die Verpackung Ihres Codes direkt in Ihren Maven-Workflow integriert.
Ursprüngliche Antwort (der Vollständigkeit halber enthalten, aber vor Ewigkeiten geschrieben)
Versuchen Sie es mit den neuen offiziellen Bildern, es gibt eines für Maven
https://registry.hub.docker.com/_/maven/
Das Image kann verwendet werden, um Maven zur Erstellungszeit auszuführen, um eine kompilierte Anwendung zu erstellen, oder um, wie in den folgenden Beispielen, einen Maven-Build in einem Container auszuführen.
Beispiel 1 - Maven läuft in einem Container
Der folgende Befehl führt Ihren Maven-Build in einem Container aus:
docker run -it --rm \
-v "$(pwd)":/opt/maven \
-w /opt/maven \
maven:3.2-jdk-7 \
mvn clean install
Anmerkungen:
- Das Schöne an diesem Ansatz ist, dass die gesamte Software im Container installiert ist und ausgeführt wird. Benötigen Sie nur Docker auf dem Host-Computer.
- Siehe Dockerfile für diese Version
Beispiel 2 - Verwenden Sie Nexus zum Zwischenspeichern von Dateien
Führen Sie den Nexus-Container aus
docker run -d -p 8081:8081 --name nexus sonatype/nexus
Erstellen Sie eine "settings.xml" -Datei:
<settings>
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://nexus:8081/content/groups/public/</url>
</mirror>
</mirrors>
</settings>
Führen Sie nun die Maven-Verknüpfung mit dem Nexus-Container aus, damit Abhängigkeiten zwischengespeichert werden
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
Anmerkungen:
- Ein Vorteil der Ausführung von Nexus im Hintergrund besteht darin, dass andere Repositorys von Drittanbietern über die Administrator-URL transparent für die Maven-Builds verwaltet werden können, die in lokalen Containern ausgeführt werden.
mavenCentral()
in meinen Gradle-Abhängigkeiten durch ersetztmaven {url "http://nexus:8081..."
und bekomme jetzt nur noch Auflösungsprobleme.Es kann viele Möglichkeiten geben. Aber ich habe zwei Möglichkeiten implementiert
Das gegebene Beispiel ist ein Maven-Projekt.
1. Verwenden von Dockerfile im Maven-Projekt
Verwenden Sie die folgende Dateistruktur:
Demo └── src | ├── main | │ ├── java | │ └── org | │ └── demo | │ └── Application.java | │ | └── test | ├──── Dockerfile ├──── pom.xml
Und aktualisieren Sie die Docker-Datei wie folgt:
FROM java:8 EXPOSE 8080 ADD /target/demo.jar demo.jar ENTRYPOINT ["java","-jar","demo.jar"]
Navigieren Sie zum Projektordner und geben Sie den folgenden Befehl ein, mit dem Sie ein Image erstellen und dieses Image ausführen können:
$ mvn clean $ mvn install $ docker build -f Dockerfile -t springdemo . $ docker run -p 8080:8080 -t springdemo
Holen Sie sich Videos bei Spring Boot mit Docker
2. Verwenden von Maven-Plugins
Fügen Sie das angegebene Maven-Plugin hinzu
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>
Navigieren Sie zum Projektordner und geben Sie den folgenden Befehl ein. Sie können ein Image erstellen und dieses Image ausführen:
$ mvn clean package docker:build $ docker images $ docker run -p 8080:8080 -t <image name>
Im ersten Beispiel erstellen wir Dockerfile und stellen das Basis-Image bereit und fügen jar an. Danach führen wir den Docker-Befehl aus, um ein Image mit einem bestimmten Namen zu erstellen, und führen dann das Image aus.
Während im zweiten Beispiel verwenden wir Maven Plugin in die wir bieten
baseImage
undimageName
so brauchen wir nicht zu schaffen Dockerfile hier .. nach Maven - Projekt Verpackung werden wir die Docker Bild erhalten , und wir müssen nur das Bild laufen ..quelle
Als Faustregel sollten Sie mit Maven eine fette JAR erstellen (eine JAR, die sowohl Ihren Code als auch alle Abhängigkeiten enthält).
Dann können Sie eine Docker-Datei schreiben , die Ihren Anforderungen entspricht (wenn Sie eine fette JAR erstellen können, benötigen Sie nur ein Basis-Betriebssystem wie CentOS und die JVM).
Dies ist, was ich für eine Scala-App (die Java-basiert ist) verwende.
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
Dadurch wird mit Java7 ein CentOS-basiertes Image erstellt. Beim Start wird Ihre App-JAR ausgeführt.
Die Bereitstellung erfolgt am besten über die Docker-Registrierung. Sie ähnelt einem Github für Docker-Images.
Sie können ein Bild wie folgt erstellen:
# current dir must contain the Dockerfile docker build -t username/projectname:tagname .
Sie können dann ein Bild folgendermaßen verschieben:
docker push username/projectname # this pushes all tags
Sobald sich das Image in der Docker-Registrierung befindet, können Sie es von überall auf der Welt abrufen und ausführen.
Weitere Informationen finden Sie im Docker-Benutzerhandbuch .
Beachten Sie Folgendes :
Sie können Ihr Repository auch in ein Image ziehen und das JAR als Teil der Containerausführung erstellen. Dies ist jedoch kein guter Ansatz, da sich der Code ändern kann und Sie möglicherweise ohne vorherige Ankündigung eine andere Version der App verwenden.
Das Bauen eines fetten Glases behebt dieses Problem.
quelle
RUN wget -O {project.build.finalname}.jar
Aber ich möchte das obige Glas von Nexus herunterladen.Hier ist mein Beitrag.
Ich werde nicht versuchen, alle Tools / Bibliotheken / Plugins aufzulisten, die vorhanden sind, um Docker mit Maven zu nutzen. Einige Antworten haben es bereits getan.
Stattdessen werde ich mich auf die Anwendungstypologie und den Dockerfile-Weg konzentrieren.
Dockerfile
ist wirklich ein einfaches und wichtiges Konzept von Docker (alle bekannten / öffentlichen Bilder hängen davon ab) und ich denke, dass der Versuch, das Verstehen und Verwenden vonDockerfile
s zu vermeiden, nicht unbedingt der bessere Weg ist, in die Docker-Welt einzutreten.Das Andocken einer Anwendung hängt von der Anwendung selbst und dem zu erreichenden Ziel ab
1) Für Anwendungen, die wir auf einem installierten / eigenständigen Java-Server ausführen möchten (Tomcat, JBoss usw.)
Der Weg ist schwieriger und das ist nicht das ideale Ziel, da dies die Komplexität erhöht (wir müssen den Server verwalten / warten) und weniger skalierbar und weniger schnell als eingebettete Server in Bezug auf das Erstellen / Bereitstellen / Aufheben der Bereitstellung ist.
Bei älteren Anwendungen kann dies jedoch als erster Schritt betrachtet werden.
Im Allgemeinen besteht die Idee hier darin, ein Docker-Image für den Server zu definieren und ein Image pro zu implementierender Anwendung zu definieren.
Die Docker-Images für die Anwendungen erzeugen das erwartete WAR / EAR, diese werden jedoch nicht als Container ausgeführt, und das Image für die Serveranwendung stellt die von diesen Images erzeugten Komponenten als bereitgestellte Anwendungen bereit.
Für große Anwendungen (Millionen von Codezeilen) mit vielen Legacy-Inhalten und so schwer zu migrieren, um auf eine eingebettete Full-Spring-Boot-Lösung zu migrieren, ist das wirklich eine schöne Verbesserung.
Ich werde diesen Ansatz nicht näher erläutern, da dies für kleinere Anwendungsfälle von Docker gilt, aber ich wollte die Gesamtidee dieses Ansatzes erläutern, da ich denke, dass es für Entwickler, die sich diesen komplexen Fällen stellen, gut ist zu wissen, dass einige Türen geöffnet sind Docker integrieren.
2) Für Anwendungen, die den Server selbst einbetten / booten (Spring Boot mit eingebettetem Server: Tomcat, Netty, Jetty ...)
Das ist das ideale Ziel mit Docker . Ich habe Spring Boot spezifiziert, weil dies ein wirklich schönes Framework ist, das auch ein sehr hohes Maß an Wartbarkeit aufweist, aber theoretisch könnten wir jeden anderen Java-Weg verwenden, um dies zu erreichen.
Im Allgemeinen besteht die Idee hier darin, ein Docker-Image pro bereitzustellender Anwendung zu definieren.
Die Docker-Images für die Anwendungen erzeugen eine JAR oder eine Reihe von JAR- / Klassen- / Konfigurationsdateien. Diese starten eine JVM mit der Anwendung (Java-Befehl), wenn wir aus diesen Images einen Container erstellen und starten.
Für neue Anwendungen oder Anwendungen, die für die Migration nicht zu komplex sind, muss dieser Weg gegenüber eigenständigen Servern bevorzugt werden, da dies der Standardweg und der effizienteste Weg zur Verwendung von Containern ist.
Ich werde diesen Ansatz detailliert beschreiben.
Andocken einer Maven-Anwendung
1) Ohne Spring Boot
Die Idee ist, mit Maven ein Fat Jar zu erstellen (das Maven Assembly Plugin und das Maven Shade Plugin helfen dabei), das sowohl die kompilierten Klassen der Anwendung als auch die erforderlichen Maven-Abhängigkeiten enthält.
Dann können wir zwei Fälle identifizieren:
Wenn es sich bei der Anwendung um eine Desktop- oder autonome Anwendung handelt (die nicht auf einem Server bereitgestellt werden muss): Wir können wie
CMD/ENTRYPOINT
inDockerfile
der Java-Ausführung der Anwendung Folgendes angeben :java -cp .:/fooPath/* -jar myJar
Wenn es sich bei der Anwendung um eine Serveranwendung handelt, z. B. Tomcat, ist die Idee dieselbe: ein dickes Glas der Anwendung abzurufen und eine JVM in der Anwendung auszuführen
CMD/ENTRYPOINT
. Aber hier mit einem wichtigen Unterschied: Wir müssen einige logische und spezifische Bibliotheken (org.apache.tomcat.embed
Bibliotheken und einige andere) einbeziehen, die den eingebetteten Server starten, wenn die Hauptanwendung gestartet wird.Wir haben einen umfassenden Leitfaden auf der Heroku-Website .
Für den ersten Fall (autonome Anwendung) ist dies eine einfache und effiziente Möglichkeit, Docker zu verwenden.
Für den zweiten Fall (Serveranwendung) funktioniert dies, ist aber nicht eindeutig, kann fehleranfällig sein und ist kein sehr erweiterbares Modell, da Sie Ihre Anwendung nicht in den Rahmen eines ausgereiften Frameworks wie Spring Boot stellen, das viele Funktionen ausführt von diesen Dingen für Sie und bietet auch ein hohes Maß an Erweiterung.
Dies hat jedoch einen Vorteil: Sie haben ein hohes Maß an Freiheit, da Sie die eingebettete Tomcat-API direkt verwenden.
2) Mit Spring Boot
Endlich geht es los.
Das ist einfach, effizient und sehr gut dokumentiert.
Es gibt wirklich mehrere Ansätze, um eine Maven / Spring Boot-Anwendung auf Docker auszuführen.
Alle aufzudecken wäre lang und vielleicht langweilig.
Die beste Wahl hängt von Ihren Anforderungen ab.
Wie auch immer, die Build-Strategie in Bezug auf Docker-Ebenen sieht gleich aus.
Wir möchten einen mehrstufigen Build verwenden: einen, der sich für die Abhängigkeitsauflösung auf Maven und für den Build stützt, und einen anderen, der sich beim Starten der Anwendung auf JDK oder JRE stützt.
Bauphase (Maven-Bild):
Über das,
mvn dependency:resolve-plugins
angekettet anmvn dependency:resolve
den Job kann zu tun , aber nicht immer.Warum ? Da diese Plugins und die
package
Ausführung zum Packen des Fat Jars möglicherweise von unterschiedlichen Artefakten / Plugins und sogar von demselben Artefakt / Plugin abhängen, wird möglicherweise immer noch eine andere Version verwendet. Ein sicherer Ansatz, der möglicherweise langsamer ist, besteht darin, Abhängigkeiten aufzulösen, indem genau dermvn
Befehl ausgeführt wird, der zum Packen der Anwendung verwendet wird (wodurch genau die benötigten Abhängigkeiten abgerufen werden), aber die Quellkompilierung übersprungen und der Zielordner gelöscht wird, um die Verarbeitung zu beschleunigen und zu beschleunigen Verhindern Sie eine unerwünschte Schichtwechselerkennung für diesen Schritt.Phase ausführen (JDK- oder JRE-Image):
Hier zwei Beispiele.
a) Ein einfacher Weg ohne Cache für heruntergeladene Maven-Abhängigkeiten
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
Nachteil dieser Lösung? Alle Änderungen in der Datei pom.xml bedeuten, dass die gesamte Ebene, die die Maven-Abhängigkeiten herunterlädt und speichert, neu erstellt wird. Dies ist im Allgemeinen für Anwendungen mit vielen Abhängigkeiten nicht akzeptabel (und Spring Boot zieht viele Abhängigkeiten), insgesamt, wenn Sie während der Image-Erstellung keinen Maven-Repository-Manager verwenden.
b) Ein effizienterer Weg mit Cache für heruntergeladene Maven-Abhängigkeiten
Der Ansatz ist hier der gleiche, jedoch werden Downloads von Maven-Abhängigkeiten im Docker-Builder-Cache zwischengespeichert.
Die Cache-Operation basiert auf dem Buildkit (experimentelle API von Docker).
Um das Buildkit zu aktivieren, muss die env-Variable DOCKER_BUILDKIT = 1 festgelegt werden (Sie können dies tun, wo Sie möchten: .bashrc, Befehlszeile, Docker-Daemon-JSON-Datei ...).
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
quelle