如何缩小我的 java/gradle docker 图片大小?

How to reduce my java/gradle docker image size?

我有一个 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 的最佳方法是什么?

你的图片好像来自

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

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

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

Docker 图片

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

Java 依赖项

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

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

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

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

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.

中添加大小

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

使用 Docker 17.05+ 可以使用 multi-stage builds.

"With multi-stage builds, you use multiple FROM statements in your Dockerfile. Each FROM instruction can use a different base, and each of them begins a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind everything you don’t want in the final image."

因此您的 Docker 文件可能如下所示:

#
# 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,所有不必要的文件都不会包含在最终图像中。

对于 OpenJDK-12

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

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

我想减小我的容器大小,所以我选择了 adoptopenjdk/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 基本图像大小。)