QCon 演讲火热征集中,快来分享技术实践与洞见! 了解详情
写点什么

使用 GraalVM 为 Java 应用程序构建最少的 Docker 镜像

  • 2019-09-27
  • 本文字数:4232 字

    阅读完需:约 14 分钟

使用 GraalVM 为 Java 应用程序构建最少的 Docker 镜像

优化 Docker 镜像的大小有诸多好处。其中一个好处是能加快部署速度。如果您的应用程序需要快速向外扩展以响应意外的突发流量,那么这一点将非常重要。在这篇博文中,我将向您展示通过一种有趣的方法来优化 Java 应用程序的 Docker 镜像,这也有助于缩短启动时间。所举的例子基于我几个月前发布的另一篇博文 Reactive Microservices Architecture on AWS。

Java 应用程序的工作原理是怎样的?

Java 应用程序使用 Java 11 实现,并使用 Vert.x 3.6 作为主框架。Vert.x 是具有反应性和非阻塞特性的事件驱动型 Polyglot 框架,其作用是实施微服务。它在 Java 虚拟机 (JVM) 上运行,使用低级 I/O 库 Netty。该应用程序包含五个不同的 verticle,涵盖业务逻辑的不同方面。


为了构建此应用程序,我使用了采用不同配置文件的 Maven。第一个配置文件(默认配置文件)使用“标准”构建以创建 Uber JAR(一个包含所有依赖项的独立应用程序)。第二个配置文件使用 GraalVM 编译本地镜像。标准构建使用 jlink 来构建一个具有有限模块集的自定义 Java 运行时。(使用命令行工具 jlink 可关联一系列模块及其可传递依赖项,以创建运行时镜像。)

使用 jlink 构建自定义 JDK 分配

我们需要关注 JDK 9 中包含的 Java 平台模块功能 (JPMS),也称为 Project Jigsaw。该功能是为构建仅包含必要依赖项的模块化 Java 运行时而开发的。对于此应用程序,您只需要数量有限的一组模块(可在构建过程中指定)。在构建前的准备工作中,请下载 Amazon Corretto 11 并解压,然后删除 JDK 附带的 src.zip 文件等不必要的文件。为帮助读者更好地理解相关内容,以下部分使用了多阶段构建,并单独介绍了构建的不同部分。

第 1 步:构建自定义运行时模块

构建过程的第一步是构建自定义运行时(只包含运行应用程序所必需的几个模块),然后将结果写入 /opt/minimal:


FROM debian:9-slim AS builderLABEL maintainer="Sascha Möllering <smoell@amazon.de>"
# First step: build java runtime moduleRUN set -ex && \ apt-get update && apt-get install -y wget unzip && \ wget https://d3pxv6yz143wms.cloudfront.net/11.0.3.7.1/amazon-corretto-11.0.3.7.1-linux-x64.tar.gz -nv && \ mkdir -p /opt/jdk && \ tar zxvf amazon-corretto-11.0.3.7.1-linux-x64.tar.gz -C /opt/jdk --strip-components=1 && \ rm amazon-corretto-11.0.3.7.1-linux-x64.tar.gz && \ rm /opt/jdk/lib/src.zip
RUN /opt/jdk/bin/jlink \ --module-path /opt/jdk/jmods \ --verbose \ --add-modules java.base,java.logging,java.naming,java.net.http,java.se,java.security.jgss,java.security.sasl,jdk.aot,jdk.attach,jdk.compiler,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.internal.ed,jdk.internal.le,jdk.internal.opt,jdk.naming.dns,jdk.net,jdk.security.auth,jdk.security.jgss,jdk.unsupported,jdk.zipfs \ --output /opt/jdk-minimal \ --compress 2 \ --no-header-files
复制代码

第 2 步:将自定义运行时复制到目标镜像

接下来,将新创建的自定义运行时从构建映像复制到实际目标镜像。在这一步,您要再次使用 debian:9-slim 作为基本镜像。复制最小运行时后,将 Java 应用程序复制到 /opt/,添加 Docker 运行状况检查,然后启动 Java 进程:


FROM debian:9-slimLABEL maintainer="Sascha Möllering <smoell@amazon.de>"
COPY --from=builder /opt/jdk-minimal /opt/jdk-minimal
ENV JAVA_HOME=/opt/jdk-minimalENV PATH="$PATH:$JAVA_HOME/bin"
RUN mkdir /opt/app && apt-get update && apt-get install curl -yCOPY target/reactive-vertx-1.5-fat.jar /opt/app
HEALTHCHECK --interval=5s --timeout=3s --retries=3 \ CMD curl -f http://localhost:8080/health/check || exit 1
EXPOSE 8080
CMD ["java", "-server", "-XX:+DoEscapeAnalysis", "-XX:+UseStringDeduplication", \ "-XX:+UseCompressedOops", "-XX:+UseG1GC", \ "-jar", "opt/app/reactive-vertx-1.5-fat.jar"]
复制代码

使用 GraalVM 将 Java 编译为本机

GraalVM 是 Oracle 提供的一种开源、高性能 Polyglot 虚拟机。使用它可以提前编译本机镜像,以提高启动性能,并减少基于 JVM 的应用程序的内存消耗和文件大小。允许预先编译的框架称为 SubstrateVM。


在以下部分,您可以看到 pom.xml 文件的相关片段。创建一个名为 native-image-fargate 的附加 Maven 配置文件,该文件使用 native-image-maven 插件在“包装”阶段将源代码编译为本机镜像:


<profile>    <id>native-image-fargate</id>    <build>        <plugins>            <plugin>                <groupId>com.oracle.substratevm</groupId>                <artifactId>native-image-maven-plugin</artifactId>                <version>${graal.version}</version>                <executions>                    <execution>                        <goals>                            <goal>native-image</goal>                        </goals>                        <phase>package</phase>                    </execution>                </executions>                <configuration>                    <imageName>${project.artifactId}</imageName>                    <mainClass>${vertx.verticle}</mainClass>                    <buildArgs>--enable-all-security-services -H:+ReportUnsupportedElementsAtRuntime --allow-incomplete-classpath</buildArgs>                </configuration>            </plugin>        </plugins>    </build></profile>
复制代码

Docker 多阶段构建

您的目标是定义一个包含尽可能少相关项的可重现构建环境。为此,要创建一个使用 Docker 多阶段构建的自包含构建过程。


对于多阶段构建而言,一个值得关注的方面在于,您可以在 Dockerfile 中使用多个 FROM 语句。每条 FROM 指令可以使用不同的基本镜像,并开始构建的新阶段。您可以选择必需的文件并将其从一个阶段复制到另一个阶段,这非常有用,因为这样可以限制必须复制的文件数。使用此功能可以在一个阶段构建应用程序,并将已编译的构件和其他文件复制到目标镜像。


在以下部分,您可以看到构建的两个不同阶段。您的 Dockerfile(称为 Dockerfile-native)分为两部分:构建器镜像和目标镜像。


第一个代码示例显示了基于 graalvm-ce 的生成器镜像。在构建过程中,必须安装 Maven、设置一些环境变量,并将必要的文件复制到 Docker 镜像中。对于构建,您需要源代码和 pom.xml 文件。成功将文件复制到 Docker 镜像后,将使用配置文件 native-image-fargate 启动应用程序到可执行二进制文件的构建。当然,也可以使用 Maven 基本镜像并安装 GraalVM(整个构建过程会稍有不同)。


FROM oracle/graalvm-ce:1.0.0-rc16 AS build-aot
RUN yum update -yRUN yum install wget -yRUN wget https://www-eu.apache.org/dist/maven/maven-3/3.6.1/binaries/apache-maven-3.6.1-bin.tar.gz -P /tmpRUN tar xf /tmp/apache-maven-3.6.1-bin.tar.gz -C /optRUN ln -s /opt/apache-maven-3.6.1 /opt/mavenRUN ln -s /opt/graalvm-ce-1.0.0-rc16 /opt/graalvm
ENV JAVA_HOME=/opt/graalvmENV M2_HOME=/opt/mavenENV MAVEN_HOME=/opt/mavenENV PATH=${M2_HOME}/bin:${PATH}ENV PATH=${JAVA_HOME}/bin:${PATH}
COPY ./pom.xml ./pom.xmlCOPY src ./src/
ENV MAVEN_OPTS='-Xmx6g'RUN mvn -Dmaven.test.skip=true -Pnative-image-fargate clean package
复制代码


现在,开始执行多阶段构建过程中第二部的操作:创建实际目标镜像。此镜像基于 debian:9-slim,并将两个环境变量设置为 TLS 特定设置(因为应用程序使用 TLS 与 Amazon Kinesis 数据流通信)。


FROM debian:9-slimLABEL maintainer="Sascha Möllering <smoell@amazon.de>"
ENV javax.net.ssl.trustStore /cacertsENV javax.net.ssl.trustAnchors /cacerts
RUN apt-get update && apt-get install -y curl
COPY --from=build-aot target/reactive-vertx /usr/bin/reactive-vertxCOPY --from=build-aot /opt/graalvm/jre/lib/amd64/libsunec.so /libsunec.soCOPY --from=build-aot /opt/graalvm/jre/lib/security/cacerts /cacerts
HEALTHCHECK --interval=5s --timeout=3s --retries=3 \ CMD curl -f http://localhost:8080/health/check || exit 1
EXPOSE 8080
CMD [ "/usr/bin/reactive-vertx" ]
复制代码


构建目标镜像非常简单。运行以下命令:


docker build . -t <your_docker_repo>/reactive-vertx-native -f Dockerfile-native
复制代码


要使用 Uber JAR 构建标准 Docker 镜像,请运行以下命令:


docker build . -t <your_docker_repo>/reactive-vertx -f Dockerfile
复制代码


成功完成两个构建后,运行命令 Docker 镜像将显示以下结果:


REPOSITORY                     TAG                 IMAGE ID            CREATED             SIZEsmoell/reactive-vertx          latest              391f944bb553        19 minutes ago      181MB<none>                         <none>              389ee5ec6a8c        19 minutes ago      411MBsmoell/reactive-vertx-native   latest              ecd72b58a3d2        25 minutes ago      133MB<none>                         <none>              d93993d1d5ab        26 minutes ago      2.89GBdebian                         9-slim              92d2f0789514        4 days ago          55.3MBoracle/graalvm-ce              1.0.0-rc16          131b80926177        2 weeks ago         1.72G
复制代码


您可以看到在构建中使用的不同基本镜像(oracle/graalvm-ce:1.0.0-rc16 和 debian:9-slim)、构建期间使用的临时映像(没有合适的名称)以及目标镜像 smoell/reactive-vertx 和 smoell/reactive-vertx-native。

小结

在本博文中,我介绍了如何通过基于 Docker 多阶段构建的自包含应用程序,使用 GraalVM 将 Java 应用程序编译为本机镜像。我还演示了如何使用 jlink 为较小的目标镜像创建自定义 JDK 分配。我们希望这些内容能为您带来一些灵感,帮助您优化现有 Java 应用程序以减少启动时间和内存消耗。


本文转载自 AWS 技术博客。


原文链接:


https://amazonaws-china.com/cn/blogs/china/using-graalvm-build-minimal-docker-images-java-applications/


2019-09-27 14:431881
用户头像

发布了 1854 篇内容, 共 121.7 次阅读, 收获喜欢 78 次。

关注

评论

发布
暂无评论
发现更多内容

探索数据湖和大数据在亚马逊云服务云存储服务上的威力

亚马逊云科技 (Amazon Web Services)

大数据 S3 EMR Amazon Glue 云存储服务

软件测试/测试开发丨探索标识符在编程中的重要性

测试人

软件测试

从BST到LSM的进阶之路 | 京东物流技术团队

京东科技开发者

数据结构 Lsm 数据结构与算法 BST 企业号11月PK榜

稳定扩散与潜伏扩散:哪个更好?

3D建模设计

人工智能 机器学习 Stable Diffusion Latent Diffusion

Stable Diffusion 是否使用 GPU?

3D建模设计

Stable Diffusion 稳定扩散 img2img text2img AI图像合成

简单好用的数据恢复 Disk Drill激活最新中文版

mac大玩家j

数据恢复 恢复数据 Mac软件

对话在行人|飞龙汽车:通过业财融合,年节约成本2000多万

用友BIP

【万字长文】前端性能优化实践 | 京东云技术团队

京东科技开发者

性能优化 前端

CPU vs GPU:谁更适合进行图像处理?

Finovy Cloud

gpu cpu

什么是稳定扩散,它是如何工作的?

3D建模设计

机器学习 Stable Diffustion 稳定扩散

如何通过一个 SQL 语句构建实时数仓?

NineData

数据分析 sql 数据库系统 数据查询 异构

稳定扩散 img2img 的工作原理

3D建模设计

Stable Diffusion 稳定扩散 img2img text2img

什么是稳定扩散中的采样步骤?

3D建模设计

Stable Diffusion 稳定扩散 图像修复 img2img text2img

MYSQL 事务的底层原理 | 京东物流技术团队

京东科技开发者

MySQL 数据库 事务 底层原理 企业号11月PK榜

BusyCal for Mac(任务日历工具) 2023.4.1中文激活版

mac

苹果mac Windows软件 BusyCal 日历应用程序

Mac远程控制工具 Screens 4中文最新激活版

胖墩儿不胖y

远程控制软件 Mac软件 远程工具

教您如何使用API接口获取拼多多商品详情

Noah

架构实战营-模块七作业

王朝阳

如何定制开发软件 App?

Geek_16d138

定制软件开发 app定制开发

使用 Stable Diffusion Img2Img 生成、放大、模糊和增强

3D建模设计

Stable Diffusion 稳定扩散 图像修复 AI 图像生成

OmniGraffle for mac(绘图软件) v7.22.2中文正式版

mac

苹果mac Windows软件 OmniGraffle 图表设计软件

企业软件app定制开发流程

Geek_16d138

app定制开发 软件开发定制

职业倦怠无所畏惧,智慧云为你的工作增添活力

知者如C

使用 GraalVM 为 Java 应用程序构建最少的 Docker 镜像_语言 & 开发_亚马逊云科技 (Amazon Web Services)_InfoQ精选文章