我正在尝试使用docker将我们的java web应用程序部署到aws弹性beanstalk,其想法是能够在本地运行容器以进行开发和测试,并最终使用git将其推送到生产环境.
我已经创建了一个安装了tomcat8和java8的基本映像,执行gradle构建的映像继承了这个基本映像,从而加快了构建过程.
一切都运行良好,除了使用docker构建的继承应用程序容器似乎不缓存gradle依赖项,它每次都下载它,包括gradlew.我们使用以下命令构建Web应用程序:
./gradlew war
有没有办法可以缓存文件,~/.gradle
这会大大加快我的建立速度.
这不是beanstalk上的一个问题,但对于尝试在本地构建和运行的开发人员来说这是一个大问题,因为这需要花费很多时间,正如您可以想象的那样.
基本映像dockerfile:
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"]
应用程序dockerfile:
FROMRUN 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"]
Sairam Krish.. 14
我遇到了这个问题.您可能会同意,最佳做法是在构建docker镜像时单独下载依赖项作为单独的步骤.使用gradle变得有点棘手,因为没有直接支持只下载依赖项.
选项1:使用docker-gradle Docker镜像
我们可以使用预先构建的gradle docker镜像来构建应用程序.这可以确保它不是本地系统构建,而是在干净的docker镜像上完成构建.
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在这里作为卷加载.因此,后续构建将重用已下载的依赖项.
在此之后,我们可以使用Dockerfile来获取此工件并生成特定于应用程序的图像以运行该应用程序.
这样,不需要构建器映像.分离出应用程序构建流程和应用程序运行流程.
由于挂载了gradle-cache卷,我们可以在不同的gradle项目中重用已下载的依赖项.
选项2:多阶段构建
----- 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]
在上面的Dockerfile中
首先,我们尝试单独复制项目的gradle文件,如build.gradle,gradlew等,
然后我们复制gradle目录本身
然后我们尝试运行构建.此时,目录中不存在其他源代码文件.所以构建将失败.但在此之前它将下载依赖项.
由于我们期望构建失败,我尝试了一种简单的技术来返回0并允许docker继续执行
这将加速后续构建流程,因为所有依赖项都已下载,并且docker缓存了此层.相比之下,安装gradle缓存目录的卷仍然是最好的方法.
上面的例子还展示了多阶段docker镜像构建,它避免了多个docker构建文件.
Daniel Wei.. 11
在build.gradle中添加resolveDependencies任务:
task resolveDependencies { doLast { project.rootProject.allprojects.each { subProject -> subProject.buildscript.configurations.each { configuration -> configuration.resolve() } subProject.configurations.each { configuration -> configuration.resolve() } } } }
并更新Dockerfile:
ADD build.gradle /opt/app/ WORKDIR /opt/app RUN gradle resolveDependencies ADD . . RUN gradle build -x test --parallel && \ touch build/libs/api.jar
贝娄是我现在所做的:
的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
选项2对我来说效果很好。我创建了一个示例进行尝试(以防万一,如果有帮助的话):https://github.com/yb172/experiments/tree/master/java-multistage (2认同)
gesellix.. 7
您可能需要考虑将应用程序映像拆分为两个映像:一个用于构建myapp.war,另一个用于运行应用程序.这样,您可以在实际构建期间使用docker卷,并将主机的~/.gradle
文件夹绑定到执行构建的容器中.不过,只有一步来运行您的应用程序,您可以采取更多步骤.例:
生成器图像
FROM# 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"]
应用图片
FROMADD ./ROOT.war /opt/tomcat/webapps/ROOT.war # Start Tomcat CMD ["/opt/tomcat/bin/catalina.sh", "run"]
如何在项目根目录中使用,假设构建器Dockerfile位于那里并且应用程序Dockerfile位于webapp
子文件夹(或您喜欢的任何其他路径):
$ 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
我没有测试显示的代码,但我希望你能得到这个想法.通过使用.gradle/cache的数据卷甚至可以改进该示例,有关详细信息,请参阅Docker 用户指南.