【问题标题】:How to reduce my java/gradle docker image size?如何减小我的 java/gradle docker 镜像大小?
【发布时间】:2022-04-23 22:58:40
【问题描述】:

我有一个 Docker 文件,如下所示:

FROM openjdk:8

ADD . /usr/share/app-name-tmp

WORKDIR /usr/share/app-name-tmp

RUN ./gradlew build \
    mv ./build/libs/app-name*.jar /usr/share/app-name/app-name.jar

WORKDIR /usr/share/app-name

RUN rm -rf /usr/share/app-name-tmp

EXPOSE 8080

RUN chmod +x ./docker-entry.sh

ENTRYPOINT [ "./docker-entry.sh" ]

问题是最终图像大小为 1.1GB,我知道这是因为 gradle 下载并存储了所有依赖项。删除那些不必要的文件并保留 jar 的最佳方法是什么?

【问题讨论】:

    标签: java gradle docker dockerfile gradlew


    【解决方案1】:

    我真的对你的图片大小感到困惑。我有典型的 Spring Boot 应用程序,提供 REST 服务,包括不到 200MB 的嵌入式 servlet 容器!看起来您的项目依赖项可以而且应该进行优化。

    Docker 映像

    openjdk:8(压缩后的 243MB)可以替换为具有缩减的 Alpine unix 映像,例如 openjdk:8-jdk-alpine(52MB)作为基本映像,但如果您不需要编译器功能(例如,不使用 JSP)您也可以选择 openjdk:8-jre-alpine (42MB),其中仅包含运行时,请查看 Docker Hub。我将它用于基于 Spring Boot 的 REST 服务,效果很好。

    Java 依赖项

    必须包含编译和运行时所需的 Java 依赖项,但您可能包含未使用的依赖项:

    • 检查您的依赖关系,当前编译/运行时依赖关系是否真正使用,或者是否可以删除或移动以进行测试,请参阅Gradle Java Plugin
    • 一些依赖有很多传递依赖(使用gradle dependencies显示),检查不必要的,如果不使用则排除它们,见Gradle Dependency Management。确保在最终应用之前进行集成测试,一些传递依赖项没有很好的文档记录,但可能是必不可少的!

    【讨论】:

    • 如果我执行./gradlew build,生成的jar只有95MB,但是镜像大小在1GB左右
    • 为什么要从 docker 运行 gradle 以及添加的目录中有什么?我使用 gradle docker 插件,只将 jar 添加到从 gradle 调用 docker 的图像中,而不是相反
    【解决方案2】:

    使用 Docker 17.05+,您可以使用 multi-stage builds

    “使用多阶段构建,您可以在 Dockerfile 中使用多个 FROM 语句。每个 FROM 指令可以使用不同的基础,并且每个指令都开始构建的新阶段。您可以选择性地将工件从一个阶段复制到另一个阶段,在最终图像中留下你不想要的一切。”

    所以你的 Dockerfile 可能看起来像这样:

    #
    # first stage (build)
    #
    FROM openjdk:8 as build
    
    ADD . /usr/share/app-name-tmp
    
    WORKDIR /usr/share/app-name-tmp
    
    RUN ./gradlew build && \
        mv ./build/libs/app-name*.jar /usr/share/app-name/app-name.jar
    
    #
    # second stage. use alpine to reduce the image size
    #
    FROM openjdk:8-jre-alpine
    
    WORKDIR /usr/share/app-name
    
    COPY --from=build /usr/share/app-name/app-name.jar .
    
    EXPOSE 8080
    
    RUN chmod +x ./docker-entry.sh
    
    ENTRYPOINT [ "./docker-entry.sh" ]
    

    这样你只保留jar,所有不必要的文件都不会包含在最终图像中。

    【讨论】:

    • 不应该是COPY --from=build,因为第一阶段被命名为build(不是builder)?
    【解决方案3】:

    每个 RUN 指令在现有文件系统之上创建一个新层。因此,删除您app-name-tmp 目录的 RUN 指令之后的新层只会掩盖包含下载库的前一层。因此,您的 docker 映像在所有构建的层中仍然具有该大小。

    删除单独的 RUN rm -rf /usr/share/app-name-tmp 指令并将其包含在执行 gradle build 的同一 RUN 指令中,如下所示。

    RUN ./gradlew build \
        mv ./build/libs/app-name*.jar /usr/share/app-name/app-name.jar \
        rm -rf /usr/share/app-name-tmp/*
    

    所以,你最终的 Dockerfile 将是

    FROM openjdk:8
    
    ADD . /usr/share/app-name-tmp
    WORKDIR /usr/share/app-name-tmp
    
    RUN ./gradlew build \
        mv ./build/libs/app-name*.jar /usr/share/app-name/app-name.jar \
        rm -rf /usr/share/app-name-tmp/*
    
    WORKDIR /usr/share/app-name
    
    EXPOSE 8080
    RUN chmod +x ./docker-entry.sh
    ENTRYPOINT [ "./docker-entry.sh" ]
    

    构建的映像仍然会从 /usr/share/app-name-tmp 目录中增加大小。

    【讨论】:

    【解决方案4】:

    看来你的图片来自

    FROM openjdk:8

    所以从

    https://github.com/docker-library/openjdk/blob/e6e9cf8b21516ba764189916d35be57486203c95/8-jdk/Dockerfile

    实际上是一个 Debian

    FROM buildpack-deps:jessie-scm

    你应该尝试使用阿尔卑斯山基地

    https://github.com/docker-library/openjdk/blob/9a0822673dffd3e5ba66f18a8547aa60faed6d08/8-jdk/alpine/Dockerfile

    我猜你的图片至少会缩小一半

    【讨论】:

    • 我知道 Alpine,但我想继续使用 'openjdk:8' 图像。它大约是 650MB,但我的应用程序在 ./gradlew 构建步骤中增加了大约 500MB。
    【解决方案5】:

    这是您部署到生产环境的容器吗?如果是这样,请不要将其用于实际构建。在其他地方进行构建(和测试),一旦成功,只需将 JAR 复制到您的 Docker 生产容器中。

    【讨论】:

      【解决方案6】:

      对于 OpenJDK-12

      我的应用程序是用 Kotlin 以及 spring boot 和 maven 编写的。

      我对 openJDK-12 有同样的问题,OracleOpenJDK-12 大小为 470 MB。

      我想减小容器大小,所以我选择了采用openjdk/openjdk12:x86_64-alpine-jre-12.33 并达到了 189 MB,如下所示。

      FROM adoptopenjdk/openjdk12:x86_64-alpine-jre-12.33
      RUN mkdir /app
      COPY ./target/application-SNAPSHOT.jar /app/application-SNAPSHOT.jar
      WORKDIR /app
      CMD ["java", "-jar", "application-SNAPSHOT.jar"]
      

      我的最终容器大小为 189MB(34 MB 应用程序 Jar 大小 + 155 MB 基本映像大小。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-02-15
        • 1970-01-01
        • 1970-01-01
        • 2019-08-18
        • 1970-01-01
        • 2018-12-21
        • 2019-07-11
        相关资源
        最近更新 更多