Ich möchte ein Docker-Image für meine Java-Anwendung erstellen. Die folgenden Überlegungen sollten jedoch für die meisten kompilierten Sprachen zutreffen.
Auf meinem Build-Server möchte ich ein Docker-Image für meine Anwendung als lieferbares Produkt erstellen. Dazu muss ich die Anwendung mit einem Build-Tool (normalerweise Gradle, Maven oder Ant) kompilieren und dann die erstellte JAR-Datei zum Docker-Image hinzufügen. Da ich möchte, dass das Docker-Image nur die JAR-Datei ausführt, beginne ich natürlich mit einem Basis-Image, auf dem Java bereits installiert ist.
In diesem Fall steuert mein Build-Tool den gesamten Prozess. Es bereitet also die JAR-Datei vor und ruft nach dem Erstellen des JAR Docker auf, um das Image zu erstellen. Dies funktioniert, da der JAR zuvor erstellt wurde und Docker den Erstellungsprozess zum Erstellen des JAR nicht kennt.
Aber meine Dockerfile ist nicht mehr eigenständig. Es hängt von den Schritten ab, die außerhalb von Docker ausgeführt werden müssen. In meiner Docker-Datei habe ich eine COPY
- oder ADD
-Anweisung, die die JAR-Datei in das Image kopieren soll. Diese Anweisung schlägt fehl, wenn die Dose nicht zuvor erstellt wurde. Das Ausführen der Docker-Datei funktioniert möglicherweise nicht. Dies wird zu einem Problem, wenn Sie Dienste integrieren möchten, die nur mit der aktuellen Docker-Datei erstellt werden, z. B. der Auto-Build-Funktion von DockerHub.
In diesem Fall werden alle erforderlichen Schritte zum Erstellen des Images zur Dockerfile hinzugefügt, sodass das Image durch einfaches Ausführen des Docker-Builds erstellt werden kann.
Das Hauptproblem bei diesem Ansatz besteht darin, dass es nicht möglich ist, Befehle zu einer Dockerfile hinzuzufügen, die außerhalb des erstellten Docker-Images ausgeführt werden sollten. Das heißt, ich muss meinen Quellcode und meine Build-Tools zum Docker-Image hinzufügen und meine JAR-Datei im Image erstellen. Dies führt dazu, dass mein Image größer wird, als es aufgrund der hinzugefügten Dateien ist, die zur Laufzeit nicht erforderlich sind. Dadurch werden meinem Bild auch zusätzliche Ebenen hinzugefügt.
Da @ adrian-mouat darauf hinwies, wenn ich die Quellen hinzufügen, die Anwendung erstellen und die Quellen in einer RUN-Anweisung löschen würde, könnte ich das Hinzufügen von unnötigen Dateien und Ebenen zum Docker-Image vermeiden. Dies würde bedeuten, einen wahnsinnig verketteten Befehl zu erstellen.
In diesem Fall teilen wir unseren Build in zwei Teile auf: Zuerst erstellen wir die JAR-Datei mit unserem Build-Tool und laden sie in ein Repository (Maven- oder Ivy-Repository) hoch. Wir lösen dann einen separaten Docker-Build aus, der nur die JAR-Datei aus dem Repository hinzufügt.
Meiner Meinung nach wäre der bessere Weg das Build-Tool den Prozess steuern lassen . Dies führt zu einem sauberen Docker-Image. Da das Image das ist, was wir liefern wollen, ist dies von Bedeutung. Um zu vermeiden, dass eine möglicherweise nicht funktionierende Docker-Datei herumliegt, sollte diese als Teil des Builds erstellt werden. Daher würde niemand versehentlich einen defekten Build starten.
Dies erlaubt mir jedoch nicht, mich mit DockerHub zu integrieren.
Gibt es eine andere Möglichkeit, wie ich vermisse?
Der Docker-Registrierungshub verfügt über ein Maven-Image , mit dem Java-Container erstellt werden können.
Bei diesem Ansatz muss auf der Build-Maschine weder Java noch Maven vorinstalliert sein. Docker steuert den gesamten Build-Prozess.
├── Dockerfile
├── pom.xml
└── src
├── main
│ ├── Java
│ │ └── org
│ │ └── demo
│ │ └── App.Java
│ └── resources
│ └── log4j.properties
└── test
└── Java
└── org
└── demo
└── AppTest.Java
Container ist wie folgt aufgebaut:
docker build -t my-maven .
Und wie folgt laufen:
$ docker run -it --rm my-maven
0 [main] INFO org.demo.App - hello world
FROM maven:3.3-jdk-8-onbuild
CMD ["Java","-jar","/usr/src/app/target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar"]
Wenn Sie Ihren Container optimieren möchten, um die Quelle auszuschließen, können Sie eine Docker-Datei erstellen, die nur die erstellte jar enthält:
FROM Java:8
ADD target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar /opt/demo/demo-1.0-SNAPSHOT-jar-with-dependencies.jar
CMD ["Java","-jar","/opt/demo/demo-1.0-SNAPSHOT-jar-with-dependencies.jar"]
Und bauen Sie den Container in zwei Schritten auf:
docker run -it --rm -w /opt/maven \
-v $PWD:/opt/maven \
-v $HOME/.m2:/root/.m2 \
maven:3.3-jdk-8 \
mvn clean install
docker build -t my-app .
__
Docker verfügt jetzt über eine mehrstufige Build Fähigkeit. Dadurch kann Docker einen Container mit einem Image erstellen, das die Build-Tools enthält, aber ein Image nur mit den Laufzeitabhängigkeiten ausgeben.
Das folgende Beispiel veranschaulicht dieses Konzept. Beachten Sie, wie die JAR-Datei aus dem Zielverzeichnis der ersten Erstellungsphase kopiert wird
FROM maven:3.3-jdk-8-onbuild
FROM Java:8
COPY --from=0 /usr/src/app/target/demo-1.0-SNAPSHOT.jar /opt/demo.jar
CMD ["Java","-jar","/opt/demo.jar"]
Struktur der Java-Anwendung
Demo
└── src
| ├── main
| │ ├── Java
| │ │ └── org
| │ │ └── demo
| │ │ └── App.Java
| │ └── resources
| │ └── application.properties
| └── test
| └── Java
| └── org
| └── demo
| └── App.Java
├──── Dockerfile
├──── pom.xml
Inhalt von Dockerfile
FROM Java:8
EXPOSE 8080
ADD /target/demo.jar demo.jar
ENTRYPOINT ["Java","-jar","demo.jar"]
Befehle zum Erstellen und Ausführen von Image
- Wechseln Sie in das Verzeichnis des Projekts. Lassen Sie uns D:/Demo sagen
$ cd D/demo
$ mvn clean install
$ docker build demo .
$ docker run -p 8080:8080 -t demo
Prüfen Sie, ob der Container läuft oder nicht
$ docker ps
Die Ausgabe wird sein
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
55c11a464f5a demo1 "Java -jar demo.jar" 21 seconds ago Up About a minute 0.0.0.0:8080->8080/tcp cranky_mayer
Am einfachsten geht es mit das Build-Tool den Prozess steuern lassen. Andernfalls müssten Sie die Build-Datei Ihres Build-Tools (wie pom.xml
für Maven oder build.gradle
für Gradle) sowie eine Dockerfile
-Datei verwalten.
Ein einfacher Weg, um einen Docker-Container für Ihre Java-App zu erstellen, ist die Verwendung von Jib , das als Maven und Gradle plugins verfügbar ist.
Wenn Sie beispielsweise Maven verwenden und Ihren Container zu Ihrem laufenden Docker-Dämon erstellen möchten, können Sie einfach diesen Befehl ausführen:
mvn compile com.google.cloud.tools:jib-maven-plugin:0.9.2:dockerBuild
Sie können auch direkt in eine Docker-Registry einbauen mit Jib , ohne docker
installieren zu müssen, einen Docker-Daemon auszuführen (der Rootberechtigungen erfordert) oder eine Dockerfile
zu schreiben. Es ist auch schneller und erstellt Bilder reproduzierbar.
Weitere Informationen zu Jib finden Sie im Github-Repo: https://github.com/GoogleContainerTools/jib
Einige Sachen:
Wenn Sie Dateien in derselben Anweisung löschen, in der Sie sie hinzufügen, beanspruchen sie keinen Platz im Bild. Wenn Sie sich einige Dockerfiles für die offiziellen Bilder ansehen, werden Sie sehen, dass sie den Quellcode herunterladen, erstellen und in demselben Schritt löschen (zB https://github.com/docker-library/python/blob/0fa3202789648132971160f686f5a37595108f44 /3.5/slim/Dockerdatei ). Dies bedeutet, dass Sie einige lästige Gymnastik machen müssen, aber es ist durchaus machbar.
Ich sehe das Problem nicht mit zwei separaten Dockerfiles. Das Schöne daran ist, dass Sie die JRE anstelle der JDK verwenden können, um Ihr Glas zu hosten.
es gibt alternative Verwendungsmöglichkeiten für die Ausführung von Jar- oder Kriegspaketen
beispiel Dockerfile
FROM base
ADD sample.jar renamed.jar
ENV HEAP_SIZE 256m
ENTRYPOINT exec Java -Xms$HEAP_SIZE -Xmx$HEAP_SIZE -jar renamed.jar
zusätzlich Paketbereitstellungsbeispiel auf Tomcat
FROM Tomcat7
ADD sample.war ${CATALINA_HOME}/webapps/ROOT.war
CMD ${CATALINA_HOME}/bin/catalina.sh run
Erstellen von Dockerfiles als Bild
cp Tomcat.dockerfile /workingdir/Dockerfile
docker build -t name /workingdir/Dockerfile .
Bilder auflisten
docker images
Verwenden Sie image, um einen Container zu erstellen
docker run --name cont_name --extra-vars var1=val1 var2=val2 imagename
Containerisieren Sie Ihre Java -Anwendung mit dem Jib-Tool, ohne Dockerfile zu schreiben
Jib ist ein Open-Source-Tool Java, das von Google zum Erstellen von Docker-Images von Java - Anwendungen verwaltet wird. Dies vereinfacht die Containerisierung, da wir keine Docker-Datei schreiben müssen . Tatsächlich muss nicht einmal Docker installiert sein , um die Docker-Images selbst zu erstellen und zu veröffentlichen.
Google veröffentlicht Jib sowohl als Maven- als auch als Gradle-Plugin. https://github.com/GoogleContainerTools/jib
Containerisieren Sie Ihre Java -Anwendung mit dem Maven-Projekt
https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin#quickstart
Containerisieren Sie Ihre Java -Anwendung mit Gradle-Projekt
https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#quickstart
Hier Ich beschreibe, wie ich das in meiner Entwicklungsumgebung mache.
Ich hoffe es hilft.
Wir haben das Spotify Docker Maven Plugin eine Zeit lang verwendet. Mit dem Plugin können Sie einen Docker Build an eine Phase des Maven-Lebenszyklus binden.
Ein Beispiel: Führen Sie den Docker-Build nach dem Paketieren (phase: package) Ihrer Anwendung aus, indem Sie das Plugin so konfigurieren, dass Ihre erstellte Anwendung dem Docker-Build-Kontext als Ressource hinzugefügt wird. Führen Sie in der Bereitstellungsphase das Docker Push-Ziel aus, um Ihr Docker-Image in eine Registrierung zu verschieben. Dies kann neben dem normalen Implementierungs-Plugin ausgeführt werden, das das Artefakt in einem Repository wie Nexus veröffentlicht.
Später haben wir den Build in zwei separate Jobs auf dem CI-Server aufgeteilt. Da Docker nur eine Möglichkeit zum Ausführen Ihrer Anwendung ist (manchmal benötigen wir die freigegebene Anwendung in verschiedenen Umgebungen und nicht nur Docker), sollte der Maven-Build nicht auf Docker angewiesen sein.
Der erste Job gibt die Anwendung in Nexus frei (über Maven deploy). Der zweite Job (der eine Downstream-Abhängigkeit des ersten Jobs sein kann) lädt das neueste Release-Artefakt herunter, führt den Docker-Build durch und überträgt das Image in die Registry. Um die neueste Version herunterzuladen, verwenden wir das Versions Maven Plugin (Versionen: use-latest-Releases) sowie das Maven Dependency Plugin (Abhängigkeit: get und Abhängigkeit: Kopie).
Der zweite Job kann auch für eine bestimmte Version der Anwendung gestartet werden, um das Docker-Image für eine ältere Version (neu) zu erstellen. Außerdem können Sie eine Build-Pipeline (auf Jenkins) verwenden, die beide Jobs ausführt und die Release-Version oder das Release-Artefakt an den Docker-Build übergibt.