Een docker-container bouwen voor een Java-toepassing

Wat ik wil doen is een docker-image bouwen voor mijn Java-toepassing, maar de volgende overwegingen zouden moeten gelden voor de meeste gecompileerde talen.

probleem

Op mijn build-server wil ik als deliverable een docker-image voor mijn applicatie produceren. Hiervoor moet ik de applicatie compileren met behulp van een bouwtool (meestal Gradle, Maven of Ant) en vervolgens het gemaakte JAR-bestand toevoegen aan de docker-afbeelding. Omdat ik wil dat de docker-image alleen het JAR-bestand uitvoert, ga ik natuurlijk uit van een basisimage waarop Java al is geïnstalleerd.

Er zijn drie manieren om dit te doen:

laat de build-tool het proces beheersen

In dit geval bestuurt mijn build-tool het hele proces. Dus het bereidt het JAR-bestand voor en nadat de JAR is gemaakt, roept het Docker aan om de afbeelding te maken. Dit werkt omdat de JAR van tevoren wordt gemaakt en Docker zich niet bewust is van het bouwproces dat nodig is om de JAR te maken.

Maar mijn Dockerfile staat niet langer op zichzelf. Het hangt af van de stappen die buiten Docker plaatsvinden om het te laten werken. In mijn Dockerfile heb ik een COPY– of ADD-statement dat het JAR-bestand naar de afbeelding moet kopiëren. Deze verklaring zal mislukken als de pot niet van tevoren is gemaakt. Dus alleen het uitvoeren van de Dockerfile werkt mogelijk niet. Dit wordt een probleem als u wilt integreren met services die alleen zijn gebouwd met het huidige Dockerfile, zoals de functie voor automatisch bouwen op DockerHub.

Laat Docker de build besturen

In dit geval worden alle noodzakelijke stappen om de afbeelding te maken toegevoegd aan de Dockerfile, zodat de afbeelding kan worden gemaakt door gewoon de Docker-build uit te voeren.

Het grootste probleem met deze aanpak is dat er geen manier is om aan een Dockerfile commando’s toe te voegen die moeten worden uitgevoerd buiten de docker-image die wordt gemaakt. Dit betekent dat ik mijn broncode en mijn build-tools moet toevoegen aan de docker-afbeelding en mijn JAR-bestand in de afbeelding moet bouwen. Dit zal ertoe leiden dat mijn afbeelding groter wordt dan nodig is vanwege alle toegevoegde bestanden die tijdens runtime niet nodig zijn. Dit zal ook extra lagen aan mijn afbeelding toevoegen.

Bewerken:

Zoals @adrian-mouat erop wees dat als ik de bronnen zou toevoegen, de applicatie zou bouwen en de bronnen zou verwijderen in één RUN-statement, ik zou kunnen voorkomen dat ik onnodige bestanden en lagen aan de Docker-afbeelding zou toevoegen. Dit zou betekenen dat je een krankzinnig geketend commando moet maken.

twee afzonderlijke builds

In dit geval splitsen we onze build in tweeën: eerst maken we het JAR-bestand met behulp van onze build-tool en uploaden deze naar een repository (Maven- of Ivy-repository). We activeren dan een aparte Docker-build die alleen het JAR-bestand uit de repository toevoegt.

conclusie

Naar mijn mening zou de betere manier zijn om de bouwtool het proces te laten beheersen. Dit zal resulteren in een schoon docker-beeld en aangezien het beeld is wat we willen leveren, is dit van belang. Om te voorkomen dat er een mogelijk niet werkend Dockerfile rondslingert, moet dit als onderdeel van de build worden gemaakt. Dus niemand zou het per ongeluk gebruiken om een ​​kapotte build te starten.

Maar hierdoor kan ik niet integreren met DockerHub.

vraag

Is er nog een manier die ik mis?

update juni 2020

In de jaren sinds ik deze vraag voor het eerst heb gemaakt, is er veel veranderd. Op dit moment zou ik aanraden om Googels JIB Toolte gebruiken. Het integreert met de meest voorkomende Java Build Tools (Maven en Gradle) en stelt u in staat om rechtstreeks vanuit uw build een container te maken. Dit is veel beknopter dan alle oude benaderingen die ik al die jaren geleden heb overwogen.

update februari 2021

Ik vond deze blogpost en video van James Ward die beter weergeven wat momenteel de stand van de techniek is.
https://cloud.google .com/blog/topics/developers-practitioners/comparing-containerization-methods-buildpacks-jib-and-dockerfile


Antwoord 1, autoriteit 100%

De docker-registerhub heeft een Maven-afbeeldingdie kan worden gebruikt om java te maken containers.

Bij deze benadering hoeft Java of Maven niet vooraf te zijn geïnstalleerd op de bouwmachine, Docker regelt het hele bouwproces.

Voorbeeld

├── Dockerfile
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── org
    │   │       └── demo
    │   │           └── App.java
    │   └── resources
    │       └── log4j.properties
    └── test
        └── java
            └── org
                └── demo
                    └── AppTest.java

Afbeelding is als volgt opgebouwd:

docker build -t my-maven .

En voer als volgt uit:

$ docker run -it --rm my-maven
0    [main] INFO  org.demo.App  - hello world

Dockerbestand

FROM maven:3.3-jdk-8-onbuild
CMD ["java","-jar","/usr/src/app/target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar"]

Bijwerken

Als je je afbeelding wilt optimaliseren om de bron uit te sluiten, kun je een Docker-bestand maken dat alleen de ingebouwde pot bevat:

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"]

En bouw de afbeelding in twee stappen:

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 .

__

Update (27-07-2017)

Docker heeft nu een multi-stage build-mogelijkheid . Hierdoor kan Docker een image bouwen met de buildtools, maar alleen de runtime-afhankelijkheden.

Het volgende voorbeeld demonstreert dit concept, let op hoe de jar wordt gekopieerd uit de doelmap van de eerste bouwfase

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"]

Antwoord 2, autoriteit 19%

Structuur van Java-toepassing

Demo
└── src
|    ├── main
|    │   ├── java
|    │   │   └── org
|    │   │       └── demo
|    │   │           └── App.java
|    │   └── resources
|    │       └── application.properties
|    └── test
|         └── java
|               └── org
|                   └── demo
|                         └── App.java  
├──── Dockerfile
├──── pom.xml

Inhoud van Dockerfile

FROM java:8
EXPOSE 8080
ADD /target/demo.jar demo.jar
ENTRYPOINT ["java","-jar","demo.jar"]

Opdrachten voor het bouwen en uitvoeren van een afbeelding

  • Ga naar de directory van project.Laten we zeggen D:/Demo
$ cd D/demo
$ mvn clean install
$ docker build demo .
$ docker run -p 8080:8080 -t demo

Controleer of de container actief is of niet

$ docker ps

De uitvoer zal zijn

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

Antwoord 3, autoriteit 11%

De gemakkelijkste manier is om het bouwprogramma het proces te laten besturen. Anders zou u het buildbestand van uw build-tool moeten onderhouden (zoals pom.xmlvoor Maven of build.gradlevoor Gradle), evenals een Dockerfile.

Een eenvoudige manier om een ​​Docker-container voor uw Java-app te bouwen, is door Jibte gebruiken. beschikbaar als Mavenen Gradleplug-ins .

Als u bijvoorbeeld Maven gebruikt en uw container wilt bouwen naar uw draaiende Docker-daemon, kunt u dit ene commando uitvoeren:

mvn compile com.google.cloud.tools:jib-maven-plugin:0.9.2:dockerBuild

Je kunt ook rechtstreeks bouwen naar een Docker-registermet Jib zonder dat u dockerhoeft te installeren, voer een Docker-daemon uit (waarvoor root-rechten vereist zijn), of schrijf een Dockerfile. Het is ook sneller en maakt reproduceerbare afbeeldingen.

Bekijk meer over Jib op de Github-repo: https://github.com/GoogleContainerTools/jib


Antwoord 4, autoriteit 6%

We hebben een tijdje de Spotify Docker Maven Plugingebruikt. Met de plug-in kunt u een Docker-build binden aan een fase van de Maven-levenscyclus.

Een voorbeeld:
Voer de Docker-build uit na het verpakken (fase: pakket) van uw toepassing door de plug-in te configureren om uw gebouwde toepassing als een hulpmiddel toe te voegen aan de Docker-buildcontext. Voer in de implementatiefase het Docker-pushdoel uit om uw Docker-image naar een register te pushen. Dit kan worden uitgevoerd naast de normale implementatie-plug-in, die het artefact publiceert in een repository zoals Nexus.

Later hebben we de build gesplitst in twee afzonderlijke taken op de CI-server. Aangezien Docker slechts één manier is om uw applicatie uit te voeren (soms hebben we de vrijgegeven applicatie niet alleen in verschillende omgevingen nodig, niet alleen Docker), zou de Maven-build niet op Docker moeten vertrouwen.

Dus de eerste taak geeft de toepassing vrij in Nexus (via Maven-implementatie). De tweede taak (die een downstream-afhankelijkheid van de eerste taak kan zijn) downloadt het nieuwste release-artefact, voert de Docker-build uit en pusht de afbeelding naar het register. Voor het downloaden van de nieuwste release gebruiken we de Versions Maven Plugin(versions:use-latest -releases) evenals de Maven Dependency Plugin(dependency:get en afhankelijkheid:kopie).

De tweede taak kan ook worden gestart voor een specifieke versie van de applicatie om de Docker-image te (her)bouwen voor een oudere release. Bovendien kunt u een build-pipeline gebruiken (op Jenkins), die beide taken uitvoert en de releaseversie of het release-artefact doorgeeft aan de Docker-build.


Antwoord 5, autoriteit 6%

Container uw Java-toepassing met behulp van de Jib-tool zonder het dockerbestand te schrijven

Jibis een open-source Java-tool die wordt onderhouden door Google voor het bouwen van Docker-images van Java-applicaties. Het vereenvoudigt containerisatie omdat we geen dockerfile hoeven te schrijven. En eigenlijk we hoeven niet eens docker geïnstalleerd te hebbenom de docker-images zelf te maken en te publiceren.

Google publiceert Jib als zowel een Maven- als een Gradle-plug-in.
https://github.com/GoogleContainerTools/jib

Container uw Java-toepassing met behulp van het Maven-project

https://github.com/GoogleContainerTools/jib /tree/master/jib-maven-plugin#quickstart

Container uw Java-toepassing met Gradle-project

https://github.com/GoogleContainerTools/jib /tree/master/jib-gradle-plugin#quickstart


Antwoord 6, autoriteit 3%

Een paar dingen:

  • Als je bestanden verwijdert in dezelfde instructie als waarin je ze toevoegt, nemen ze geen ruimte in beslag in de afbeelding. Als je naar enkele van de Docker-bestanden kijkt voor de officiële afbeeldingen, zul je zien dat ze de bron downloaden, bouwen en verwijderen in dezelfde stap (bijv. https://github.com/docker-library/python/blob/0fa3202789648132971160f686f5a37595108f44/3.5/slim>/Dockerfile). Dit betekent dat je wat vervelende gymnastiek moet doen, maar het is perfect te doen.

  • Ik zie het probleem niet met twee aparte Dockerfiles. Het leuke hiervan is dat je de JRE kunt gebruiken in plaats van de JDK om je jar te hosten.


Antwoord 7

er zijn alternatieve toepassingen voor het uitvoeren van jar- of oorlogspakketten

  • voeg pot toe aan afbeelding.
  • heapsize instellen voor java
  • voer het jar-commando uit via het toegangspunt

voorbeeld dockerbestand

FROM base
ADD sample.jar renamed.jar
ENV HEAP_SIZE 256m
ENTRYPOINT exec java -Xms$HEAP_SIZE -Xmx$HEAP_SIZE -jar renamed.jar

Bovendien voorbeeld van pakketimplementatie op Tomcat

FROM tomcat7
ADD sample.war ${CATALINA_HOME}/webapps/ROOT.war
CMD ${CATALINA_HOME}/bin/catalina.sh run

Dokkerbestanden bouwen als afbeelding

cp tomcat.dockerfile /workingdir/Dockerfile
docker build -t name /workingdir/Dockerfile .

Lijst afbeeldingen

docker images

Gebruik afbeelding om een ​​container te maken

docker run --name cont_name --extra-vars var1=val1 var2=val2 imagename

Antwoord 8

Hierbeschrijf ik hoe ik het doe in mijn ontwikkelomgeving.

  • Bouw de oorlog/jar lokaal met Maven
  • Kopieer het naar een lokale Docker-map
  • Voer Intellij Docker-plug-in uit die een docker-image maakt die de war/jar bevat, de applicatieserver uitvoert en deze implementeert op de externe Docker-server

Hopelijk helpt het.

Other episodes