Come costruire un contenitore docker per un'applicazione Java


Quello che voglio fare è costruire un'immagine docker per la mia applicazione Java, ma le seguenti considerazioni dovrebbero essere vere per la maggior parte dei linguaggi compilati.

Problema

Sul mio server di compilazione Voglio produrre un'immagine docker per la mia applicazione come deliverable. Per questo devo compilare l'applicazione utilizzando alcuni strumenti di compilazione (in genere Gradle, Maven o Ant) e quindi aggiungere il file JAR creato all'immagine docker. Come voglio che l'immagine docker esegua solo il File JAR Ovviamente inizierò da un'immagine di base con Java già installato.

Ci sono tre modi per farlo:

Lascia che lo strumento di compilazione controlli il processo

In questo caso il mio strumento di compilazione controlla l'intero processo. Quindi prepara il file JAR e dopo che il JAR è stato creato chiama Docker per creare l'immagine. Funziona come il JAR viene creato in anticipo e Docker può essere ignaro del processo di compilazione necessario per creare il JAR.

Ma il mio Dockerfile è no più standalone. Dipende dai passaggi che si verificano al di fuori della finestra mobile per funzionare. Nel mio Dockerfile avrò un'istruzione COPY o ADD che dovrebbe copiare il file JAR nell'immagine. Questa affermazione fallirà quando il jar non viene creato in anticipo. Quindi solo l'esecuzione del Dockerfile potrebbe non funzionare. Questo diventa un problema se si desidera integrarsi con servizi che si compilano utilizzando l'attuale Dockerfile come la funzione di auto-build su DockerHub.

Lascia che la finestra mobile controlli il costruire

In questo caso tutti i passaggi necessari per creare l'immagine vengono aggiunti al Dockerfile in modo che l'immagine possa essere creata semplicemente eseguendo la build Docker.

Il problema principale con questo approccio è che non c'è modo di aggiungere a un Dockerfile comandi che dovrebbero essere eseguiti al di fuori dell'immagine docker creata. Ciò significa che devo aggiungere il mio codice sorgente e i miei strumenti di compilazione all'immagine docker e creare il mio file JAR all'interno dell'immagine. Ciò comporterà che la mia immagine sia più grande di quella deve essere dovuto a tutti i file aggiunti che non saranno necessari in fase di runtime. Questo aggiungerà anche livelli extra alla mia immagine.

Modifica:

Come ha sottolineato @adrian-mouat se aggiungessi i sorgenti, costruissi l'applicazione e cancellassi i sorgenti in un'istruzione RUN potrei evitare di aggiungere file e livelli non necessari all'immagine Docker. Ciò significherebbe creare un folle comando concatenato.

Due build separate

In questo caso dividiamo la nostra build in due: prima creiamo il file JAR utilizzando il nostro strumento di compilazione e caricarlo in un repository (Maven o Ivy repository). Quindi attiviamo una build Docker separata che aggiunge solo il file JAR dal repository.

Conclusione

A mio parere il modo migliore sarebbe lasciare che lo strumento di compilazione controlli il processo. Ciò si tradurrà in un'immagine docker pulita e poiché l'immagine è ciò che vogliamo fornire, questo è importante. Per evitare di avere un Dockerfile potenzialmente non funzionante in giro questo dovrebbe essere creato come parte della build. Quindi nessuno lo userebbe accidentalmente per avviare una build rotta.

Ma questo non mi permetterà di integrarmi con DockerHub.

Domanda

C'è un altro modo in cui mi manca?

Aggiornamento giugno 2020

Negli anni da quando ho creato questa domanda molte cose sono cambiate. A questo punto vorrei sostenere l'uso di Strumento JIB di Googel . Si integra con i più comuni strumenti di compilazione Java (Maven e Gradle) e consente di crea un contenitore direttamente dalla tua build. Questo è molto più conciso di qualsiasi vecchio approccio che ho considerato tutti questi anni fa.

Author: Tobias Kremer, 2015-07-29

8 answers

L'hub del registro docker ha un'immagine Maven che può essere utilizzata per creare contenitori java.

Usando questo approccio la macchina di compilazione non ha bisogno di avere Java o Maven preinstallati, Docker controlla l'intero processo di compilazione.

Esempio

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

L'immagine è costruita come segue:

docker build -t my-maven .

Ed eseguire come segue:

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

File finestra mobile

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

Aggiornare

Se si voleva ottimizzare l'immagine per escludere la fonte si potrebbe creare un file Dockerfile che include solo il jar costruito:

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

E costruisci l'immagine in due passaggi:

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 .

__

Aggiornamento (2017-07-27)

Docker ora ha una capacità di compilazione a più stadi . Ciò consente a Docker di creare un'immagine contenente gli strumenti di compilazione ma solo le dipendenze di runtime.

Il seguente esempio dimostra questo concetto, si noti come il jar viene copiato dalla directory di destinazione della prima fase di compilazione

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"]
 32
Author: Mark O'Connor, 2019-10-02 09:45:07

Struttura dell'applicazione java

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

Contenuto del Dockerfile

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

Comandi per creare ed eseguire image

  • Vai alla directory del progetto.Diciamo D:/Demo
$ cd D/demo
$ mvn clean install
$ docker build demo .
$ docker run -p 8080:8080 -t demo

Verificare che il contenitore sia in esecuzione o meno

$ docker ps

L'output sarà

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
 7
Author: Riddhi Gohil, 2016-04-04 09:02:49

Il modo più semplice è lasciare che lo strumento di compilazione controlli il processo. Altrimenti, dovresti mantenere il file di compilazione del tuo strumento di compilazione (come pom.xml per Maven o build.gradle per Gradle) e Dockerfile.

Un modo semplice per creare un contenitore Docker per la tua app Java è usare Jib, che è disponibile come Maven e Gradle plugin.

Ad esempio, se stai usando Maven e vuoi costruire il tuo contenitore per la tua corsa Docker daemon, puoi semplicemente eseguire questo comando:

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

Puoi anche creare direttamente in un registro Docker con Jib senza bisogno di installare docker, esegui un demone Docker (che richiede i privilegi di root) o scrivi un Dockerfile. È anche più veloce e crea immagini riproducibili.

Vedi di più su Jib al suo repository Github: https://github.com/GoogleContainerTools/jib

 4
Author: Qingyang Chen, 2018-07-09 13:01:13

Abbiamo usato il plugin Spotify Docker Maven per un po'. Il plugin consente di associare un Docker costruirlo a una fase del ciclo di vita di Maven.

Un esempio: Esegui la compilazione Docker dopo il confezionamento (fase: pacchetto) dell'applicazione configurando il plugin per aggiungere l'applicazione creata come risorsa al contesto di compilazione Docker. Nella fase di distribuzione, esegui l'obiettivo Docker push per inviare l'immagine Docker a un registro. Questo può essere eseguito accanto al normale plug-in deploy, che pubblica il artefatto in un repository come Nexus.

In seguito, abbiamo diviso la build in due processi separati sul server CI. Poiché Docker è solo un modo per eseguire la tua applicazione (a volte abbiamo bisogno dell'applicazione rilasciata su diversi ambienti non solo Docker), la build Maven non dovrebbe fare affidamento su Docker.

Quindi il primo lavoro rilascia l'applicazione in Nexus (tramite Maven deploy). Il secondo lavoro (che può essere una dipendenza a valle del primo lavoro) scarica l'ultima versione artefatto, esegue la compilazione Docker e spinge l'immagine al registro di sistema. Per scaricare l'ultima versione usiamo il Versions Maven Plugin (versions:use-latest-releases) così come il Maven Dependency Plugin (dependency:get e dependency:copy).

Il secondo lavoro può anche essere avviato per una versione specifica dell'applicazione per (ri)creare l'immagine Docker per una versione precedente. Inoltre è possibile utilizzare una pipeline di compilazione (su Jenkins), che esegue sia i lavori che i passaggi la versione di rilascio o l'artefatto di rilascio nella build Docker.

 2
Author: gclaussn, 2017-07-27 18:33:31

Un paio di cose:

  • Se elimini i file nella stessa istruzione in cui li aggiungi, non consumeranno spazio nell'immagine. Se guardi alcuni dei Dockerfiles per le immagini ufficiali vedrai che scaricano il sorgente, lo costruiscono ed eliminano tutto nello stesso passaggio (ad esempio https://github.com/docker-library/python/blob/0fa3202789648132971160f686f5a37595108f44/3.5/slim/Dockerfile). Questo significa che devi fare un po ' di ginnastica fastidiosa, ma è perfettamente fattibile.

  • Non vedo il problema con due Dockerfiles separati. La cosa bella di questo è che potresti usare il JRE piuttosto che il JDK per ospitare il tuo barattolo.

 1
Author: Adrian Mouat, 2016-07-08 16:17:37

Esistono usi alternativi per l'esecuzione di pacchetti jar o war

  • aggiungi jar nell'immagine.
  • imposta heapsize per java
  • esegui il comando jar tramite entrypoint

File dockerfile di esempio

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

Inoltre esempio di distribuzione del pacchetto su tomcat

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

Creazione di file dockerfile come immagine

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

Elenco immagini

docker images

Usa l'immagine per creare un contenitore

docker run --name cont_name --extra-vars var1=val1 var2=val2 imagename
 0
Author: pmoksuz, 2015-12-26 22:16:11

Qui Descrivo come lo faccio nel mio ambiente di sviluppo.

Spero che aiuti.

 0
Author: Eyal.Dahari, 2017-05-23 11:55:04

Containerizza la tua applicazione java usando lo strumento Jib senza scrivere dockerfile

Jib è uno strumento Java open source gestito da Google per la creazione di immagini Docker di applicazioni Java. Semplifica la containerizzazione poiché con esso, non abbiamo bisogno di scrivere un dockerfile. E in realtà, non abbiamo nemmeno bisogno di avere installato docker per creare e pubblicare le immagini docker noi stessi.

Google pubblica Jib sia come Maven che come Gradle plugin. https://github.com/GoogleContainerTools/jib

Containerizza la tua applicazione java usando Maven project

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

Containerizza la tua applicazione java usando Gradle project

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

 0
Author: anandchaugule, 2019-04-12 09:37:14