Java 11 应用程序作为轻量级 docker 镜像 [英] Java 11 application as lightweight docker image

查看:62
本文介绍了Java 11 应用程序作为轻量级 docker 镜像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

受到问题 为什么 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

  • 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:

由于这些问题,即使 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:

  1. 绝地路径如果我们想在稳定的超薄 Linux 版本上使用官方 Oracle OpenJDK 发行版(Debian 9 "Stretch" for现在):

  1. Jedi path if we want to use official Oracle OpenJDK distribution on stable slim Linux version (Debian 9 "Stretch" for now):

  • use debian:stretch-slim (latest stable) base image
  • use Docker multi-stage build

  1. 第一个 Docker 构建阶段:

  1. 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 复制到映像中

所以,最终的 Dockerfile 看起来像这样

So, final Dockerfile looks smth like this

(实现JDK VERSIONURLHASH):

(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 fixing ClassNotFoundException. Waiting for some further Spring Boot developers recommendations/guides which Java modules to include and when, as same as removing some redundant dependencies, like java.desktop, which seems to be used only for PropertyEditorSupport
  • 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.* and jdk.* 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆