Docker cache gradle dependencies

我正在尝试使用 docker 将我们的 java Web 应用程序部署到 aws elastic 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:

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

答案 1

我遇到了这个问题。您可能同意,在构建 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 缓存作为卷加载在此处。因此,后续版本将重用下载的依赖项。
  • 在此之后,我们可以有一个 Dockerfile 来获取此工件并生成特定于应用程序的映像来运行应用程序。
  • 这样,构建器映像就不是必需的。应用程序生成流和应用程序运行流分离出来。
  • 由于 gradle 缓存卷已挂载,因此我们可以在不同的 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 构建文件。

答案 2

在 build.gradle 中添加 resolveDependencies task:

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

推荐