diff --git a/pkg/kubelet/cm/cgroup_manager_linux.go b/pkg/kubelet/cm/cgroup_manager_linux.go index 81994640038..1e2579ea6cf 100644 --- a/pkg/kubelet/cm/cgroup_manager_linux.go +++ b/pkg/kubelet/cm/cgroup_manager_linux.go @@ -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" diff --git a/pkg/kubelet/cm/helpers_linux.go b/pkg/kubelet/cm/helpers_linux.go index 6eff274b497..70f5beaf9cd 100644 --- a/pkg/kubelet/cm/helpers_linux.go +++ b/pkg/kubelet/cm/helpers_linux.go @@ -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), } } } diff --git a/pkg/kubelet/cm/helpers_linux_test.go b/pkg/kubelet/cm/helpers_linux_test.go index f6378fd5d04..aaf51239cfa 100644 --- a/pkg/kubelet/cm/helpers_linux_test.go +++ b/pkg/kubelet/cm/helpers_linux_test.go @@ -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{ diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go b/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go index 6bc4544f84e..167e6a2cc97 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go @@ -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. diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go b/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go index 419085e3547..6b0115ee008 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go @@ -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) } }