- Adds updating PidsLimit in UpdateContainer().
- Adds setting PidsLimit in toContainerResources().
Signed-off-by: Sunny Gogoi <indiasuny000@gmail.com>
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
| ... | ... |
@@ -460,9 +460,10 @@ definitions: |
| 460 | 460 |
type: "boolean" |
| 461 | 461 |
x-nullable: true |
| 462 | 462 |
PidsLimit: |
| 463 |
- description: "Tune a container's pids limit. Set -1 for unlimited." |
|
| 463 |
+ description: "Tune a container's pids limit. Set 0 or -1 for unlimited. Leave null to not change" |
|
| 464 | 464 |
type: "integer" |
| 465 | 465 |
format: "int64" |
| 466 |
+ x-nullable: true |
|
| 466 | 467 |
Ulimits: |
| 467 | 468 |
description: | |
| 468 | 469 |
A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`"
|
| ... | ... |
@@ -3689,6 +3690,10 @@ definitions: |
| 3689 | 3689 |
See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) |
| 3690 | 3690 |
type: "boolean" |
| 3691 | 3691 |
example: true |
| 3692 |
+ PidsLimit: |
|
| 3693 |
+ description: "Indicates if the host kernel has PID limit support enabled." |
|
| 3694 |
+ type: "boolean" |
|
| 3695 |
+ example: true |
|
| 3692 | 3696 |
OomKillDisable: |
| 3693 | 3697 |
description: "Indicates if OOM killer disable is supported on the host." |
| 3694 | 3698 |
type: "boolean" |
| ... | ... |
@@ -4625,7 +4630,7 @@ paths: |
| 4625 | 4625 |
OomKillDisable: false |
| 4626 | 4626 |
OomScoreAdj: 500 |
| 4627 | 4627 |
PidMode: "" |
| 4628 |
- PidsLimit: -1 |
|
| 4628 |
+ PidsLimit: 0 |
|
| 4629 | 4629 |
PortBindings: |
| 4630 | 4630 |
22/tcp: |
| 4631 | 4631 |
- HostPort: "11022" |
| ... | ... |
@@ -334,7 +334,7 @@ type Resources struct {
|
| 334 | 334 |
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap |
| 335 | 335 |
MemorySwappiness *int64 // Tuning container memory swappiness behaviour |
| 336 | 336 |
OomKillDisable *bool // Whether to disable OOM Killer or not |
| 337 |
- PidsLimit int64 // Setting pids limit for a container |
|
| 337 |
+ PidsLimit *int64 // Setting pids limit for a container |
|
| 338 | 338 |
Ulimits []*units.Ulimit // List of ulimits to be set in the container |
| 339 | 339 |
|
| 340 | 340 |
// Applicable to Windows |
| ... | ... |
@@ -342,6 +342,9 @@ func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfi |
| 342 | 342 |
if resources.CPURealtimeRuntime != 0 {
|
| 343 | 343 |
cResources.CPURealtimeRuntime = resources.CPURealtimeRuntime |
| 344 | 344 |
} |
| 345 |
+ if resources.PidsLimit != nil {
|
|
| 346 |
+ cResources.PidsLimit = resources.PidsLimit |
|
| 347 |
+ } |
|
| 345 | 348 |
|
| 346 | 349 |
// update HostConfig of container |
| 347 | 350 |
if hostConfig.RestartPolicy.Name != "" {
|
| ... | ... |
@@ -159,7 +159,7 @@ func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfi |
| 159 | 159 |
resources.MemorySwap != 0 || |
| 160 | 160 |
resources.MemorySwappiness != nil || |
| 161 | 161 |
resources.OomKillDisable != nil || |
| 162 |
- resources.PidsLimit != 0 || |
|
| 162 |
+ (resources.PidsLimit != nil && *resources.PidsLimit != 0) || |
|
| 163 | 163 |
len(resources.Ulimits) != 0 || |
| 164 | 164 |
resources.CPUCount != 0 || |
| 165 | 165 |
resources.CPUPercent != 0 || |
| ... | ... |
@@ -118,6 +118,19 @@ func getMemoryResources(config containertypes.Resources) *specs.LinuxMemory {
|
| 118 | 118 |
return &memory |
| 119 | 119 |
} |
| 120 | 120 |
|
| 121 |
+func getPidsLimit(config containertypes.Resources) *specs.LinuxPids {
|
|
| 122 |
+ limit := &specs.LinuxPids{}
|
|
| 123 |
+ if config.PidsLimit != nil {
|
|
| 124 |
+ limit.Limit = *config.PidsLimit |
|
| 125 |
+ if limit.Limit == 0 {
|
|
| 126 |
+ // docker API allows 0 to unset this to be consistent with default values. |
|
| 127 |
+ // when updating values, runc requires -1 |
|
| 128 |
+ limit.Limit = -1 |
|
| 129 |
+ } |
|
| 130 |
+ } |
|
| 131 |
+ return limit |
|
| 132 |
+} |
|
| 133 |
+ |
|
| 121 | 134 |
func getCPUResources(config containertypes.Resources) (*specs.LinuxCPU, error) {
|
| 122 | 135 |
cpu := specs.LinuxCPU{}
|
| 123 | 136 |
|
| ... | ... |
@@ -453,9 +466,10 @@ func verifyPlatformContainerResources(resources *containertypes.Resources, sysIn |
| 453 | 453 |
if resources.OomKillDisable != nil && *resources.OomKillDisable && resources.Memory == 0 {
|
| 454 | 454 |
warnings = append(warnings, "OOM killer is disabled for the container, but no memory limit is set, this can result in the system running out of resources.") |
| 455 | 455 |
} |
| 456 |
- if resources.PidsLimit != 0 && !sysInfo.PidsLimit {
|
|
| 456 |
+ if resources.PidsLimit != nil && *resources.PidsLimit != 0 && !sysInfo.PidsLimit {
|
|
| 457 | 457 |
warnings = append(warnings, "Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.") |
| 458 |
- resources.PidsLimit = 0 |
|
| 458 |
+ var limit int64 |
|
| 459 |
+ resources.PidsLimit = &limit |
|
| 459 | 460 |
} |
| 460 | 461 |
|
| 461 | 462 |
// cpu subsystem checks and adjustments |
| ... | ... |
@@ -181,7 +181,7 @@ func verifyPlatformContainerResources(resources *containertypes.Resources, isHyp |
| 181 | 181 |
if resources.OomKillDisable != nil && *resources.OomKillDisable {
|
| 182 | 182 |
return warnings, fmt.Errorf("invalid option: Windows does not support OomKillDisable")
|
| 183 | 183 |
} |
| 184 |
- if resources.PidsLimit != 0 {
|
|
| 184 |
+ if resources.PidsLimit != nil && *resources.PidsLimit != 0 {
|
|
| 185 | 185 |
return warnings, fmt.Errorf("invalid option: Windows does not support PidsLimit")
|
| 186 | 186 |
} |
| 187 | 187 |
if len(resources.Ulimits) != 0 {
|
| ... | ... |
@@ -27,6 +27,7 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) |
| 27 | 27 |
v.CPUCfsQuota = sysInfo.CPUCfsQuota |
| 28 | 28 |
v.CPUShares = sysInfo.CPUShares |
| 29 | 29 |
v.CPUSet = sysInfo.Cpuset |
| 30 |
+ v.PidsLimit = sysInfo.PidsLimit |
|
| 30 | 31 |
v.Runtimes = daemon.configStore.GetAllRuntimes() |
| 31 | 32 |
v.DefaultRuntime = daemon.configStore.GetDefaultRuntimeName() |
| 32 | 33 |
v.InitBinary = daemon.configStore.GetInitPath() |
| ... | ... |
@@ -72,9 +72,7 @@ func setResources(s *specs.Spec, r containertypes.Resources) error {
|
| 72 | 72 |
ThrottleReadIOPSDevice: readIOpsDevice, |
| 73 | 73 |
ThrottleWriteIOPSDevice: writeIOpsDevice, |
| 74 | 74 |
}, |
| 75 |
- Pids: &specs.LinuxPids{
|
|
| 76 |
- Limit: r.PidsLimit, |
|
| 77 |
- }, |
|
| 75 |
+ Pids: getPidsLimit(r), |
|
| 78 | 76 |
} |
| 79 | 77 |
|
| 80 | 78 |
if s.Linux.Resources != nil && len(s.Linux.Resources.Devices) > 0 {
|
| ... | ... |
@@ -7,6 +7,7 @@ import ( |
| 7 | 7 |
"testing" |
| 8 | 8 |
"time" |
| 9 | 9 |
|
| 10 |
+ "github.com/docker/docker/api/types" |
|
| 10 | 11 |
containertypes "github.com/docker/docker/api/types/container" |
| 11 | 12 |
"github.com/docker/docker/integration/internal/container" |
| 12 | 13 |
"gotest.tools/assert" |
| ... | ... |
@@ -101,3 +102,67 @@ func TestUpdateCPUQuota(t *testing.T) {
|
| 101 | 101 |
assert.Check(t, is.Equal(strconv.FormatInt(test.update, 10), strings.TrimSpace(res.Stdout()))) |
| 102 | 102 |
} |
| 103 | 103 |
} |
| 104 |
+ |
|
| 105 |
+func TestUpdatePidsLimit(t *testing.T) {
|
|
| 106 |
+ skip.If(t, testEnv.DaemonInfo.OSType == "windows") |
|
| 107 |
+ skip.If(t, !testEnv.DaemonInfo.PidsLimit) |
|
| 108 |
+ |
|
| 109 |
+ defer setupTest(t)() |
|
| 110 |
+ client := testEnv.APIClient() |
|
| 111 |
+ ctx := context.Background() |
|
| 112 |
+ |
|
| 113 |
+ cID := container.Run(t, ctx, client) |
|
| 114 |
+ |
|
| 115 |
+ intPtr := func(i int64) *int64 {
|
|
| 116 |
+ return &i |
|
| 117 |
+ } |
|
| 118 |
+ |
|
| 119 |
+ for _, test := range []struct {
|
|
| 120 |
+ desc string |
|
| 121 |
+ update *int64 |
|
| 122 |
+ expect int64 |
|
| 123 |
+ expectCg string |
|
| 124 |
+ }{
|
|
| 125 |
+ {desc: "update from none", update: intPtr(32), expect: 32, expectCg: "32"},
|
|
| 126 |
+ {desc: "no change", update: nil, expectCg: "32"},
|
|
| 127 |
+ {desc: "update lower", update: intPtr(16), expect: 16, expectCg: "16"},
|
|
| 128 |
+ {desc: "unset limit", update: intPtr(0), expect: 0, expectCg: "max"},
|
|
| 129 |
+ } {
|
|
| 130 |
+ var before types.ContainerJSON |
|
| 131 |
+ if test.update == nil {
|
|
| 132 |
+ var err error |
|
| 133 |
+ before, err = client.ContainerInspect(ctx, cID) |
|
| 134 |
+ assert.NilError(t, err) |
|
| 135 |
+ } |
|
| 136 |
+ |
|
| 137 |
+ t.Run(test.desc, func(t *testing.T) {
|
|
| 138 |
+ _, err := client.ContainerUpdate(ctx, cID, containertypes.UpdateConfig{
|
|
| 139 |
+ Resources: containertypes.Resources{
|
|
| 140 |
+ PidsLimit: test.update, |
|
| 141 |
+ }, |
|
| 142 |
+ }) |
|
| 143 |
+ assert.NilError(t, err) |
|
| 144 |
+ |
|
| 145 |
+ inspect, err := client.ContainerInspect(ctx, cID) |
|
| 146 |
+ assert.NilError(t, err) |
|
| 147 |
+ assert.Assert(t, inspect.HostConfig.Resources.PidsLimit != nil) |
|
| 148 |
+ |
|
| 149 |
+ if test.update == nil {
|
|
| 150 |
+ assert.Assert(t, before.HostConfig.Resources.PidsLimit != nil) |
|
| 151 |
+ assert.Equal(t, *before.HostConfig.Resources.PidsLimit, *inspect.HostConfig.Resources.PidsLimit) |
|
| 152 |
+ } else {
|
|
| 153 |
+ assert.Equal(t, *inspect.HostConfig.Resources.PidsLimit, test.expect) |
|
| 154 |
+ } |
|
| 155 |
+ |
|
| 156 |
+ ctx, cancel := context.WithTimeout(ctx, 60*time.Second) |
|
| 157 |
+ defer cancel() |
|
| 158 |
+ |
|
| 159 |
+ res, err := container.Exec(ctx, client, cID, []string{"cat", "/sys/fs/cgroup/pids/pids.max"})
|
|
| 160 |
+ assert.NilError(t, err) |
|
| 161 |
+ assert.Assert(t, is.Len(res.Stderr(), 0)) |
|
| 162 |
+ |
|
| 163 |
+ out := strings.TrimSpace(res.Stdout()) |
|
| 164 |
+ assert.Equal(t, out, test.expectCg) |
|
| 165 |
+ }) |
|
| 166 |
+ } |
|
| 167 |
+} |