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.
至于 2018 年 12 月 7 日
,存在常见问题/陷阱(在上面的票证中讨论):
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,所以轻量级图像不容易创建
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 版本的原因基本泊坞窗图像
- 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:
- 从 openjdk 创建的 jlink 运行时图像大小(特别是 libjvm.so)很大.预计它会小得多.
- 解决方案: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 from 07.2019: https://stackoverflow.com/a/57145029/907576
以简单的 spring 启动应用程序(只有一个 REST 端点)为例,到目前为止我能够找出以下解决方案(考虑到应用程序 jar 位于 build/libs/spring-boot-demo.jar ).Docker 构建之前的 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:
绝地路径如果我们想在稳定的超薄 Linux 版本上使用官方 Oracle OpenJDK 发行版(
Debian 9 "Stretch"
for现在):
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
工具为您的项目(又名 JRE)编译 Java 最小发行版
- 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 复制到映像中
所以,最终的
So, final Dockerfile
looks smth like this
(实现JDK VERSION
、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" ]
注意:
- 最小 JRE 示例中包含 5 个 java 模块(
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 支持 Alpine 端口 并有各自的基础 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屋!