Pregunta Cómo construir un contenedor docker para una aplicación java


Lo que quiero hacer es construir una imagen acoplable para mi aplicación Java, pero las siguientes consideraciones deberían ser ciertas para la mayoría de los lenguajes compilados.

problema

En mi servidor de compilación, quiero producir una imagen de acoplador para mi aplicación como entregable. Para esto tengo que compilar la aplicación usando alguna herramienta de compilación (típicamente Gradle, Maven o Ant) y luego agregar el archivo JAR creado a la imagen de la ventana acoplable. Como quiero que la imagen del acoplador solo ejecute el archivo JAR, por supuesto comenzaré desde una imagen base con Java ya instalado.

Hay tres formas de hacer esto:

deja que la herramienta de compilación controle el proceso

En este caso, mi herramienta de compilación controla todo el proceso. Así que prepara el archivo JAR y después de que se crea el JAR llama a Docker para crear la imagen. Esto funciona ya que el JAR se creó de antemano y Docker puede ignorar el proceso de compilación necesario para crear el JAR.

Pero mi Dockerfile ya no es independiente. Depende de los pasos a seguir fuera de Docker para que funcione. En mi Dockerfile tendré un COPY o ADD declaración que se supone que copia el archivo JAR a la imagen. Esta declaración fallará cuando el contenedor no se haya creado de antemano. Así que solo ejecutar Dockerfile podría no funcionar. Esto se convierte en un problema si desea integrarse con los servicios que acaban de construir utilizando el presente Dockerfile como la característica de autoconstrucción en DockerHub.

deja que Docker controle la construcción

En este caso, todos los pasos necesarios para crear la imagen se agregan al archivo Docker para que la imagen se pueda crear simplemente ejecutando la compilación Docker.

El principal problema con este enfoque es que no hay forma de agregar a los comandos de Dockerfile que se deben ejecutar fuera de la imagen de la ventana acoplable que se está creando. Esto significa que tengo que agregar mi código fuente y mis herramientas de compilación a la imagen de la ventana acoplable y crear mi archivo JAR dentro de la imagen. Esto dará como resultado que mi imagen sea más grande de lo que debe ser debido a todos los archivos agregados que serán innecesarios en el tiempo de ejecución. Esto también agregará capas adicionales a mi imagen.

Editar:

Como @ adrian-mouat señaló si agregaría las fuentes, construiría la aplicación y eliminaría las fuentes en una sola declaración RUN, podría evitar agregar capas y archivos innecesarios a la imagen Docker. Esto significaría crear un comando encadenado insano.

dos compilaciones separadas

En este caso, dividimos nuestra construcción en dos: primero creamos el archivo JAR utilizando nuestra herramienta de compilación y lo cargamos en un repositorio (repositorio de Maven o Ivy). Luego activamos una compilación separada de Docker que simplemente agrega el archivo JAR del repositorio.

conclusión

En mi opinión, la mejor manera sería dejar que la herramienta de compilación controle el proceso. Esto dará como resultado una imagen de acoplador limpio y como la imagen es lo que queremos entregar, esto es importante. Para evitar tener un Dockerfile que potencialmente no funciona, debería crearse como parte de la compilación. Entonces nadie lo usaría accidentalmente para comenzar una construcción rota.

Pero esto no me permitirá integrarme con DockerHub.

pregunta

¿Hay alguna otra forma en la que me estoy perdiendo?


32
2017-07-29 09:28


origen


Respuestas:


El concentrador de registro de Docker tiene un Imagen de Maven que se puede usar para crear contenedores java.

Usando este enfoque, la máquina de compilación no necesita preinstalar Java o Maven, Docker controla todo el proceso de compilación.

Ejemplo

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

El contenedor está construido de la siguiente manera:

docker build -t my-maven .

Y ejecuta de la siguiente manera:

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

Dockerfile

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

Actualizar

Si desea optimizar su contenedor para excluir el origen, puede crear un archivo Docker que solo incluya el contenedor integrado:

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

Y construye el contenedor en dos pasos:

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 .

__

Actualización (2017-07-27)

Docker ahora tiene una construcción de múltiples etapas capacidad. Esto permite a Docker construir un contenedor con una imagen que contenga las herramientas de compilación, pero genera una imagen solo con las dependencias de tiempo de ejecución.

El siguiente ejemplo demuestra este concepto, observe cómo se copia el jar del directorio de destino de la primera fase de compilación

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

21
2017-07-29 20:11



Estructura de la aplicación java

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

Contenido de Dockerfile

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

Comandos para construir y ejecutar imágenes

  • Vaya al directorio del proyecto. Le dicen D: / Demo
$ cd D/demo
$ mvn clean install
$ docker build demo .
$ docker run -p 8080:8080 -t demo

Comprueba que el contenedor esté funcionando o no

$ docker ps

La salida será

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

4
2018-04-01 12:25



Un par de cosas:

  • Si elimina archivos en la misma instrucción que los agrega, no consumirán espacio en la imagen. Si observa algunos de los archivos Docker para las imágenes oficiales, verá que descargan la fuente, la compilan y la eliminan en el mismo paso (p. https://github.com/docker-library/python/blob/0fa3202789648132971160f686f5a37595108f44/3.5/slim/Dockerfile) Esto significa que debes hacer gimnasia molesta, pero es perfectamente factible.

  • No veo el problema con dos archivos Docker separados. Lo bueno de esto es que puedes usar el JRE en lugar del JDK para alojar tu jar.


1
2017-07-29 11:23



La forma más fácil es deja que la herramienta de compilación controle el proceso. De lo contrario, tendrías que mantener el archivo de compilación de tu herramienta de compilación (como pom.xml para Maven o build.gradle para Gradle), así como una Dockerfile.

Una forma simple de construir un contenedor Docker para su aplicación Java es usar Foque, que está disponible como Maven y Gradle complementos

Por ejemplo, si está utilizando Maven y desea construir su contenedor en el daemon Docker en ejecución, puede ejecutar este comando:

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

Tú también puedes construir directamente en un registro Docker con Jib sin necesidad de instalar docker, ejecute un demonio Docker (que requiere privilegios de administrador) o escriba un Dockerfile. También es más rápido y construye imágenes reproducibles.

Ver más sobre Jib en su repositorio de Github: https://github.com/GoogleContainerTools/jib


1
2017-07-09 13:01



hay usos alternativos para ejecutar paquetes jar o war

  • agregar jar a la imagen.
  • establecer heapsize para java
  • ejecutar el comando jar vía entrypoint

muestra dockerfile

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

Además, ejemplo de implementación de paquete en tomcat

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

Construir dockerfiles como una imagen

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

Listar imágenes

docker images

Usa imagen para crear un contenedor

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

0
2017-12-26 22:09



aquí Describo cómo lo hago en mi entorno de desarrollo.

  • Construye la guerra / jar a nivel local con Maven
  • Cópialo en una carpeta Docker local
  • correr Complemento Intellij Docker que crea una imagen del acoplador que contiene el war / jar, ejecuta el servidor de aplicaciones y lo despliega en el servidor Docker remoto

Espero eso ayude.


0
2018-01-29 10:04



Usamos el Plugin de Spotify Docker Maven por un momento. El complemento le permite vincular un Docker para construirlo en una fase del ciclo de vida de Maven.

Un ejemplo: Ejecute la compilación Docker después de empaquetar (fase: paquete) su aplicación configurando el complemento para agregar su aplicación construida como un recurso al contexto de compilación de Docker. En la fase de implementación, ejecute el objetivo de Docker push para insertar su imagen de Docker en un registro. Esto puede ejecutarse junto al complemento de implementación normal, que publica el artefacto en un repositorio como Nexus.

Más tarde, dividimos la compilación en dos trabajos separados en el servidor de CI. Como Docker es solo una forma de ejecutar su aplicación (a veces necesitamos la aplicación liberada en diferentes entornos, no solo Docker), la versión de Maven no debería depender de Docker.

Por lo tanto, el primer trabajo libera la aplicación en Nexus (a través de la implementación de Maven). El segundo trabajo (que puede ser una dependencia posterior del primer trabajo) descarga el artefacto de la última versión, realiza la compilación de Docker y envía la imagen al registro. Para descargar la última versión, usamos el Versiones Maven Plugin (versiones: use-latest-releases), así como la Complemento Dependencia Maven (dependencia: obtener y dependencia: copiar).

El segundo trabajo también se puede iniciar para una versión específica de la aplicación para (re) construir la imagen de Docker para una versión anterior. Además, puede usar una canalización de compilación (en Jenkins), que ejecuta ambos trabajos y pasa la versión de lanzamiento o el artefacto de publicación a la compilación de Docker.


0
2017-07-27 18:33