java - jre - Docker caché dependencias de gradle
dockerfile java jar (4)
Estoy tratando de implementar nuestra aplicación web java para aws elastic beanstalk usando la ventana acoplable, la idea es poder ejecutar el contenedor localmente para el desarrollo y las pruebas y eventualmente subirlo a producción utilizando git.
He creado una imagen base que tiene tomcat8 y java8, la imagen que realiza las compilaciones de gradle heredadas de esta imagen base, acelerando el proceso de compilación.
Todo funciona bien, excepto por el hecho de que el contenedor de aplicaciones heredado que se construye utilizando la ventana acoplable no parece almacenar en caché las dependencias de Gradle, sino que las descarga cada vez, incluido Gradlew. Construimos nuestra aplicación web usando el siguiente comando:
./gradlew war
¿Hay alguna manera de que pueda almacenar en caché los archivos en ~/.gradle
esto aceleraría dramáticamente mi acumulación?
Esto no es tanto un problema en beanstalk, pero es un gran problema para los desarrolladores que intentan compilar y ejecutar localmente, ya que esto lleva mucho tiempo, como puedes imaginar.
El archivo de base de la imagen base:
FROM phusion/baseimage
EXPOSE 8080
RUN apt-get update
RUN add-apt-repository ppa:webupd8team/java
RUN apt-get update
RUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | sudo /usr/bin/debconf-set-selections
RUN apt-get -y install oracle-java8-installer
RUN java -version
ENV TOMCAT_VERSION 8.0.9
RUN wget --quiet --no-cookies http://archive.apache.org/dist/tomcat/tomcat-8/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz -O /tmp/catalina.tar.gz
# Unpack
RUN tar xzf /tmp/catalina.tar.gz -C /opt
RUN mv /opt/apache-tomcat-${TOMCAT_VERSION} /opt/tomcat
RUN ln -s /opt/tomcat/logs /var/log/tomcat
RUN rm /tmp/catalina.tar.gz
# Remove unneeded apps
RUN rm -rf /opt/tomcat/webapps/examples
RUN rm -rf /opt/tomcat/webapps/docs
RUN rm -rf /opt/tomcat/webapps/ROOT
ENV CATALINA_HOME /opt/tomcat
ENV PATH $PATH:$CATALINA_HOME/bin
ENV CATALINA_OPTS $PARAM1
# Start Tomcat
CMD ["/opt/tomcat/bin/catalina.sh", "run"]
La aplicación dockerfile:
FROM <tag name here for base image>
RUN mkdir ~/.gradle
# run some extra stuff here to add things to gradle.properties file
# Add project Source
ADD . /var/app/myapp
# Compile and Deploy Application, this is what is downloading gradlew and all the maven dependencies every time, if only there was a way to take the changes it makes to ~/.gradle and persist it as a cache layer
RUN cd /var/app/myapp/ && ./gradlew war
RUN mv /var/app/myapp/build/libs/myapp.war /opt/tomcat/webapps/ROOT.war
# Start Tomcat
CMD ["/opt/tomcat/bin/catalina.sh", "run"]
yo
Agregar la tarea resolviendo dependencias en build.gradle:
task resolveDependencies {
doLast {
project.rootProject.allprojects.each { subProject ->
subProject.buildscript.configurations.each { configuration ->
configuration.resolve()
}
subProject.configurations.each { configuration ->
configuration.resolve()
}
}
}
}
y actualizar Dockerfile:
ADD build.gradle /opt/app/
WORKDIR /opt/app
RUN gradle resolveDependencies
ADD . .
RUN gradle build -x test --parallel && /
touch build/libs/api.jar
II
Bramido es lo que hago ahora:
construir.gradle
ext {
speed = project.hasProperty(''speed'') ? project.getProperty(''speed'') : false
offlineCompile = new File("$buildDir/output/lib")
}
dependencies {
if (speed) {
compile fileTree(dir: offlineCompile, include: ''*.jar'')
} else {
// ...dependencies
}
}
task downloadRepos(type: Copy) {
from configurations.all
into offlineCompile
}
Dockerfile
ADD build.gradle /opt/app/
WORKDIR /opt/app
RUN gradle downloadRepos
ADD . /opt/app
RUN gradle build -Pspeed=true
Es posible que desee considerar dividir la imagen de su aplicación en dos imágenes: una para compilar myapp.war y la otra para ejecutar su aplicación. De esa manera, puede usar los volúmenes de la ~/.gradle
acoplable durante la compilación real y vincular la carpeta ~/.gradle
en el contenedor que realiza la compilación. Sin embargo, en lugar de un solo paso para ejecutar su aplicación, tendría más pasos. Ejemplo:
imagen del constructor
FROM <tag name here for base image including all build time dependencies>
# Add project Source
# -> you can use a project specific gradle.properties in your project root
# in order to override global/user gradle.properties
ADD . /var/app/myapp
RUN mkdir -p /root/.gradle
ENV HOME /root
# declare shared volume path
VOLUME /root/.gradle
WORKDIR /var/app/myapp/
# Compile only
CMD ["./gradlew", "war"]
imagen de la aplicación
FROM <tag name here for application base image>
ADD ./ROOT.war /opt/tomcat/webapps/ROOT.war
# Start Tomcat
CMD ["/opt/tomcat/bin/catalina.sh", "run"]
Cómo usarlo en la raíz de su proyecto, asumiendo que el generador Dockerfile está ubicado allí y la aplicación Dockerfile está ubicada en la subcarpeta de la aplicación webapp
(o en cualquier otra ruta que prefiera):
$ docker build -t builder .
$ docker run --name=build-result -v ~/.gradle/:/root/.gradle/ builder
$ docker cp build-result:/var/app/myapp/myapp.war webapp/ROOT.war
$ cd webapp
$ docker build -t application .
$ docker run -d -P application
No he probado el código mostrado, pero espero que se te ocurra. El ejemplo podría incluso mejorarse utilizando volúmenes de datos para .gradle / cache, consulte la guía del usuario de Docker para obtener más información.
Me enfrenté a este problema. Como puede estar de acuerdo, es una buena práctica descargar las dependencias solo como un paso separado al construir la imagen de la ventana acoplable. Con Gradle se vuelve un poco complicado, ya que no hay soporte directo para descargar solo dependencias.
Opción 1: usar la imagen de docker-gradle Docker
Podemos usar una imagen de Gradle Docker pre-construida para construir la aplicación. Esto garantiza que no se trate de una compilación del sistema local sino de una compilación realizada en una imagen de ventana acoplable.
docker volume create --name gradle-cache
docker run --rm -v gradle-cache:/home/gradle/.gradle -v "$PWD":/home/gradle/project -w /home/gradle/project gradle:4.7.0-jdk8-alpine gradle build
ls -ltrh ./build/libs
- gradle cache se carga aquí como un volumen. Así que las compilaciones posteriores reutilizarán las dependencias descargadas.
- Después de esto, podríamos tener un Dockerfile para tomar este artefacto y generar una imagen específica de la aplicación para ejecutar la aplicación.
- De esta manera, la imagen del constructor no es necesaria. El flujo de compilación de la aplicación y el flujo de ejecución de la aplicación están separados.
- Dado que el volumen de caché gradle está montado, podríamos reutilizar las dependencias descargadas en diferentes proyectos gradle.
Opción 2: construcción de varias etapas
----- Dockerfile -----
FROM openjdk:8 AS TEMP_BUILD_IMAGE
ENV APP_HOME=/usr/app/
WORKDIR $APP_HOME
COPY build.gradle settings.gradle gradlew $APP_HOME
COPY gradle $APP_HOME/gradle
RUN ./gradlew build || return 0
COPY . .
RUN ./gradlew build
FROM openjdk:8
ENV ARTIFACT_NAME=your-application.jar
ENV APP_HOME=/usr/app/
WORKDIR $APP_HOME
COPY --from=TEMP_BUILD_IMAGE $APP_HOME/build/libs/$ARTIFACT_NAME .
EXPOSE 8080
CMD ["java","-jar",$ARTIFACT_NAME]
En el anterior Dockerfile
- Primero intentamos copiar los archivos de gradle del proyecto solo, como build.gradle, gradlew, etc.
- Luego copiamos el propio directorio de Gradle.
- Y luego intentamos ejecutar el build. En este punto, no hay otros archivos de código fuente en el directorio. Así que la construcción fallará. Pero antes de eso se descargarán las dependencias.
- Como esperamos que la compilación falle, he probado una técnica simple para devolver 0 y permitir que la ventana acoplable continúe ejecutándose
- esto acelerará los flujos de compilación subsiguientes, ya que todas las dependencias se descargan y la ventana acoplable almacena en caché esta capa. Comparativamente, el montaje en volumen del directorio de caché de Gradle sigue siendo el mejor enfoque.
- El ejemplo anterior también muestra la creación de imágenes de la ventana acoplable en varias etapas , lo que evita múltiples archivos de compilación de la ventana acoplable.
intenta cambiar el directorio de inicio de usuario de Gradle
EJECUTAR mkdir -p /opt/gradle/.gradle
ENV GRADLE_USER_HOME = / opt / gradle / .gradle