首页 > 解决方案 > 基于 JVM 堆内存的 Kubernetes HPA

问题描述

我在 Kubernetes 集群上运行了一个 openjdk:8 映像。我添加了内存 HPA(Horizo​​ntal Pod Autoscaling),它可以很好地扩展,但由于 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 处理这个问题?就像不检查操作系统的内存使用情况一样,我们可以不只检查堆中的内存使用情况并在其上进行扩展吗?

标签: kubernetesgarbage-collectionjvm

解决方案


在 Kubernetes 中扩展 Java 应用程序有点棘手。HPA 只查看系统内存,正如所指出的,JVM 通常不会释放提交的堆空间(至少不会立即释放)。

有两种主要方法可以解决这个问题

1.调整JVM参数,使提交的堆更接近使用的堆

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

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

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

Patrick Dillon 撰写了一篇由 RedHat 发表的文章,名为Scaling Java containers,深入探讨了这个主题。

2.自定义缩放逻辑

您可以创建自己的扩展逻辑并将其作为定期运行的作业部署到 Kubernetes 中,而不是使用 HPA:

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

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

这方面的一个例子可以在文章Autoscaling based on CPU/Memory in Kubernetes - Part II by powercloudup中找到


推荐阅读