在 Kubernetes 中,像 CPU 这样的资源被称作"可压缩资源"(compressible resources)。它的典型特点是,当可压缩资源不足时,Pod 只会"饥饿",但不会退出。而像内存这样的资源,则被称作"不可压缩资源"(incompressible resources)。当不可压缩资源不足时,Pod 就会因为 OOM(Out-Of-Memory)被内核杀掉。

1. 容器最小内存和最大内存设置为一致

简单来理解:最小内存等同于 k8s 的 resources.requests 资源,最大内存等同于 resources.limits 资源。参考:为容器和 Pod 分配内存资源 | Kubernetes

上述配置中,查看对应的 YAML 文件可以看到,对应的 memory 的请求和限制保持一致。

一般情况下,对于核心资源,我们推荐 requests == limits,这是为什么呢?

Pod 的三种 QoS 类别

  • Guaranteed:当 Pod 里的每一个 Container 都同时设置了 requestslimits,并且 requestslimits 值相等时
  • Burstable:当 Pod 不满足 Guaranteed 的条件,但至少有一个 Container 设置了 requests
  • BestEffort:Pod 既没有设置 requests,也没有设置 limits

资源不足时的驱逐顺序

当资源不足需要强制剔除部分 Pod 时,kubelet 会按照以下顺序挑选 Pod 进行删除操作:

  1. 首当其冲的,自然是 BestEffort 类别的 Pod
  2. 其次,是属于 Burstable 类别、并且发生"饥饿"的资源使用量已经超出了 requests 的 Pod
  3. 最后,才是 Guaranteed 类别。并且,Kubernetes 会保证只有当 Guaranteed 类别的 Pod 的资源使用量超过了其 limits 的限制,或者宿主机本身正处于 Memory Pressure 状态时,Guaranteed 的 Pod 才可能被选中进行 Eviction 操作

所以,一般我们设置 requestlimit 一致可以保证资源比较稳定。

2. 最大堆内存和最小堆内存设置为一致

当堆内存使用发生变化时,并不是简单地增加和减少堆内存即可,还会进行 GC 操作。如果 -Xms 起始值设置得比较小,就会进行频繁的 GC 操作,当 GC 操作无法释放更多的内存时,才会进行内存的扩充。在这期间,GC 操作需要耗时,且 Full GC 还会引起线程暂停,导致性能问题。

因为是容器化部署,一个容器就部署了一个节点(服务实例),所以独占资源就没有必要动态调整内存。

设置相同值的利弊

优点

  • 最大值和最小值设置为一样,可以减轻伸缩堆大小带来的压力
  • GC 次数会减少

缺点

  • 在 JVM 使用完之前,内存会一直增长
  • 对于不同的 JVM 实现,效果不同:
    • 对于 HotSpot 虚拟机,-Xms-Xmx 设置为一样可以减轻伸缩堆大小带来的压力
    • 对于 IBM 虚拟机,设置为一样会增大堆碎片产生的几率,并且这种负面影响足以抵消前者产生的益处

另外,同一 JDK 的 GC 策略也有很多种,不能一概而论。