cri: add support for configuring swap
Signed-off-by: Danielle Lancashire <dani@builds.terrible.systems>
This commit is contained in:
parent
591d7097e7
commit
2fa4e9c0e2
@ -20,9 +20,14 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containerd/cgroups"
|
"github.com/containerd/cgroups"
|
||||||
|
cgroupsv2 "github.com/containerd/cgroups/v2"
|
||||||
|
"github.com/containerd/containerd"
|
||||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -39,7 +44,164 @@ func checkMemoryLimit(t *testing.T, spec *runtimespec.Spec, memLimit int64) {
|
|||||||
assert.Equal(t, memLimit, *spec.Linux.Resources.Memory.Limit)
|
assert.Equal(t, memLimit, *spec.Linux.Resources.Memory.Limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateContainerResources(t *testing.T) {
|
func checkMemorySwapLimit(t *testing.T, spec *runtimespec.Spec, memLimit *int64) {
|
||||||
|
require.NotNil(t, spec)
|
||||||
|
require.NotNil(t, spec.Linux)
|
||||||
|
require.NotNil(t, spec.Linux.Resources)
|
||||||
|
require.NotNil(t, spec.Linux.Resources.Memory)
|
||||||
|
if memLimit == nil {
|
||||||
|
require.Nil(t, spec.Linux.Resources.Memory.Swap)
|
||||||
|
} else {
|
||||||
|
require.NotNil(t, spec.Linux.Resources.Memory.Swap)
|
||||||
|
assert.Equal(t, *memLimit, *spec.Linux.Resources.Memory.Swap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCgroupSwapLimitForTask(t *testing.T, task containerd.Task) uint64 {
|
||||||
|
if cgroups.Mode() == cgroups.Unified {
|
||||||
|
groupPath, err := cgroupsv2.PidGroupPath(int(task.Pid()))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
cgroup2, err := cgroupsv2.LoadManager("/sys/fs/cgroup", groupPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
stat, err := cgroup2.Stat()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return stat.Memory.SwapLimit
|
||||||
|
}
|
||||||
|
cgroup, err := cgroups.Load(cgroups.V1, cgroups.PidPath(int(task.Pid())))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
stat, err := cgroup.Stat(cgroups.IgnoreNotExist)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return stat.Memory.HierarchicalSwapLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCgroupMemoryLimitForTask(t *testing.T, task containerd.Task) uint64 {
|
||||||
|
if cgroups.Mode() == cgroups.Unified {
|
||||||
|
groupPath, err := cgroupsv2.PidGroupPath(int(task.Pid()))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
cgroup2, err := cgroupsv2.LoadManager("/sys/fs/cgroup", groupPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
stat, err := cgroup2.Stat()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return stat.Memory.UsageLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
cgroup, err := cgroups.Load(cgroups.V1, cgroups.PidPath(int(task.Pid())))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
stat, err := cgroup.Stat(cgroups.IgnoreNotExist)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return stat.Memory.Usage.Limit
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSwapLikelyEnabled() bool {
|
||||||
|
// Check whether swap is enabled.
|
||||||
|
swapFile := "/proc/swaps"
|
||||||
|
swapData, err := ioutil.ReadFile(swapFile)
|
||||||
|
if err != nil {
|
||||||
|
// We can't read the file or it doesn't exist, assume we don't have swap.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
swapData = bytes.TrimSpace(swapData) // extra trailing \n
|
||||||
|
swapLines := strings.Split(string(swapData), "\n")
|
||||||
|
// If there is more than one line (table headers) in /proc/swaps, swap is enabled
|
||||||
|
return len(swapLines) > 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateContainerResources_MemorySwap(t *testing.T) {
|
||||||
|
if !isSwapLikelyEnabled() {
|
||||||
|
t.Skipf("Swap is not enabled, or /proc/swaps is not readable. Swap is required for this test")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("Create a sandbox")
|
||||||
|
sb, sbConfig := PodSandboxConfigWithCleanup(t, "sandbox", "update-container-swap-resources")
|
||||||
|
|
||||||
|
EnsureImageExists(t, pauseImage)
|
||||||
|
|
||||||
|
memoryLimit := int64(128 * 1024 * 1024)
|
||||||
|
baseSwapLimit := int64(200 * 1024 * 1024)
|
||||||
|
increasedSwapLimit := int64(256 * 1024 * 1024)
|
||||||
|
|
||||||
|
t.Log("Create a container with memory limit but no swap")
|
||||||
|
cnConfig := ContainerConfig(
|
||||||
|
"container",
|
||||||
|
pauseImage,
|
||||||
|
WithResources(&runtime.LinuxContainerResources{
|
||||||
|
MemoryLimitInBytes: memoryLimit,
|
||||||
|
MemorySwapLimitInBytes: baseSwapLimit,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Log("Check memory limit in container OCI spec")
|
||||||
|
container, err := containerdClient.LoadContainer(context.Background(), cn)
|
||||||
|
require.NoError(t, err)
|
||||||
|
spec, err := container.Spec(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkMemoryLimit(t, spec, memoryLimit)
|
||||||
|
checkMemorySwapLimit(t, spec, &baseSwapLimit)
|
||||||
|
|
||||||
|
t.Log("Update container swap limit after created")
|
||||||
|
err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
|
||||||
|
MemorySwapLimitInBytes: baseSwapLimit,
|
||||||
|
}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Log("Check memory limit in container OCI spec")
|
||||||
|
spec, err = container.Spec(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
sw1 := baseSwapLimit
|
||||||
|
checkMemorySwapLimit(t, spec, &sw1)
|
||||||
|
|
||||||
|
t.Log("Start the container")
|
||||||
|
require.NoError(t, runtimeService.StartContainer(cn))
|
||||||
|
task, err := container.Task(context.Background(), nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Log("Check memory limit in cgroup")
|
||||||
|
memLimit := getCgroupMemoryLimitForTask(t, task)
|
||||||
|
swapLimit := getCgroupSwapLimitForTask(t, task)
|
||||||
|
assert.Equal(t, uint64(memoryLimit), memLimit)
|
||||||
|
assert.Equal(t, uint64(baseSwapLimit-memoryLimit), swapLimit)
|
||||||
|
|
||||||
|
t.Log("Update container memory limit after started")
|
||||||
|
err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
|
||||||
|
MemorySwapLimitInBytes: increasedSwapLimit,
|
||||||
|
}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Log("Check memory limit in container OCI spec")
|
||||||
|
spec, err = container.Spec(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkMemorySwapLimit(t, spec, &increasedSwapLimit)
|
||||||
|
|
||||||
|
t.Log("Check memory limit in cgroup")
|
||||||
|
swapLimit = getCgroupSwapLimitForTask(t, task)
|
||||||
|
assert.Equal(t, uint64(increasedSwapLimit-memoryLimit), swapLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateContainerResources_MemoryLimit(t *testing.T) {
|
||||||
// TODO(claudiub): Make this test work once https://github.com/microsoft/hcsshim/pull/931 merges.
|
// TODO(claudiub): Make this test work once https://github.com/microsoft/hcsshim/pull/931 merges.
|
||||||
t.Log("Create a sandbox")
|
t.Log("Create a sandbox")
|
||||||
sb, sbConfig := PodSandboxConfigWithCleanup(t, "sandbox", "update-container-resources")
|
sb, sbConfig := PodSandboxConfigWithCleanup(t, "sandbox", "update-container-resources")
|
||||||
|
@ -450,6 +450,7 @@ func WithResources(resources *runtime.LinuxContainerResources, tolerateMissingHu
|
|||||||
q = resources.GetCpuQuota()
|
q = resources.GetCpuQuota()
|
||||||
shares = uint64(resources.GetCpuShares())
|
shares = uint64(resources.GetCpuShares())
|
||||||
limit = resources.GetMemoryLimitInBytes()
|
limit = resources.GetMemoryLimitInBytes()
|
||||||
|
swapLimit = resources.GetMemorySwapLimitInBytes()
|
||||||
hugepages = resources.GetHugepageLimits()
|
hugepages = resources.GetHugepageLimits()
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -471,6 +472,10 @@ func WithResources(resources *runtime.LinuxContainerResources, tolerateMissingHu
|
|||||||
if limit != 0 {
|
if limit != 0 {
|
||||||
s.Linux.Resources.Memory.Limit = &limit
|
s.Linux.Resources.Memory.Limit = &limit
|
||||||
}
|
}
|
||||||
|
if swapLimit != 0 {
|
||||||
|
s.Linux.Resources.Memory.Swap = &swapLimit
|
||||||
|
}
|
||||||
|
|
||||||
if !disableHugetlbController {
|
if !disableHugetlbController {
|
||||||
if isHugetlbControllerPresent() {
|
if isHugetlbControllerPresent() {
|
||||||
for _, limit := range hugepages {
|
for _, limit := range hugepages {
|
||||||
|
Loading…
Reference in New Issue
Block a user