From 0deef4610e8e62690136e98674d1bf0e8c85dffa Mon Sep 17 00:00:00 2001 From: Elana Hashman Date: Wed, 16 Jun 2021 16:27:50 -0700 Subject: [PATCH] Set MemorySwapLimitInBytes for CRI when NodeSwapEnabled --- pkg/kubelet/kubelet.go | 1 + .../kuberuntime_container_linux.go | 14 +++ .../kuberuntime_container_linux_test.go | 89 +++++++++++++++++++ .../kuberuntime/kuberuntime_manager.go | 5 ++ 4 files changed, 109 insertions(+) diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index ebf2fbf5e2f..43cad89dba0 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -652,6 +652,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration, klet.containerLogManager, klet.runtimeClassManager, seccompDefault, + kubeCfg.MemorySwap.SwapBehavior, ) if err != nil { return nil, err diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go b/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go index 85ad47772e8..c65d6b22946 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go @@ -89,6 +89,20 @@ func (m *kubeGenericRuntimeManager) generateLinuxContainerConfig(container *v1.C lc.Resources.HugepageLimits = GetHugepageLimitsFromResources(container.Resources) + if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.NodeSwapEnabled) { + // NOTE(ehashman): Behaviour is defined in the opencontainers runtime spec: + // https://github.com/opencontainers/runtime-spec/blob/1c3f411f041711bbeecf35ff7e93461ea6789220/config-linux.md#memory + switch m.memorySwapBehavior { + case "UnlimitedSwap": + // -1 = unlimited swap + lc.Resources.MemorySwapLimitInBytes = -1 + default: + // memorySwapLimit = total permitted memory+swap; if equal to memory limit, => 0 swap + // Note that if memory limit is 0, memory swap limit is ignored. + lc.Resources.MemorySwapLimitInBytes = lc.Resources.MemoryLimitInBytes + } + } + return lc } diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go b/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go index 98203fef23e..2028c951453 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go @@ -367,3 +367,92 @@ func TestGenerateLinuxContainerConfigNamespaces(t *testing.T) { }) } } + +func TestGenerateLinuxContainerConfigSwap(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeSwapEnabled, true)() + _, _, m, err := createTestRuntimeManager() + if err != nil { + t.Fatalf("error creating test RuntimeManager: %v", err) + } + m.machineInfo.MemoryCapacity = 1000000 + containerName := "test" + + for _, tc := range []struct { + name string + swapSetting string + pod *v1.Pod + expected int64 + }{ + { + name: "config unset, memory limit set", + // no swap setting + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Containers: []v1.Container{{ + Name: containerName, + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "memory": resource.MustParse("1000"), + }, + Requests: v1.ResourceList{ + "memory": resource.MustParse("1000"), + }, + }, + }}, + }, + }, + expected: 1000, + }, + { + name: "config unset, no memory limit", + // no swap setting + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Containers: []v1.Container{ + {Name: containerName}, + }, + }, + }, + expected: 0, + }, + { + // Note: behaviour will be the same as previous two cases + name: "config set to NoSwap, memory limit set", + swapSetting: "NoSwap", + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Containers: []v1.Container{{ + Name: containerName, + Resources: v1.ResourceRequirements{ + Limits: v1.ResourceList{ + "memory": resource.MustParse("1000"), + }, + Requests: v1.ResourceList{ + "memory": resource.MustParse("1000"), + }, + }, + }}, + }, + }, + expected: 1000, + }, + { + name: "UnlimitedSwap enabled", + swapSetting: "UnlimitedSwap", + pod: &v1.Pod{ + Spec: v1.PodSpec{ + Containers: []v1.Container{ + {Name: containerName}, + }, + }, + }, + expected: -1, + }, + } { + t.Run(tc.name, func(t *testing.T) { + m.memorySwapBehavior = tc.swapSetting + actual := m.generateLinuxContainerConfig(&tc.pod.Spec.Containers[0], tc.pod, nil, "", nil) + assert.Equal(t, tc.expected, actual.Resources.MemorySwapLimitInBytes, "memory swap config for %s", tc.name) + }) + } +} diff --git a/pkg/kubelet/kuberuntime/kuberuntime_manager.go b/pkg/kubelet/kuberuntime/kuberuntime_manager.go index c9bf6a847ee..87bf70b031e 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_manager.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_manager.go @@ -144,6 +144,9 @@ type kubeGenericRuntimeManager struct { // Use RuntimeDefault as the default seccomp profile for all workloads. seccompDefault bool + + // MemorySwapBehavior defines how swap is used + memorySwapBehavior string } // KubeGenericRuntime is a interface contains interfaces for container runtime and command. @@ -186,6 +189,7 @@ func NewKubeGenericRuntimeManager( logManager logs.ContainerLogManager, runtimeClassManager *runtimeclass.Manager, seccompDefault bool, + memorySwapBehavior string, ) (KubeGenericRuntime, error) { kubeRuntimeManager := &kubeGenericRuntimeManager{ recorder: recorder, @@ -206,6 +210,7 @@ func NewKubeGenericRuntimeManager( runtimeClassManager: runtimeClassManager, logReduction: logreduction.NewLogReduction(identicalErrorDelay), seccompDefault: seccompDefault, + memorySwapBehavior: memorySwapBehavior, } typedVersion, err := kubeRuntimeManager.getTypedVersion()