Java 11应用程序作为轻量级Docker映像 [英] Java 11 application as lightweight docker image
问题描述
受问题的启发,为什么Java 11基础Docker映像如此之大? (openjdk:11-jre-slim)我发现Java世界中的这个主题仍未解决。
Inspired by question Why is the Java 11 base Docker image so large? (openjdk:11-jre-slim) I found that this topic in Java world is still not settled.
对于 07 2018年12月
有一些常见问题/陷阱(在上面的票证中讨论):
As for 07 Dec 2018
there are common issues/pitfalls (discussed in the ticket above):
-
JRE是不作为单独的程序包分发。应该改用JDK的模块
JRE is not distributed as a separate "package". Modules from JDK should be used instead
Oracle OpenJDK 11 不支持 Linux Alpine ,因此 lightweight 无法轻松创建图像
Oracle OpenJDK 11 doesn't support Linux Alpine, so lightweight images can't be easily created
- 与此同时,当前稳定的Debian版本仍然没有 Java 11软件包( Ubuntu在openjdk-11软件包下安装了Java 10 ),这就是为什么不稳定的sid版本用于基本docker镜像
- In the same time current stable Debian versions still doesn't have Java 11 packages (Ubuntu has Java 10 installed under openjdk-11 packages), that's why unstable sid versions are used for base docker images
当前可用的Oracle openjdk-11映像构建未剥离的 libjvm.so
模块,该模块具有数百兆字节,必须单独剥离:
currently available Oracle openjdk-11 images build unstripped libjvm.so
module, which has hundreds megabyte and must be stripped separately:
- jlink运行时映像大小(规格从openjdk创建的libjvm.so)非常庞大。
- 解决方案: https://github.com/docker-library/openjdk/issues/217#issuecomment-436079779
- jlink runtime image size (specifically libjvm.so) created from openjdk is huge. Expected it to be much smaller.
- Solution: https://github.com/docker-library/openjdk/issues/217#issuecomment-436079779
由于这些问题,甚至 slim Oracle Java 11基本映像也很重并且被认为是不稳定的: https://hub.docker.com/_/openjdk/
As a result of these issues even slim Oracle Java 11 base images are quite heavy and considered to be unstable: https://hub.docker.com/_/openjdk/
所以问题是:
什么是优化的或推荐的构建方式并将Java 11应用程序作为docker映像交付 ??
what are optimized or recommended ways to build and deliver Java 11 applications as docker images?
推荐答案
UPD从07.2019起:< a href = https://stackoverflow.com/a/57145029/907576> https://stackoverflow.com/a/57145029/907576
以到目前为止的简单Spring Boot应用程序为例(只有一个REST端点) e找出以下解决方案(考虑在Docker构建之前,应用程序jar位于 build / libs / spring-boot-demo.jar
中:
Taking as an example of simple spring boot application (with only one REST endpoint) so far i was able to figure out the following solutions (considering application jar is located at build/libs/spring-boot-demo.jar
before Docker build:
-
Jedi path 如果我们要在稳定的超薄Linux版本上使用 Oracle OpenJDK官方发行版(
Debian 9 Stretch
现在):
Jedi path if we want to use official Oracle OpenJDK distribution on stable slim Linux version (
Debian 9 "Stretch"
for now):
- 使用
debian :stretch-slim
(最新稳定)基本映像 -
使用 Docker多阶段构建
- use
debian:stretch-slim
(latest stable) base image use Docker multi-stage build
-
第一个Docker构建阶段:
First Docker build stage:
- 在第一个Docker构建阶段下载并安装
Oracle OpenJDK
存档 - 使用
jlink
工具
- download and install
Oracle OpenJDK
archive on the first Docker build stage - compile Java minimal distribution for your project (aka JRE) using
jlink
tool
第二个Docker构建阶段:
Second Docker build stage:
- 从该阶段复制编译的最小Java发行版1到新映像
- 配置访问Java的路径
- 将应用程序jar复制到映像
因此,最终 Dockerfile
看起来像这样
So, final Dockerfile
looks smth like this
(实现JDK 版本
, URL
和 HASH
值):
(actualize JDK VERSION
, URL
and HASH
value):
# First stage: JDK 11 with modules required for Spring Boot
FROM debian:stretch-slim as packager
# source JDK distribution names
# update from https://jdk.java.net/java-se-ri/11
ENV JDK_VERSION="11.0.1"
ENV JDK_URL="https://download.java.net/java/GA/jdk11/13/GPL/openjdk-${JDK_VERSION}_linux-x64_bin.tar.gz"
ENV JDK_HASH="7a6bb980b9c91c478421f865087ad2d69086a0583aeeb9e69204785e8e97dcfd"
ENV JDK_HASH_FILE="${JDK_ARJ_FILE}.sha2"
ENV JDK_ARJ_FILE="openjdk-${JDK_VERSION}.tar.gz"
# target JDK installation names
ENV OPT="/opt"
ENV JKD_DIR_NAME="jdk-${JDK_VERSION}"
ENV JAVA_HOME="${OPT}/${JKD_DIR_NAME}"
ENV JAVA_MINIMAL="${OPT}/java-minimal"
# downlodad JDK to the local file
ADD "$JDK_URL" "$JDK_ARJ_FILE"
# verify downloaded file hashsum
RUN { \
echo "Verify downloaded JDK file $JDK_ARJ_FILE:" && \
echo "$JDK_HASH $JDK_ARJ_FILE" > "$JDK_HASH_FILE" && \
sha256sum -c "$JDK_HASH_FILE" ; \
}
# extract JDK and add to PATH
RUN { \
echo "Unpack downloaded JDK to ${JAVA_HOME}/:" && \
mkdir -p "$OPT" && \
tar xf "$JDK_ARJ_FILE" -C "$OPT" ; \
}
ENV PATH="$PATH:$JAVA_HOME/bin"
RUN { \
java --version ; \
echo "jlink version:" && \
jlink --version ; \
}
# build modules distribution
RUN jlink \
--verbose \
--add-modules \
java.base,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument \
# java.naming - javax/naming/NamingException
# java.desktop - java/beans/PropertyEditorSupport
# java.management - javax/management/MBeanServer
# java.security.jgss - org/ietf/jgss/GSSException
# java.instrument - java/lang/instrument/IllegalClassFormatException
--compress 2 \
--strip-debug \
--no-header-files \
--no-man-pages \
--output "$JAVA_MINIMAL"
# Second stage, add only our minimal "JRE" distr and our app
FROM debian:stretch-slim
ENV JAVA_HOME=/opt/java-minimal
ENV PATH="$PATH:$JAVA_HOME/bin"
COPY --from=packager "$JAVA_HOME" "$JAVA_HOME"
COPY "build/libs/spring-boot-demo.jar" "/app.jar"
EXPOSE 8080
CMD [ "-jar", "/app.jar" ]
ENTRYPOINT [ "java" ]
注意:
- 其中包括5个Java模块到最小的JRE示例(
java.base,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument
)。我发现他们手动运行应用程序并修复ClassNotFoundException
。等待更多的Spring Boot开发人员建议/指南,包括哪些Java模块以及何时包括,这与删除一些冗余依赖项相同,例如java.desktop
,似乎仅用于对于PropertyEditorSupport
-
,如果您害怕错过某些模块-它们非常轻巧,它们总共提供了2个MB大小增加。获取
java。*
和jdk。*
11个模块的完整列表:
- there are 5 java modules included to the minimal JRE example (
java.base,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument
). I found them "manually" running the application and fixingClassNotFoundException
. Waiting for some further Spring Boot developers recommendations/guides which Java modules to include and when, as same as removing some redundant dependencies, likejava.desktop
, which seems to be used only forPropertyEditorSupport
if you are afraid to miss some modules - they are quite lightweight and all of them together give about 2 MB size increasing. Get a full list of
java.*
andjdk.*
11 modules:
java --list-modules | grep -E ^ java\。[^ @] * | cut -d @ -f 1
java --list-modules | grep -E ^ jdk\。[^ @] * | cut -d @ -f 1
在我的情况下,生成的图像大小为 123 MB ,带有最少7个Spring Boot模块, 125 MB ,带有所有 java。*
模块
The resulting image size in my case was 123 MB with minimal 7 Spring Boot modules and 125 MB with all java.*
modules
作为此构建工作流程的可选改进:
- 预构建下载并提取了JDK的图像,并将其用作第一阶段的基础图像
- 如果您每次都知道要包含哪些模块,请使用已编译的最小JRE和包含的模块预先构建基础图像
使用供应商的Open JDK发行版的简便方法:
与Oracle相反> Azul的Zulu JDK 11 支持高山港口,并具有各自的基本 Docker映像。
Opposite to Oracle Azul's Zulu JDK 11 supports Alpine port and has respective base Docker image.
因此,如果尊重Zulu JVM / JDK,则Docker构建会更加简单:
Thus, if Zulu JVM/JDK is respected, Docker build is much simpler:
FROM azul/zulu-openjdk-alpine:11 as packager
RUN { \
java --version ; \
echo "jlink version:" && \
jlink --version ; \
}
ENV JAVA_MINIMAL=/opt/jre
# build modules distribution
RUN jlink \
--verbose \
--add-modules \
java.base,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument \
# java.naming - javax/naming/NamingException
# java.desktop - java/beans/PropertyEditorSupport
# java.management - javax/management/MBeanServer
# java.security.jgss - org/ietf/jgss/GSSException
# java.instrument - java/lang/instrument/IllegalClassFormatException
--compress 2 \
--strip-debug \
--no-header-files \
--no-man-pages \
--output "$JAVA_MINIMAL"
# Second stage, add only our minimal "JRE" distr and our app
FROM alpine
ENV JAVA_MINIMAL=/opt/jre
ENV PATH="$PATH:$JAVA_MINIMAL/bin"
COPY --from=packager "$JAVA_MINIMAL" "$JAVA_MINIMAL"
COPY "build/libs/spring-boot-demo.jar" "/app.jar"
EXPOSE 8080
CMD [ "-jar", "/app.jar" ]
ENTRYPOINT [ "java" ]
生成的图像为 73 MB ,与剥离的Alpine发行版预期的一样。
The resulting image is 73 MB, as expected with stripped Alpine distributions.
这篇关于Java 11应用程序作为轻量级Docker映像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!