From f02381660819139839b07d900f432762fd5a3658 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Fri, 11 May 2018 19:46:11 +0000 Subject: [PATCH 1/2] Add memory.kernelTCP support for linux This fix tries to address the issue raised in 37038 where there were no memory.kernelTCP support for linux. This fix add MemoryKernelTCP to HostConfig, and pass the config to runtime-spec. Additional test case has been added. This fix fixes 37038. Signed-off-by: Yong Tang --- api/swagger.yaml | 4 ++ api/types/container/host_config.go | 1 + api/types/types.go | 1 + daemon/daemon_unix.go | 4 ++ daemon/info_unix.go | 1 + docs/api/version-history.md | 1 + integration/container/run_linux_test.go | 49 +++++++++++++++++++++++++ pkg/sysinfo/sysinfo.go | 3 ++ pkg/sysinfo/sysinfo_linux.go | 5 +++ 9 files changed, 69 insertions(+) create mode 100644 integration/container/run_linux_test.go diff --git a/api/swagger.yaml b/api/swagger.yaml index 49d3e0a83b..aa6fa2ca7e 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -429,6 +429,10 @@ definitions: description: "Kernel memory limit in bytes." type: "integer" format: "int64" + KernelMemoryTCP: + description: "Sets hard limit for kernel TCP buffer memory." + type: "integer" + format: "int64" MemoryReservation: description: "Memory soft limit in bytes." type: "integer" diff --git a/api/types/container/host_config.go b/api/types/container/host_config.go index 4ef26fa6c8..de48975fdb 100644 --- a/api/types/container/host_config.go +++ b/api/types/container/host_config.go @@ -329,6 +329,7 @@ type Resources struct { DeviceCgroupRules []string // List of rule to be added to the device cgroup DiskQuota int64 // Disk limit (in bytes) KernelMemory int64 // Kernel memory limit (in bytes) + KernelMemoryTCP int64 // Sets hard limit for kernel TCP buffer memory MemoryReservation int64 // Memory soft limit (in bytes) MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap MemorySwappiness *int64 // Tuning container memory swappiness behaviour diff --git a/api/types/types.go b/api/types/types.go index a8fae3ba32..2accda9d07 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -158,6 +158,7 @@ type Info struct { MemoryLimit bool SwapLimit bool KernelMemory bool + KernelMemoryTCP bool CPUCfsPeriod bool `json:"CpuCfsPeriod"` CPUCfsQuota bool `json:"CpuCfsQuota"` CPUShares bool diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index b69eede21c..b18fca17c7 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -111,6 +111,10 @@ func getMemoryResources(config containertypes.Resources) *specs.LinuxMemory { memory.Kernel = &config.KernelMemory } + if config.KernelMemoryTCP != 0 { + memory.KernelTCP = &config.KernelMemoryTCP + } + return &memory } diff --git a/daemon/info_unix.go b/daemon/info_unix.go index 60b2f99870..cbfee36b56 100644 --- a/daemon/info_unix.go +++ b/daemon/info_unix.go @@ -20,6 +20,7 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) v.MemoryLimit = sysInfo.MemoryLimit v.SwapLimit = sysInfo.SwapLimit v.KernelMemory = sysInfo.KernelMemory + v.KernelMemoryTCP = sysInfo.KernelMemoryTCP v.OomKillDisable = sysInfo.OomKillDisable v.CPUCfsPeriod = sysInfo.CPUCfsPeriod v.CPUCfsQuota = sysInfo.CPUCfsQuota diff --git a/docs/api/version-history.md b/docs/api/version-history.md index a59a90322a..f0cdbdb189 100644 --- a/docs/api/version-history.md +++ b/docs/api/version-history.md @@ -32,6 +32,7 @@ keywords: "API, Docker, rcli, REST, documentation" * `POST /swarm/init` now accepts a `DataPathPort` property to set data path port number. * `GET /info` now returns information about `DataPathPort` that is currently used in swarm * `GET /swarm` endpoint now returns DataPathPort info +* `POST /containers/create` now takes `KernelMemoryTCP` field to set hard limit for kernel TCP buffer memory. ## V1.39 API changes diff --git a/integration/container/run_linux_test.go b/integration/container/run_linux_test.go new file mode 100644 index 0000000000..6a2a647c21 --- /dev/null +++ b/integration/container/run_linux_test.go @@ -0,0 +1,49 @@ +package container // import "github.com/docker/docker/integration/container" + +import ( + "context" + "strconv" + "strings" + "testing" + "time" + + containertypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/integration/internal/container" + "github.com/docker/docker/internal/test/request" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" + "gotest.tools/skip" +) + +func TestKernelTCPMemory(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux") + skip.If(t, !testEnv.DaemonInfo.KernelMemoryTCP) + + defer setupTest(t)() + client := request.NewAPIClient(t) + ctx := context.Background() + + const ( + kernelMemoryTCP int64 = 200 * 1024 * 1024 + ) + + cID := container.Run(t, ctx, client, func(c *container.TestContainerConfig) { + c.HostConfig.Resources = containertypes.Resources{ + KernelMemoryTCP: kernelMemoryTCP, + } + }) + + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + + inspect, err := client.ContainerInspect(ctx, cID) + assert.NilError(t, err) + assert.Check(t, is.Equal(kernelMemoryTCP, inspect.HostConfig.KernelMemoryTCP)) + + res, err := container.Exec(ctx, client, cID, + []string{"cat", "/sys/fs/cgroup/memory/memory.kmem.tcp.limit_in_bytes"}) + assert.NilError(t, err) + assert.Assert(t, is.Len(res.Stderr(), 0)) + assert.Equal(t, 0, res.ExitCode) + assert.Check(t, is.Equal(strconv.FormatInt(kernelMemoryTCP, 10), strings.TrimSpace(res.Stdout()))) +} diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go index 0f327d5068..5fa5a5628c 100644 --- a/pkg/sysinfo/sysinfo.go +++ b/pkg/sysinfo/sysinfo.go @@ -47,6 +47,9 @@ type cgroupMemInfo struct { // Whether kernel memory limit is supported or not KernelMemory bool + + // Whether kernel memory TCP limit is supported or not + KernelMemoryTCP bool } type cgroupCPUInfo struct { diff --git a/pkg/sysinfo/sysinfo_linux.go b/pkg/sysinfo/sysinfo_linux.go index dde5be19bc..6492a1c9ac 100644 --- a/pkg/sysinfo/sysinfo_linux.go +++ b/pkg/sysinfo/sysinfo_linux.go @@ -95,6 +95,10 @@ func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo { if !quiet && !kernelMemory { logrus.Warn("Your kernel does not support kernel memory limit") } + kernelMemoryTCP := cgroupEnabled(mountPoint, "memory.kmem.tcp.limit_in_bytes") + if !quiet && !kernelMemoryTCP { + logrus.Warn("Your kernel does not support kernel memory TCP limit") + } return cgroupMemInfo{ MemoryLimit: true, @@ -103,6 +107,7 @@ func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo { OomKillDisable: oomKillDisable, MemorySwappiness: memorySwappiness, KernelMemory: kernelMemory, + KernelMemoryTCP: kernelMemoryTCP, } } From ee74cd777a94dcb680c30965196a80bbfab76ddd Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Sat, 27 Oct 2018 17:44:52 +0000 Subject: [PATCH 2/2] Skip KernelMemoryTCP if version is less than 1.40 Signed-off-by: Yong Tang --- api/server/router/container/container_routes.go | 6 ++++++ integration/container/run_linux_test.go | 2 ++ 2 files changed, 8 insertions(+) diff --git a/api/server/router/container/container_routes.go b/api/server/router/container/container_routes.go index c1f32724ee..836f2b1727 100644 --- a/api/server/router/container/container_routes.go +++ b/api/server/router/container/container_routes.go @@ -475,6 +475,12 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo } } + // When using API 1.39 and under, KernelMemoryTCP should be ignored because it + // was added in API 1.40. + if hostConfig != nil && versions.LessThan(version, "1.40") { + hostConfig.KernelMemoryTCP = 0 + } + ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{ Name: name, Config: config, diff --git a/integration/container/run_linux_test.go b/integration/container/run_linux_test.go index 6a2a647c21..e9d816b81f 100644 --- a/integration/container/run_linux_test.go +++ b/integration/container/run_linux_test.go @@ -8,6 +8,7 @@ import ( "time" containertypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/versions" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" "gotest.tools/assert" @@ -18,6 +19,7 @@ import ( func TestKernelTCPMemory(t *testing.T) { skip.If(t, testEnv.DaemonInfo.OSType != "linux") + skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "skip test from new feature") skip.If(t, !testEnv.DaemonInfo.KernelMemoryTCP) defer setupTest(t)()