写点什么

快速实现不打折扣的云原生 Java 应用

作者:Tobi Ajila, Thomas Watson

  • 2023-06-02
    北京
  • 本文字数:4694 字

    阅读完需:约 15 分钟

快速实现不打折扣的云原生Java应用

近年来,向云原生计算迁移一直是开发人员比较关注的事情,按照云原生方式他们的业务应用能够从减少 IT 基础设施,提高可扩展性等方面受益。当涉及到云端部署应用程序时,“伸缩至零(scale-to-zero)”是标准的供应(provisioning)策略,以便于在业务需求较低的时候降低成本。随着需求的增加,会供应更多的应用程序实例和运行时,这种扩展必须非常迅速,这样终端用户才不会遇到响应滞后的问题。运行时的启动时间会对扩展性能产生很大的影响。

 

Open Liberty是一个云原生 Java 运行时,与其他 Java 运行时类似,它是建立在 JVM 技术之上的。JVM(更广泛地说,整个 JDK)所提供的性能、调试能力和类库使其成为支撑应用程序的重要技术。尽管 JVM 以出色的吞吐能力而闻名,但是其启动时间却落后于 Go 和 C++等静态编译语言。考虑到伸缩至零的需求,所以多年以来,显著改善启动时间一直是所有 JVM 实现的关键创新领域。AppCDS(HotSpot)Shared Classes Cache(Eclipse OpenJ9)这样的元数据缓存技术在启动时间方面做出了令人瞩目的改进,但并没有在启动时间方面实现数量级级别的减少,而这恰好是 serverless 计算的扩展场景所需要的。

 

编译为原生镜像以减少启动时间

 

Graal Native Image曾经宣布,借助编译为原生的方式,它能够实现低于 100 毫秒的启动时间,因此得到很多的关注。这是 JVM 领域的一个重大转变,因为这是 Java 第一次在启动时间方面能够与 C++相抗衡。虽然 Graal Native Image 显著降低了启动时间,但这也是有一定代价的。

 

首先,静态编译要求在构建时对应用有一个全局的了解。对于开发人员来说,他们在构建应用时一直依赖的动态能力将会受到限制。比如,像反射、动态类加载和 invokedynamic 等操作均需要特殊处理,因为它们会干扰生成原生镜像时的静态分析。这意味着,我们可能需要对应用程序进行大量的修改,这样原生镜像才能够正常运行,更糟糕的是,应用程序的依赖可能也需要更新。

其次,调试会变得更具挑战性,因为我们此时调试的已经不是一个 JVM 应用,而是一个原生可执行文件。我们需要将熟悉的 Java 调试器替换为原生调试器(如gdb)来调查问题。有种解决方式是在开发环境中使用 JVM,在生产环境中使用原生镜像。但是,这意味着生产环境与开发环境是不一致的,最终我们可能必须要在两个不同的运行时中修复缺陷!

 

最后,JVM 提供的优秀的特性之一就是出色的吞吐量,即时(just-in-time)编译器能够在运行时根据实时数据优化应用程序以达到最佳性能。这一点也必须在原生镜像中牺牲掉,因为开发人员只有一次编译的机会,那就是在构建时。有些框架,比如Spring Native,已经建立了帮助 Java 开发人员在原生镜像约束下运行应用程序的能力,但不得不承认的是,开发人员必须放弃一些东西以获取原生镜像在启动时间方面的收益。

 

使用检查点/恢复功能跳过启动时间

 

Liberty 运行时采用了一种不同的方式来改善启动时间。Liberty 的目标是提供快速启动,而不必进行取舍,这是通过一项名为Liberty InstantOn的特性实现的。该特性提供了 Java 开发人员熟悉的所有功能,相对于没有使用 InstantOn 的 JVM 运行环境,它的运行时启动时间最多能够优化 10 倍。

 

从基础上来讲,Liberty InstantOn 是基于检查点/恢复(checkpoint/restore)技术的。我们启动一个应用程序,然后暂停它,并在某些明确定义的点上持久化应用程序的状态,这就是检查点。这个检查点就会成为应用镜像,当部署应用的时候,我们只需要从已保存的状态恢复镜像即可,这就是恢复,通过这种方式,应用程序就跳过了它通常要经历的启动和初始化过程(因为这些步骤已经运行过了)。

 

Liberty 使用了OpenJ9 CRIU提供的支撑功能,这是一项基于Linux CRIU的技术,它能够让任意应用均支持检查点和恢复。在 Liberty InstantOn 方式中,因为我们依然在 JVM 上运行,所以在吞吐量性能方面没有任何损失。Java 调试也能按照预期方式运行,所有依赖动态 JVM 能力的库也能正常运行。

 

解决检查点/恢复机制的局限性

 

尽管检查点/恢复的概念听起来很简单,但在现实中,会有一些限制(这是CRIU的运行方式所导致的)需要由运行时和 JVM 共同来解决,以便于让应用程序体验到这些收益。当执行检查点时(此时会构建镜像),CRIU 会将环境“冻结”在检查点状态中,包括环境变量、计算资源的信息(CPU、内存)以及时间本身的信息都会打包到镜像中。这些东西中的任何一项在恢复环境中都可能是不同的,从而导致应用程序的不一致,这是很难追踪的。此外,检查点可能会捕获一些数据,如果镜像要通过容器注册中心跨公共网络传输到部署环境中,这就不是很理想了。这些数据可能包含对端点的外部连接,而这些连接在恢复环境中可能并不存在,另外数据中还可能包含我们不想在检查点镜像中嵌入的安全令牌。

 

基于这些原因,OpenJ9 CRIU 支持内置的补偿机制,以确保在检查点保存的应用在恢复时的行为是正确和安全的。对时间敏感的 API 进行了修改以补偿检查点和恢复时的停机时间。对于像SecureRandom这样的随机 API 在恢复的时候对种子进行了重置(re-seed),以确保每次检查点恢复时,它都会被恢复为唯一的实例。

 

JVM 可以解决它所知道的所有事情,但是应用程序代码可能也需要类似的处理。Liberty 运行时通过与 JVM 合作来解决 JVM 无法自行处理的剩余问题,从而帮助开发人员摆脱检查点/恢复的复杂性。为了实现这一点,OpenJ9提供了一个钩子机制,开发人员可以利用它来注册在检查点之前和之后要执行的方法。这种机制被 Liberty 广泛采用,比如,在部署时重新解析配置,以确保为环境使用正确的配置。

 

所以,尽管 OpenJ9 提供了高效利用检查点/恢复技术的工具,但是增强现有应用程序启动时间的最简单的方式是在 Liberty 上使用 Liberty InstantOn 来运行它。Liberty InstantOn 对检查点/恢复过程进行了抽象,将开发人员的选择简化为只有几项,比如确定检查点用于应用程序启动之前还是之后。

 

总而言之,我们的终极目标是改善 Java 应用程序的云原生体验,这意味着无论采用什么样的技术,都必须要在云环境中高效运行。Liberty InstantOn 与容器技术(如 Docker 和Podman)实现了无缝集成。Liberty InstantOn 还能与KnativeOpenShift等容器引擎协作。我们完成了相关的工作以确保 Liberty InstantOn 能够在非特权模式下运行,因为这对生产环境的安全性是非常重要的。这项工作的成果正在回馈给 CRIU 项目。

 

使用自己的项目尝试一下 Liberty InstantOn

 

Liberty InstantOn 的 beta 版本已经公开可用,开发人员可以使用现有的应用程序进行尝试,以观察启动时间的改进(最多能够快 10 倍)。你只需要使用 Liberty InstantOn 工具为你的应用程序创建一个应用容器镜像即可。Open Liberty 发布了生产就绪的容器镜像,使你的应用程序可以很容易地进行容器化,以便于在容器引擎中运行,比如 Docker、Podman 或 Red Hat OpenShift 这样的 Kubernetes 环境。

 

Open Liberty 容器镜像包含了所有必要的依赖,以便于在 Open Liberty 运行时中运行应用程序。如下针对开发人员的指南描述了如何基于 Open Liberty beta-instanton 镜像(icr.io/appcafe/open-liberty:beta-instanton)创建一个带有应用程序的基础应用容器镜像,然后如何在此基础之上创建并添加一个包含检查点进程状态的层。beta-instanton 镜像包含了对 Open Liberty 创建检查点并将检查点进程存储到容器镜像层中所需的所有先决条件。这包括对 OpenJ9 CRIU 和 Linux CRIU 支持的早期访问构建。

 

如何使用 Liberty InstantOn 容器化应用程序,使其能够更快启动

如下的指南使用 Podman 来构建和运行容器,并且使用了Open Liberty入门指南中的应用程序。如果你手头有自己的应用程序,可以将其替换掉。

 

入门应用程序包含了一个Dockerfile,如下所示:

 

FROM icr.io/appcafe/open-liberty:full-java11-openj9-ubi

ARG VERSION=1.0ARG REVISION=SNAPSHOT

COPY --chown=1001:0 src/main/liberty/config/ /config/COPY --chown=1001:0 target/*.war /config/apps/

RUN configure.sh
复制代码

 

首先,开发人员需要更新 FROM 指令以使用 beta-instanton 镜像:

 

FROM icr.io/appcafe/open-liberty:beta-instanton
复制代码

 

然后,借助更新后的 Dockerfile,可以使用如下的命令构建应用容器镜像:

 

podman build –t getting-started .
复制代码

 

该命令会创建应用容器镜像,但是还没有创建检查点进程。应用程序的检查点进程是通过如下命令运行应用容器镜像来创建的,这里添加了一些额外的选项:

 

podman run \--name getting-started-checkpoint-container \--privileged \--env WLP_CHECKPOINT=applications \getting-started
复制代码

 

通过 WLP_CHECKPOINT 变量,Open Liberty 运行时声明在被配置的应用启动后但在打开端口以接受传入的请求之前对应用进程生成检查点。当应用程序进程完成检查点生成后,运行中的容器将会停止。这将会形成一个包含检查点进程状态的已停止的容器。

 

最后一步是将这个检查点进程状态以层的形式添加到原始应用程序的进程镜像中。这可以通过如下的命令将名为 getting-started-checkpoint-container 的已停止应用容器提交给一个新的容器镜像:

 

podman commit \getting-started-checkpoint-container \getting-started-instanton
复制代码

 

最终的结果是可运行的 getting-started-instanton 容器镜像。

 

运行具有 Linux 特权能力的容器

当运行 getting-started-instanton 容器时,开发人员必须授予其一组Linux能力,以便容器镜像中的 CRIU 二进制文件执行恢复过程:

 

  • cap_checkpoint_restore

  • cap_net_admin

  • cap_sys_ptrace

 

在创建检查点进程时,使用了一个具有特权的容器,它授予了容器镜像中的 CRIU 二进制文件所需的Linux能力

 

请通过如下的 Podman 命令以运行具有三种所需能力的容器:

podman run \--rm \--cap-add=CHECKPOINT_RESTORE \--cap-add=NET_ADMIN \--cap-add=SYS_PTRACE \-p 9080:9080 \getting-started-instanton
复制代码

 

getting-started-instanton 容器会以必要的权限来运行,以执行恢复过程,应用程序的运行速度要比原始的 getting-started 应用程序快 10 倍。

 

未来的改进

 

Open Liberty的beta版本发布了对 Liberty InstantOn 的定期更新。未来的版本已经规划了一些改进,它们将会让 Liberty InstantOn 构建和运行应用镜像变得更加容易。例如,为了消除对 NET_ADMIN Linux能力的要求,一些额外的相关工作已经完成。还有一项计划是在恢复应用的过程中移除对 SYS_PTRACE 能力的要求。这将减少运行应用所需的能力清单,在运行应用程序的时候,仅需 CHECKPOINT_RESTORE 能力即可。

 

其他规划包括在应用容器的构建步骤中执行应用进程检查点,这样不需要容器运行和容器提交命令就能将应用进程状态存储到应用容器镜像层中了。

 

请反馈你们的想法

 

虽然云原生需要对组织的业务方式做出许多变化,但是有了 Liberty InstantOn,开发人员就不用担心改变他们的应用开发方式了。

 

我们鼓励开发人员使用 Open Liberty 22.0.0.11-beta 或后续版本来尝试Liberty InstantOn的beta功能。欢迎通过项目的邮件列表进行反馈。如果遇到问题,开发人员可以在StackOverflow上发布问题。如果发现缺陷的话,欢迎提交issue

 

背景说明

 

Open LibertyEclipse OpenJ9均是开源项目。IBM 基于这些项目构建了其商业的WebSphere Liberty Java 运行时和IBM Semeru Runtimes Java 发行版。Liberty InstantOn 使用了 Linux Checkpoint/Restore In Userspace(CRIU)项目提供的检查点/恢复技术,并与 CRIU 合作,将代码反馈给该项目。

 

原文链接:


https://www.infoq.com/articles/rapid-startup-of-your-cloud-native-java/

 

相关阅读:


Java 是如何毁掉你的编程思维的?

系统学 Java,看这篇 Java 综合笔记万字总结就够了!..

Java 8 之后的新特性都是鸡肋吗?

扫盲篇:Java 中为啥一个 main 方法就能启动项目?

2023-06-02 11:0011367

评论 1 条评论

发布
用户头像
这种native程序应该是像Rust那样内存安全的吧?
2023-06-03 23:35 · 湖南
回复
没有更多了
发现更多内容

软件的license是什么意思?作用是什么?

行云管家

软件 运维 许可证

互联网公司目标管理OKR和绩效考核误区

laofo

OKR 研发效能 互联网公司 快手 绩效考核 GRAD

Reactor百万连接的并发

C++后台开发

reactor 高并发 epoll Linux服务器开发 C++后台开发

通过 Amazon API Gateway 和 Amazon Lambda 实现基于 Restful API 的 CloudFront Distribution 复制/克隆功能

亚马逊云科技 (Amazon Web Services)

Lambda Gateway

融云首席科学家任杰:历练出人才,职场「经历>经验」

融云 RongCloud

Tapdata Cloud 2.1.5来啦:新增支持Amazon RDS数据库,错误日志查询更便捷,Agent部署细节再优化

tapdata

SaaS 数据同步 polarDB Amazon RDS

TDengine在弘源泰平量化投资中的实践

TDengine

数据库 tdengine 开源 时序数据库

B站S11破亿直播在线稳定性保障秘籍——演讲实录

TakinTalks稳定性社区

混沌工程 系统稳定性 全链路压测 安全生产

程序员,做业务还是做技术更有前途?

张泽豪

程序员

架构实战营|模块1

KDA

#架构实战营

互联网大厂研发效能团队的需求管理

laofo

互联网 DevOps cicd 研发效能 CI/CD

学生管理系统架构设计图

Justin1024

零基础可以学云计算运维吗?怎样从才可以学好?

行云管家

云计算 运维 IT运维 云运维 服务器运维

互联网研发效能专家怎么找?

laofo

python实现连接两个数据库

YUKI0506

设施资产管理系统解决方案

低代码小观

资产管理 CRM CRM系统 企业设备管理 设备巡检管理系统

互联网公司目标管理OKR实践落地与反思

laofo

互联网 OKR 研发效能 绩效管理 快手

ShardingSphere 在东南亚|与科技保险公司 Fuse 的技术融合

SphereEx

Apache 开源 ShardingSphere SphereEx 数据库·

微信业务架构,学生管理系统架构设计

泋清

#架构实战营

css 学习笔记【二】-背景,渐变,链接文档的MIME类,id选择器和类选择器的区别

恒山其若陋兮

5月月更

Tapdata x 轻流,为用户打造实时接入轻流的数据高速通道

tapdata

SaaS 数据同步 实时数据 轻流

私有化的IM即时通讯平台,企业首选的沟通工具

BeeWorks

架构7期模块1作业

Elvis FAN

架构实战营

为什么说 MongoDB 和 HBase 不适用于汽车行业的时序数据处理?

TDengine

数据库 tdengine 开源 时序数据库

时序数据库的集群方案?

TDengine

数据库 tdengine 开源

面试突击49:说一下 JUC 中的 Exchange 交换器?

王磊

Java java面试

万字长文:手把手教你实现一套高效的IM长连接自适应心跳保活机制

JackJiang

TCP 网络编程 即时通讯 im开发 心跳保活

直播预约|数据指标体系如何搭建才最有效,从0到1带你快速入门

袋鼠云数栈

大数据 数据中台

员工考勤打卡时,如何避免非本人代替打卡?

华为云开发者联盟

人脸识别 打卡 华为云FRS 华为云FunctionGraph 华为云APIG

开启分布式应用性能观测(APM)

观测云

可观测性 可观测

场景实践 | 如何使用融云超级群构建游戏社区

融云 RongCloud

快速实现不打折扣的云原生Java应用_服务革新_InfoQ精选文章