Blog chevron_right Cloud Cost

降低云计算支出的捷径 

A Shortcut to Reducing Cloud Compute Spending 

总结 

许多组织寻求控制和降低其云计算成本,而多种往往相辅相成的技术可用来实现这一目标。在不修改代码或重新设计架构的前提下,在更优的 JVM 上运行应用程序,是一种快速、低工作量、低投入的方式,可降低 Java 及基于 JVM 的应用程序的计算成本并提升效率。

在本文中,您将了解使用更优的 JVM 来降低 Java 和基于 JVM 的应用程序和基础设施中的系统性云计算成本的三种主要方式

  • 速度:JVM 能更快地运行应用程序代码,可减少完成相同工作量所需的 CPU
  • 一致性:JVM 能在更高利用率水平下提升执行一致性,使组织能够提高自身利用率目标,从而从付费的每个 vCPU 中挖掘出更多工作产出 
  • 弹性:JVM 通过减少会影响服务水平的相关异常来优化预热过程,进而在不影响流量的情况下提升弹性。当计算资源无需使用或利用率不足时,您可以更及时地关闭这些资源

我们将逐一探讨这些资源,并说明如何使用这些资源来降低成本。

降低计算成本最直接的方式之一是提升每个应用程序的性能,使其可以用更少的资源交付同等工作量。虽然对每个应用程序的代码进行重构以提升其效率有时能带来显著成效,但还有更快捷、更系统的方法来提升性能和降低成本。

提升应用程序性能最直接且见效最快的方式,是在更快或更经济的计算基础设施上运行相同的应用程序代码。

那么,更快且更经济的计算基础设施从何而来?

这些年来,我们用于运行应用程序的 vCPU 通常变得越来越快。当速度更快的 vCPU 以相同的每 vCPU 价格(甚至每美元对应的 vCPU 速度更快)提供时,将云计算消耗迁移至这些新型 vCPU 显然是降低成本的一种方式。选择另一种通常是一种浪费。

当可以提供相同性能但更便宜的 vCPU 出现时,情况也是如此。这种情况有时会在 vCPU 架构或供应商改变时发生(例如 x86-64 与 Aarch64、Intel 与 AMD)。但随着时间的推移,在给定的架构和硬件供应商内,这种情况也会发生,因为随着摩尔定律的自然发展,越来越多的 vCPU 可以集成到硅芯片中。

提升应用程序性能最直接且见效最快的方式,是在更快或更经济的计算基础设施上运行相同的应用程序代码。

Java 和基于 JVM 的应用程序可以利用底层 vCPU 的此类变化,因为在大多数情况下,相同的 Java 代码几乎可以在所有云计算实例类型上运行。而且由于底层 JVM 会调整应用程序代码,使其既能在其部署所在的任何硬件上运行,又能针对该硬件进行优化,因此无需重新编译即可适应不断变化的硬件选择。客户可以选择在给定时间点提供最佳“性价比”(或每美元投入获得的工作效能)的实例类型,并随着这些指标随时间的变化和发展,在这些实例类型之间进行转换。

您的 JVM 选择如何影响云成本

或许不太明显的是用于在这些 vCPU 上运行的 JVM 的选择。这种选择还可以带来更快且更经济的计算基础设施。能够在同一 vCPU 上更快地执行相同应用程序代码的 JVM,[显然] 会以与更快、更经济的 vCPU 相同的方式降低成本。

如今,不同 JVM 之间的速度差距已超过连续两代 vCPU 之间以及不同 vCPU 架构之间的速度差距。这使得在控制或降低云计算成本方面,JVM 的选择与 CPU 的选择同等重要。

请注意,这并非二选一的抉择。选择单位成本 vCPU 速度更快的实例类型与更快的 JVM,两者带来的效益是互补且叠加的。事实上,更快的 JVM 通常也更擅长利用新 vCPU 实现额外性能提升,因为它们往往会针对新硬件的特性和功能进行优化。 

总之。在更快的 JVM 上运行应用程序,其效益与在更快的 vCPU 上运行同样显著。不这样做,就是一种浪费。 

云计算中的常见现实 

让我们看一下云环境中利用率和资源使用的常见示例。以下图表源自某实际客户生产应用程序大规模运行时的多日运行模式。该图表展示了 CPU 利用率随时间的变化 [图 1]。我们在许多客户环境中反复观察到此图表中的模式。 

图 1:来自某客户环境云计算支出的多日模式的 CPU 利用率。

该组织为此应用程序集群中配置的 CPU 支付了 100% 的费用,但实际仅 使用了其支付的计算容量的 10%-30%。剩余 70%-90% 的计算资源未使用,不产生任何工作价值。 

这种利用率水平并非偶然造成。通常,组织会出现这种情况是因为:当他们尝试提高利用率时,最终得到的服务水平无法满足其预期。这就引出了一个很自然的问题:从成本角度来看,我们可以做些什么来改进系统? 

如何利用提升的速度、一致性和弹性 

我将把 Azul JVM 对应用程序的三个改进点(速度、一致性和弹性)放在此图中,以展示改进后的效果。 

第一步:我们先从速度开始 

假设使用给定的 JVM 在“X”时间内完成一定量的工作需要“Y”单位的 CPU。另一个能更快执行相同应用逻辑的 JVM,可以随着时间的推移减少完成相同工作量所需的“Y”CPU 数量。如果我们使用相同数量的云 pod 承载同等工作量,但使用 Azul Zing JVM 代替“标准版”OpenJDK JVM,则每个 pod 将消耗更少的 CPU,因为 Azul Zing JVM 能更快地执行相同的应用程序代码 [视频]。

视频:来自某客户环境云计算支出的多日模式的 CPU 利用率。

现在,由于 Azul Zing JVM 的代码执行效率更高,我们可以在相同时间内完成相同工作量,但使用的 CPU 周期显著减少。此时,最明显且直接的做法就是关闭足够数量的 Kubernetes pod 或实例,使蓝线恢复到绿线之前所处的水平 [图 2]。 

图 2:关闭足够数量的实例,使蓝线恢复到绿线之前的位置。

如果需要,您可以小心逐步地执行操作:一旦您看到 CPU 利用率下降,就可以开始减少运行节点或 Pod 数量,比如从 10 个减少到 9 个、8 个、甚至 7 个,直到达到之前相同的 CPU 使用水平,同时确保服务水平不受影响。

如何进行公平的 CPU 消耗对比测试
有时候,人们很难接受这样一个现实,仅仅将 JDK 从 OpenJDK 更改为 Azul Platform Prime 就能对结果产生如此显著的影响。为了证明这一点,进行 50:50 的并行对比测试通常很有用。例如,如果您有一个包含 20 个 pod 的集群,且流量在所有 pod 之间均匀负载均衡,您可以在“标准版”OpenJDK 上运行其中一半 pod,在 Prime 上运行另一半。这样,您可以在承载相同负载的条件下,对两种 JVM 的 CPU 消耗量进行合理的直接对比。 
 
然而,许多环境中的负载均衡并不是“均匀”的。事实上,在许多基于服务网格的环境中,速度更快的 pod 最终会被分配更多的工作负载,因为它们能更快地清空请求队列。这意味着,像上面那种测试可能会导致 JDK 配置之间的负载不相等。为了解决不同 pod 承担不同工作量的问题,需要同时记录每一组(“一半 pod”)的 CPU 水平和吞吐量水平。有了这些信息,您就可以对比在合并负载下,两种不同 JVM 表现出的“单位工作量的 CPU 占比”。 

这(利用速度优势)只是通过 Zing JVM 节省成本的第一步(通常也是最快捷、最简单的一步)。如果您再次观察此图表的形状,会发现它与开始时的形状相同。它仍然存在多个可以解决的低效率问题。 

  • 确实,使用的 vCPU 总数已经减少由于代码执行速度提升,我们能够在保持相同 CPU 利用率的情况下,以更少的 vCPU 承载相同的工作负载。我们让我们付费的每个 vCPU 产生了更高的“价值”,但图表形状并未改变… 
  • 但峰值 CPU 利用率较低使在利用率高峰时段,我们看到已配置(且已付费)的 CPU 利用率也仅达到约 30%。换句话说,即使在利用率高峰期间,仍有超过三分之二的投入资金未被使用。 
  • CPU 利用率分布不均匀,平均值远低于峰值 – CPU 利用率在大约 10% 到 30% 之间波动。在非峰值负载阶段,利用率甚至更低,浪费率(已付费但未使用的 CPU 量)则更高。 

组织往往接受这样的结果是有原因的。他们通常已 [从惨痛教训中] 认识到,如果强行提高峰值利用率,系统就会出现问题。他们可能还发现,如果尝试在低负载和低利用率时段关闭资源,之后在负载增加时重新启用这些资源,同样会导致系统出现问题。他们的 SLA 指标将出现异常,或者用户会抱怨,因此他们不会将利用率提高或调整到会引发这些问题的水平。 

第二步:利用提升的一致性 

与“标准版”OpenJDK 相比,Zing JVM 经过专门设计,能够以更一致的性能执行应用程序代码(在给定负载级别下,出现卡顿、暂停、“临时减速”或停顿的现象会减少)。因此,在此类“不良情况”开始发生前,每个实例和 pod 通常可承受更高的 CPU 利用率,且影响服务水平的异常的出现频率会降低,避免系统行为恶化到不可接受的程度。 

利用这种提升后的一致性,降低成本的下一步是关闭更多的 pod,以便峰值 CPU 利用率提升至先前目标之上。即使将这一目标小幅提升,也能实现显著的成本节约。例如,通过减少 pod 数量将峰值 CPU 利用率从约 30% 提升至约 40%,总 pod 数量将减少约 25%,每个剩余 pod 比以前多承担 33% 的工作负载。

这正是 Azul 多年来在 JVM 一致性方面的投入真正所获得的回报,因为它使我们能够将这些 CPU 目标提升到高于当前水平,从而节省大量成本 [图 3]。 

图 3:此图表显示了两级改进的累积效益:第一级改进(从底部灰线到中间灰线)展示了速度提升带来的相对成本优化,但目标峰值利用率水平保持不变。第二级改进(从中间灰线到顶部蓝线)展示了通过提高目标峰值利用率水平而实现的成本降低和 pod 数量减少。
如何评估优化后的 JVM 可达到的利用率上限
要了解通过提高目标利用率水平可以节省多少成本,可通过逐步、谨慎地提升 CPU 利用率目标来进行试验。通常最好以小步提升,例如每次提升 5%。逐步提升利用率,并在每次这样的“提升”之后,评估相应的服务水平,如果您最终因提升过快,导致服务水平降至“标准版”JDK 的既定目标利用率水平以下,请回退到之前的稳定水平。虽然您想要评估通过改进的新 JDK 设置可以将利用率提升到多高水平,但在生产环境中,您仍需要在达到那个点之前停止。确定临界点后,需要在持久配置中预留一些“缓冲空间”(例如,比实际导致性能下降的目标水平低 5%-10%)。 

第三步:启用并提升弹性

即使已实现前两项改进(速度、一致性),如果利用率仍随时间大幅波动,则说明仍存在可以解决的显著低效问题。上图 4 仍显示明显的峰值和低谷,二者之间的利用率差异很大。在资源未达到目标峰值利用率水平的时段(例如此图表中的午夜时分),存在大量资源浪费。此时需要不到一半的资源。但即使利用率非常低,实例也不会被关闭。 

我们发现许多应用程序的利用率图表都呈这种形状。这种情况通常表明弹性和自动扩展未应用,或仅部分应用。例如,许多将自动扩展用作应对极端峰值保险措施的组织,会通过设置一个保守的最小 pod 数量(绝不低于该数值),来避免在“正常”情况下使用自动扩展。 

这种利用率变化通常是由于组织在资源不需要时不愿意将其关闭所导致的。在现代支持自动扩展的部署环境(例如 k8s)中,这种缺乏意愿通常并非源于关闭 pod 可能出现的问题(在低利用率时关闭通常无害),而是源于新实例重启时出现的不良表现。这种“预热问题”(即新 JVM 实例在启动初期会出现暂时性的性能缓慢或行为故障),会导致这些实例在完全预热并“全速运行”之前,处理客户端流量时的服务水平下降。这些行为通常表现为错误率增加、超时或响应时间异常。这些故障通常与负载无关,而是由于处理流量的实例处于“运行缓慢”或“尚未预热”状态所致。 

这正是基于“更优的 JVM”实现成本优化的第三步:使用 Azul 的 Prime JVM 运行应用程序时,可以完全启用弹性,而不会导致通常与“预热”和自动扩展相关的服务水平下降问题。 

在控制或降低云计算成本方面,JVM 的选择与 CPU 的选择同等重要。

“标准版”OpenJDK JVM 仅能通过处理实际流量实现有效预热,因为它们只有在观察代码被执行足够次数(处理足够的客户端流量)后才能做出优化选择,进而优化代码。实际上,每个新启动的 pod 都必须“损害”足够多的流量才能了解优化内容和优化方法,才能达到不再损害流量的程度。 

Azul 的 Platform Prime 产品通过结合专门为此目的构建的多项技术,显著改善了单个 pod 的预热行为。通过 Azul 的 ReadyNowOptimizer Hub 技术,JVM 实例集可以共享来之不易的预热经验,从而使新启动的 pod 能够“预加载”先前的知识,避免通过损害流量来“学习”优化内容和优化方法。Azul 的 Optimizer Hub 借助其云原生编译器功能,在整个实例集中高效执行代码优化,使得新启动的 pod 中的各个 JVM 无需在启动阶段各自执行繁重的优化工作。这些技术使新启动的 pod 甚至在接收流量前就已应用强大的优化,从而消除运行缓慢且尚未预热的 pod 对流量的影响。

Azul Optimizer Hub 会自动编排和分摊优化工作,使生产环境能够实现高效有机的自我优化。这些生产环境优化无需更改软件开发生命周期 (SDLC) 即可实现。当应用程序的新版本推出时,早期实例(通常为 Canary)会自动建立经验和优化成果,后续因推出和自动扩展而启动的 pod 便可利用这些成果。

这种面向实例集的优化功能直接解决了通常与“预热”和自动扩展相关的负面服务水平问题。它使得自动扩展对于大多数应用程序和服务来说既切实可行又能带来效益。 

随着自动扩展的障碍被清除,弹性得以全面启用并被广泛采用。此前回避自动扩展的应用程序现在可以启用该功能。仅采用保守自动扩展策略的应用程序(例如,仅通过设置较高的最小 pod 数量来应对罕见峰值),可以转向持续自动扩展 [图 4]。通过在低负载期间安全关闭 pod 以提高利用率,并在负载恢复时安全地重启这些 pod(不会对服务水平产生负面影响),可以消除低负载期间的利用率低谷。  

图 4:自动扩展和弹性可用来收回在低利用率期间浪费的支出。此图表展示了通过以上 3 个步骤所实现的累积节省:(a) 更快的代码执行(降低 CPU 需求和 pod 数量),(b) 更高的一致性(提高 [峰值] 利用率目标并减少 pod 数量),以及 (c) 实际应用弹性(减少低负载期间的支出浪费)。 
实际应用弹性带来的额外成本管理效益  
当弹性可以全面启用,且不会在“预热”期间对服务水平产生影响时,云实例使用和定价模型将出现新的使用方案(例如减少预留实例,更广泛地使用竞价型实例),这些方案在以前可能难以实际推行。这些可以降低 vCPU 小时的平均成本,从而进一步节省成本。

总结 

通过在更快速、更一致且能消除与预热相关的服务水平异常的 JVM 上运行应用程序和服务,可以显著降低计算成本。Azul 的 Zing JVM 和 Prime Platform 通过提升云计算支出的有效资金利用率,实现切实的成本节约 [参见图 5]。

图 5:整体累积效应:结合速度、一致性和预热行为改进这三方面的优势(在之前的图中分别展示),推动实现更高的支出资金利用率。
“如果您想降低成本,就专注于成本降低。” 
通常,在评估成本节约的潜力时,我们希望以最少投入快速实现成本降低。根据经验,我们发现在此过程中存在一些陷阱和干扰,而采取一些专注操作有助于开展我们称之为成本节约“试点”的工作。 
 
请务必记住,虽然 Azul Prime 提供的速度、一致性和弹性功能可用于节约成本,但这些功能也可用于其他目的。如果您的目标是降低成本,那么重要的是要避免仅仅因为其他好处听起来“很酷”,或者甚至因为它们在其他方面可能有用,而分心或沉迷于追求那些好处。例如,在相同的利用率水平下,当常见情况和异常延迟在早期测试中得到改善时,人们可能会开始关注这个好处,而失去对成本效益目标的关注。有时我们甚至必须提醒他们,如果我们旨在衡量成本效益,我们应该甘愿为提升利用率水平而牺牲延迟或响应时间方面的改进。这些指标(速度和一致性)的提升通常意味着“您应该进一步提升利用率,关闭更多的 pod”。 
 
即使在实现成本节省之后,且已将利用率提升到可接受的水平后,我们可能(并且通常确实)仍然会保留一些速度和一致性方面的好处(例如中位数或平均响应时间增加、异常幅度和出现频率显著降低)。但在降低成本的背景下,我们将这些视为“附加红利”。它们不是目标。 
 
如果您追求的是更低延迟、更少异常、更高业务“胜率”或“匹配度”,且成本降低不是您的主要目的,Azul Prime 同样是实现这些目标的出色解决方案。不过这是另一篇博客文章的主题。

关注我的博客系列

在我的下一篇博客文章中,我将重点讨论 JVM 对应用程序性能的影响。在我的第三篇文章中,我将讨论 Azul Optimizer Hub,它是 Azul Platform Prime 的一项功能。关注我,获取更多可用信息。

Teal CTA Band

Prime 在线演示