Use memory.low for Burstable pods, memory.min for Guaranteed only

This commit is contained in:
Sohan Kunkerkar
2026-03-17 17:46:19 -04:00
parent c859cb3e64
commit aa560bd2c0
5 changed files with 28 additions and 13 deletions

View File

@@ -42,6 +42,8 @@ const (
systemdSuffix string = ".slice"
// Cgroup2MemoryMin is memory.min for cgroup v2
Cgroup2MemoryMin string = "memory.min"
// Cgroup2MemoryLow is memory.low for cgroup v2
Cgroup2MemoryLow string = "memory.low"
// Cgroup2MemoryHigh is memory.high for cgroup v2
Cgroup2MemoryHigh string = "memory.high"
Cgroup2MaxCpuLimit string = "max"

View File

@@ -209,13 +209,19 @@ func ResourceConfigForPod(allocatedPod *v1.Pod, enforceCPULimits bool, cpuPeriod
result.HugePageLimit = hugePageLimits
if enforceMemoryQoS && memoryReservationPolicy == kubeletconfig.HardReservationMemoryReservationPolicy {
memoryMin := int64(0)
memoryRequest := int64(0)
if request, found := reqs[v1.ResourceMemory]; found {
memoryMin = request.Value()
memoryRequest = request.Value()
}
if memoryMin > 0 {
if memoryRequest > 0 {
// Guaranteed pods get memory.min (hard protection, kernel never reclaims).
// Burstable pods get memory.low (soft protection, kernel prefers not to reclaim).
cgroupKey := Cgroup2MemoryLow
if qosClass == v1.PodQOSGuaranteed {
cgroupKey = Cgroup2MemoryMin
}
result.Unified = map[string]string{
Cgroup2MemoryMin: strconv.FormatInt(memoryMin, 10),
cgroupKey: strconv.FormatInt(memoryRequest, 10),
}
}
}

View File

@@ -698,7 +698,7 @@ func TestResourceConfigForPodWithEnforceMemoryQoS(t *testing.T) {
},
enforceCPULimits: true,
quotaPeriod: defaultQuotaPeriod,
expected: &ResourceConfig{CPUShares: &burstableShares, Unified: map[string]string{"memory.min": "104857600"}},
expected: &ResourceConfig{CPUShares: &burstableShares, Unified: map[string]string{"memory.low": "104857600"}},
},
"burstable-with-limits": {
pod: &v1.Pod{
@@ -712,7 +712,7 @@ func TestResourceConfigForPodWithEnforceMemoryQoS(t *testing.T) {
},
enforceCPULimits: true,
quotaPeriod: defaultQuotaPeriod,
expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.min": "104857600"}},
expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.low": "104857600"}},
},
"burstable-with-limits-no-cpu-enforcement": {
pod: &v1.Pod{
@@ -726,7 +726,7 @@ func TestResourceConfigForPodWithEnforceMemoryQoS(t *testing.T) {
},
enforceCPULimits: false,
quotaPeriod: defaultQuotaPeriod,
expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.min": "104857600"}},
expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.low": "104857600"}},
},
"burstable-partial-limits": {
pod: &v1.Pod{
@@ -743,7 +743,7 @@ func TestResourceConfigForPodWithEnforceMemoryQoS(t *testing.T) {
},
enforceCPULimits: true,
quotaPeriod: defaultQuotaPeriod,
expected: &ResourceConfig{CPUShares: &burstablePartialShares, Unified: map[string]string{"memory.min": "209715200"}},
expected: &ResourceConfig{CPUShares: &burstablePartialShares, Unified: map[string]string{"memory.low": "209715200"}},
},
"burstable-with-limits-with-tuned-quota": {
pod: &v1.Pod{
@@ -757,7 +757,7 @@ func TestResourceConfigForPodWithEnforceMemoryQoS(t *testing.T) {
},
enforceCPULimits: true,
quotaPeriod: tunedQuotaPeriod,
expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.min": "104857600"}},
expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.low": "104857600"}},
},
"burstable-with-limits-no-cpu-enforcement-with-tuned-quota": {
pod: &v1.Pod{
@@ -771,7 +771,7 @@ func TestResourceConfigForPodWithEnforceMemoryQoS(t *testing.T) {
},
enforceCPULimits: false,
quotaPeriod: tunedQuotaPeriod,
expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.min": "104857600"}},
expected: &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.low": "104857600"}},
},
"burstable-partial-limits-with-tuned-quota": {
pod: &v1.Pod{
@@ -788,7 +788,7 @@ func TestResourceConfigForPodWithEnforceMemoryQoS(t *testing.T) {
},
enforceCPULimits: true,
quotaPeriod: tunedQuotaPeriod,
expected: &ResourceConfig{CPUShares: &burstablePartialShares, Unified: map[string]string{"memory.min": "209715200"}},
expected: &ResourceConfig{CPUShares: &burstablePartialShares, Unified: map[string]string{"memory.low": "209715200"}},
},
"guaranteed": {
pod: &v1.Pod{

View File

@@ -156,9 +156,16 @@ func (m *kubeGenericRuntimeManager) generateLinuxContainerResources(ctx context.
memoryRequest := container.Resources.Requests.Memory().Value()
memoryLimit := container.Resources.Limits.Memory().Value()
if memoryRequest != 0 && m.memoryReservationPolicy == kubeletconfiginternal.HardReservationMemoryReservationPolicy {
unified[cm.Cgroup2MemoryMin] = strconv.FormatInt(memoryRequest, 10)
// Guaranteed pods get memory.min (hard protection).
// Burstable pods get memory.low (soft protection).
if kubeapiqos.GetPodQOS(pod) == v1.PodQOSGuaranteed {
unified[cm.Cgroup2MemoryMin] = strconv.FormatInt(memoryRequest, 10)
} else {
unified[cm.Cgroup2MemoryLow] = strconv.FormatInt(memoryRequest, 10)
}
} else {
unified[cm.Cgroup2MemoryMin] = "0"
unified[cm.Cgroup2MemoryLow] = "0"
}
// Guaranteed pods by their QoS definition requires that memory request equals memory limit and cpu request must equal cpu limit.

View File

@@ -658,7 +658,7 @@ func TestGenerateContainerConfigWithMemoryQoSEnforced(t *testing.T) {
linuxConfig, err := m.generateLinuxContainerConfig(tCtx, &test.pod.Spec.Containers[0], test.pod, new(int64), "", nil, true)
assert.NoError(t, err)
assert.Equal(t, test.expected.containerConfig, linuxConfig, test.name)
assert.Equal(t, linuxConfig.GetResources().GetUnified()["memory.min"], strconv.FormatInt(test.expected.memoryLow, 10), test.name)
assert.Equal(t, linuxConfig.GetResources().GetUnified()["memory.low"], strconv.FormatInt(test.expected.memoryLow, 10), test.name)
assert.Equal(t, linuxConfig.GetResources().GetUnified()["memory.high"], strconv.FormatInt(test.expected.memoryHigh, 10), test.name)
}
}