基于JVM堆内存的Kubernetes HPA

3

我在Kubernetes集群上运行了一个openjdk:8镜像。我添加了内存HPA(水平Pod自动缩放),它可以正常扩展,但由于JVM不会将堆中的内存释放回操作系统,因此Pod不会缩小。以下是hpa.yaml:

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: image-server
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: image-server
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 60
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 60

解决这个问题的一种方法是使用正确的GC并释放内存,但由于JVM出于性能原因而被设计为不经常从堆中释放,因此这样做并不是一个好主意。是否有一种方法可以从Kubernetes处理这个问题?例如,我们可以不检查操作系统的内存使用情况,而是直接检查堆的内存使用情况并进行缩放?


你说JVM通常不会归还内存是正确的,但这也不完全正确。看看这里和这里(如果你正在使用G1)。 - Eugene
@Eugene 我正在使用默认的ParallelGC的openjdk 8。JVM为了性能原因而保留内存,以避免分配和释放周期,通过使用不同的GC或配置它更频繁地释放内存可能会增加该周期。由于我们只在此pod上运行一个Java进程,操作系统不需要它用于其他任何事情,即使Java保留它也没问题。我正在寻找一种基于堆使用情况而不是操作系统报告的内存使用情况来执行HPA的方法。 - Manoj Suthar
你无法马上释放内存,但你可以使用不同的GC来更快或完全释放内存,从而几乎达到你想要的效果。过去认为这样做会影响性能,但现在这种说法已经是错误的了。每个并发GC,例如Shenandoah和ZGC(正如链接所显示的),都可以实现内存释放。 - Eugene
1个回答

8

Kubernetes中扩展Java应用程序有一点棘手。HPA仅查看系统内存,正如指出的那样,JVM通常不会立即释放已提交的堆空间。

解决这个问题可以采取两种主要方法:

1.调整JVM参数以更紧密地跟随已使用堆的提交堆

根据使用的JVM和GC的不同,调整选项可能略有不同,但最重要的选项是:

  • MaxHeapFreeRatio - 允许未使用多少提交堆
  • GCTimeRatio - GC允许运行的频率(影响性能)
  • AdaptiveSizePolicyWeight - 在计算新堆时如何权衡较旧与较新的GC运行

为这些选项提供确切的值并不容易,这是在释放内存快速和应用程序性能之间的折衷。最佳设置将取决于应用程序的负载特征。

Patrick Dillon撰写了一篇由RedHat发布的文章Scaling Java containers对此进行了深入探讨。

2.自定义扩展逻辑

您可以创建自己的扩展逻辑并将其部署到Kubernetes中作为定期运行的作业,以执行以下操作:

  1. 检查所有Pod中的堆使用情况(例如,在Pod内部运行jstat)
  2. 如果达到最大阈值,则扩展新的Pod
  3. 如果达到最小阈值,则缩小Pod

这种方法具有查看实际堆使用情况的好处,但需要自定义组件。

有关示例,请参见Powercloudup的文章Autoscaling based on CPU/Memory in Kubernetes — Part II.


你可以像我们一样选择一个合适的垃圾回收器开始。 - Eugene

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接