👉️URL: https://www.openshift.com/blog/scaling-java-containers 📝Description: Scaling Java Containers
随着企业越来越多地了解到部署容器化应用程序的优点,有必要纠正 JVM 在云中表现不好的误解,尤其是在内存管理方面。虽然许多JVM可能不能完美地配置成在弹性云环境中运行,但各种可用的系统属性允许对JVM进行调优,以帮助最大限度地利用其主机环境。如果一个容器化的应用程序是使用OpenShift部署的,那么该应用程序可以利用Kubernetes Vertical Pod Autoscaler (VPA),这是一个alpha特性。VPA就是一个例子,JVM的默认内存管理设置可能会降低在云中运行应用程序的好处。这篇博文将介绍配置和测试一个与VPA一起使用的容器化Java应用程序的步骤,这将演示JVM在云中运行时的适应性。
垂直缩放是增加或缩小可用于特定应用程序实例的资源的能力,这是在云中运行应用程序的优势之一。随着负载的增加,可以为容器分配更多的内存或CPU资源,并且可以在空闲时将其收缩以减少浪费。根据内存扩展Pod时,自动缩放器将根据Pod的内存使用量是否超过阈值提出建议。在Java应用程序中使用此功能可能会具有挑战性,因为VPA消耗的指标仅反映JVM的已提交总堆内存,而不是应用程序使用的内存量。
如果JVM不将未使用的内存释放回主机,则VPA仅考虑总堆大小的事实可能会成为一个问题。例如,如果应用程序内存使用量大幅增加,堆将扩展以容纳该内存,但此后可能不会收缩,以避免将来分配内存。在专用服务器上这样做会很好,因为它有助于最大程度地提高性能,但是在多租户弹性云环境中,一个容器使用的资源会以另一容器可用的资源为代价。因此,任何未被应用有效利用的内存消耗都可能浪费资源和金钱。
为了帮助使提交的堆大小更接近于应用程序的内存使用情况,您需要配置几个JVM系统属性,其中之一是MaxHeapFreeRatio。MaxHeapFreeRatio指示在JVM开始缩小堆生成量以达到目标之前,应释放的最大堆比例。
例如,在默认设置下,MaxHeapFreeRatio=70
,使用的堆大小为300MB,在需要减小JVM大小之前,总堆可能高达1GB。减小MaxHeapFreeRatio可以迫使JVM更积极地减小堆大小。通过调低该值,进程使用的内存量应更接近反映实际应用程序使用的内存。
△ Fig 1. MaxHeapFreeRatio=70
△ Fig 2. MaxHeapFreeRatio=40
从理论上讲,MaxHeapFreeRatio听起来不错,但测试应有助于表明JVM是否会遵循此配置,并在使用高峰后减少堆。对于此测试,除了MaxHeapFreeRatio之外,还有一些其他系统属性使堆可以缩小。Xms不仅是初始堆大小,而且还是最小堆大小,因此可以这样说,将Xms设置为32m应该可以防止Xms充当假地板。其他设置包括GCTimeRatio=4
(该值允许JVM花更多时间(最多20%)执行垃圾收集)和AdaptiveSizePolicyWeight=90
这应该告诉JVM在确定新的堆大小时,将当前gc的权重高于以前的gc。有关这些系统属性的更多细节,请查看这篇博文末尾的链接。Xmx最大堆设置为1.5 GB。大堆大小允许具有高吞吐量(或每秒有大量事务)的测试中的JVM拥有足够大的年轻代,以便在垃圾收集器无法足够快地清理时避免对象被提升。在调整大小测试中,MaxHeapFreeRatio被设置为40。
测试是在Wildfly 13服务器应用程序上执行的,该应用程序分配了20个随机对象,平均组合大小为4MB。服务器的负载由Apache JMeter驱动。流量模式由两个峰值组成,两个峰值之间有一个空闲时间。对于高峰时段,每秒添加一个线程,直到总数达到120,然后所有线程又持续一分钟。在空闲期间,两个线程运行了五分钟。在所有情况下,线程都会连续调用服务器。
为了演示MaxHeapFreeRatio,除了垃圾收集日志中的已用堆和已提交堆之外,还必须使用第三个度量。可以从使用的堆中计算出最大可能的堆值。然后查看提交的堆是否落在预期范围内很有趣。对于该测试,结果以视觉方式呈现。最大堆范围由用过的堆延伸的条形图表示,用橙色圆点表示。如果用蓝色圆点表示的已提交堆落在此范围内,则已根据MaxHeapFreeRatio调整了JVM的大小。
这是一个放大视图,以说明已使用的堆大小(橙色点),目标堆范围(橙色错误栏)和实际堆大小:
△ 调整测试结果图大小的详细信息。突出显示的示例的使用堆大小为377 MB,已提交堆大小为459MB。橙色框表示基于MaxHeapFreeRatio=40
的最大堆大小为628 MB。
总体趋势表明,已提交的堆的大小将根据使用的堆进行减小:
△ 调整大小的测试结果表明,总堆大小符合MaxFreeHeapRatio并跟踪应用程序的已用堆。
这两个峰值时段和空闲时段都是可见的,这表明堆正在根据应用程序的实际内存使用量进行调整。请注意,提交的堆通常遵循MaxHeapFreeRatio,但并非总是如此。这不是问题-文档承认MaxHeapFreeRatio是目标,而不是严格的限制。此外,一般的行为将允许此系统属性启用自动缩放。如果没有设置这些系统属性,则提交的堆(蓝色标记)在空闲期间不会丢失。尽管JVM内的应用程序使用率有所下降,但JVM基本上会从主机消耗稳定的内存量。
既然我们已经知道可以使用MaxHeapFreeRatio来强制堆对应于应用程序的内存使用情况,那么我们需要了解这些设置对应用程序性能的影响。我为内存密集型和CPU密集型应用程序创建了测试。两项测试均在同一Wildfly 13服务器应用程序上进行,测试之间的差异由参数控制。在内存测试中,每个请求平均分配4MB的内存,而CPU密集型测试平均分配200KB的内存,并运行一个微不足道的计算循环,即对数字进行加减运算,平均为100毫秒。在这两个测试中,通过在服务器达到2秒以上的响应时间之前找到最大吞吐量来获得结果。
除使用MaxHeapFreeRatio声明为变量外,这些测试均以与先前测试相同的系统属性运行。我还进行了一个参照组测试,其中Xmx和Xms设置为相等,以防止调整堆大小。
MaxHeapFreeRatio | TPS | 已用MB | 已提交MB | 空闲MB | % 空闲 | Free MB/Trans |
---|---|---|---|---|---|---|
25 | 188 | 283 | 383 | 100 | 26% | .53 |
30 | 250 | 291 | 390 | 99 | 26% | .40 |
40 | 295 | 320 | 472 | 152 | 32% | .52 |
40 (xmx=xms) | 310 | 331 | 542 | 211 | 39% | .68 |
70 | 525 | 538 | 1038 | 500 | 48% | .95 |
从每秒事务的线性增加来看,我们可以看到更高的MaxHeapFreeRatio允许更大的吞吐量。但是,由于JVM维护了更多的可用内存以提高效率,因此吞吐量付出了代价。右列演示了每笔事务的可用内存成本。对于我们的示例应用程序,最有效点似乎是MaxHeapFreeRatio为30,因为该配置设法以最少的内存浪费执行最多的事务。
在弹性云环境中考虑这些成本很重要,因为您可以通过水平扩展而不是增加MaxHeapFreeRatio来处理应用程序上的额外负载。当然,这些结果适用于我们的特定测试应用程序,但是希望该实践可以帮助您调整云应用程序。
▽ 图5和图6.吞吐量分别为40和70的MaxHeapFreeRatio。结果表明两者之间没有显着差异,因为这两种设置大约每秒都能完成30个事务。
△ 图5和图6
MaxHeapFreeRatio分别为40和70的吞吐量。结果表明两者之间没有显着差异,因为这两种设置大约每秒都能完成30个事务。
对于具有一定程度波动负载的任何应用程序,扩展应用程序以满足需求是一个相当大的问题。自动缩放是使用容器解决方案(例如Red Hat OpenShift)部署应用程序的主要好处。随着越来越多的企业看到以这种方式部署应用程序的好处,了解Java应用程序将能够充分利用诸如Kubernetes Vertical Pod Autoscaler之类的功能非常重要。每个Java版本似乎都有针对云量身定制的新功能,但是没有必要升级或等待新版本开始配置JVM并获得Red Hat OpenShift的好处。