如何缩小我的 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
所以来自
实际上是 Debian
FROM buildpack-deps:jessie-scm
你应该尝试使用阿尔卑斯基地
我猜你的图片至少会缩小一半
我真的很困惑你的图片大小。我有典型的 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 基本图像大小。)
我有一个 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
所以来自
实际上是 Debian
FROM buildpack-deps:jessie-scm
你应该尝试使用阿尔卑斯基地
我猜你的图片至少会缩小一半
我真的很困惑你的图片大小。我有典型的 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 基本图像大小。)