J'essaie de déployer notre Java sur aws élastique beanstalk à l'aide de docker, l'idée est de pouvoir exécuter le conteneur localement pour le développement et les tests et finalement le pousser à la production en utilisant git .
J'ai créé une image de base sur laquelle Tomcat8 et Java8 sont installés, l'image qui exécute les versions gradle héritées de cette image de base, accélérant ainsi le processus de génération.
Tout fonctionne bien, sauf que le conteneur d'application hérité qui est construit à l'aide de docker ne semble pas mettre en cache les dépendances de gradle, il le télécharge à chaque fois, y compris gradlew. Nous construisons notre application web en utilisant la commande suivante:
./gradlew war
Existe-t-il un moyen de mettre en cache les fichiers dans ~/.gradle
cela accélérerait considérablement ma construction.
Ce n'est pas vraiment un problème sur Beanstalk mais c'est un gros problème pour les développeurs qui essaient de construire et d'exécuter localement car cela prend beaucoup de temps, comme vous pouvez l'imaginer.
Le fichier docker de l'image de 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"]
Le Dockerfile d'application:
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"]
J'ai fait face à ce problème. Comme vous pouvez en convenir, il est recommandé de télécharger les dépendances seules en tant qu'étape distincte lors de la création de l'image docker. Cela devient peu délicat avec gradle, car il n'y a pas de support direct pour télécharger uniquement les dépendances.
Option 1: utilisation de l'image Docker-gradle Docker
Nous pouvons utiliser l'image Docker Gradle pré-construite pour créer l'application. Cela garantit qu'il ne s'agit pas d'une génération de système local mais d'une génération effectuée sur une image de docker propre.
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
Option 2: construction en plusieurs étapes
----- 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]
Dans le Dockerfile ci-dessus
Ajouter resolverDépendances tâche dans build.gradle:
task resolveDependencies {
doLast {
project.rootProject.allprojects.each { subProject ->
subProject.buildscript.configurations.each { configuration ->
configuration.resolve()
}
subProject.configurations.each { configuration ->
configuration.resolve()
}
}
}
}
et mettre à jour Dockerfile:
ADD build.gradle /opt/app/
WORKDIR /opt/app
RUN gradle resolveDependencies
ADD . .
RUN gradle build -x test --parallel && \
touch build/libs/api.jar
Voici ce que je fais maintenant:
build.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
Vous voudrez peut-être envisager de diviser l'image de votre application en deux images: l'une pour la construction de myapp.war et l'autre pour l'exécution de votre application. De cette façon, vous pouvez utiliser des volumes de docker pendant la génération réelle et lier le ~/.gradle
dossier dans le conteneur effectuant la construction. Au lieu d'une seule étape pour exécuter votre application, vous auriez cependant plus d'étapes. Exemple:
image de générateur
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"]
image d'application
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"]
Comment utiliser dans la racine de votre projet, en supposant que le générateur Dockerfile s'y trouve et que l'application Dockerfile se trouve dans le sous-dossier webapp
(ou tout autre chemin que vous préférez):
$ 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
Je n'ai pas testé le code affiché, mais j'espère que vous avez compris l'idée. L'exemple pourrait même être amélioré en utilisant des volumes de données pour le .gradle/cache, voir le Docker guide de l'utilisateur pour plus de détails.