写点什么

使用 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:432159
用户头像

发布了 1942 篇内容, 共 163.5 次阅读, 收获喜欢 81 次。

关注

评论

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

快递鸟 API 接口全面解析:企业级物流解决方案

快递鸟

图灵进化招人啦

慢点科技SlowTech

文心快码3.5S开发古风射覆小游戏,它帅到我了!

Comate编码助手

AI 编程 文心快码 文心快码3.5S AI编程助手

OceanBase 分布式数据库的 ETL 实践:从抽取到实时分析

谷云科技RestCloud

postgresql 数据同步 ETL 数据集成 oceanbase

过程控制 软件维护经验总结(2)

万里无云万里天

工业 工厂运维 过程控制

过程控制 通讯维护经验总结(2)

万里无云万里天

工业 工厂运维 过程控制

由 Mybatis 源码畅谈软件设计(七):从根上理解 Mybatis 一级缓存

京东科技开发者

星光不负 码向未来 | 从 Java 后端到鸿蒙开发者:我的真实成长蜕变记

谢道韫

《哪吒2》与国产超高清标准突围

脑极体

AI

Sentieon | 水稻全基因组(WGS)分析全流程

INSVAST

Sentieon WGS 分子育种 全基因组分析流程 农作物

云栖实录 | AI原生搜索引擎:Elasticsearch 换“芯”——AI原生搜索内核增强技术

阿里云大数据AI技术

elasticsearch 阿里云 AI搜索

AI智能体开发:低代码+多模态技术,让智能应用落地更高效

上海拔俗

从“腰酸背疼”到“帝王坐”,只差一把保友金豪E2 Pro人体工学椅

袁袁袁袁满

人体工学 人体工学椅 办公椅

集成平台如何实现“应用即服务”?深入解析 iPaaS 的服务化理念

谷云科技RestCloud

数据处理 数据传输 集成平台 应用集成 ipaas

2025青科会启幕,网易伏羲携游戏AI前沿实践共话未来

网易伏羲

知乎 网易伏羲 游戏AI 具身智能 群体智能

分享 4 款基于 .NET 开源免费、实用的文件搜索工具,效率提升利器!

追逐时光者

.net

开了 8 年母婴店,靠微擎守住了 20000 会员的信任,再也不怕数据泄露

微擎应用市场

护理技能AI智能交互训练系统:用AI技术重构护理实训新范式

上海拔俗

钢铁行业数字化利器,TDengine 时序数据库荣获金恒科技“年度卓越供应商”

TDengine

tdengine 时序数据库 国产时序数据库

每个团队都需要“超级引导者”

ShineScrum

敏捷 沟通艺术 引导者

由 Mybatis 源码畅谈软件设计(四):动态 SQL 执行流程

京东科技开发者

大规模微服务系统中的雪崩故障防治

百度Geek说

百度 SRE 系统稳定性 雪崩预防和应对 微服务架构治理

C#实现多线程的几种方式

追逐时光者

C# .net

springboot项目集成大数据第三方dolphinscheduler调度器

刘大猫

人工智能 云计算 算法 物联网 大模型

Gin笔记二之gin.Engine和路由设置

Hunter熊

golang gin

AI语料库智慧教学平台:用AI技术解锁语料教学新生态

上海拔俗

Squid 可观测性最佳实践

观测云

squid

百度亮相 SREcon25:搜索稳定背后的秘密,微服务雪崩故障防范

Baidu AICLOUD

微服务架构 SRE

纯配时效服务-双Redis集群设计

京东科技开发者

ASP.NET MVC+LayUI视频上传

追逐时光者

.net 微软 layui

从“走出去”到“强起来”,中国出海三强的高端攻坚战

脑极体

AI

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