diff --git a/.changelog/11526.txt b/.changelog/11526.txt new file mode 100644 index 000000000..3adb28d67 --- /dev/null +++ b/.changelog/11526.txt @@ -0,0 +1,3 @@ +```release-note:improvement +driver/docker: Added support for client-wide `pids_limit` configuration +``` diff --git a/drivers/docker/config.go b/drivers/docker/config.go index aa556d1d4..d45d6f24c 100644 --- a/drivers/docker/config.go +++ b/drivers/docker/config.go @@ -289,6 +289,7 @@ var ( hclspec.NewAttr("pull_activity_timeout", "string", false), hclspec.NewLiteral(`"2m"`), ), + "pids_limit": hclspec.NewAttr("pids_limit", "number", false), // disable_log_collection indicates whether docker driver should collect logs of docker // task containers. If true, nomad doesn't start docker_logger/logmon processes "disable_log_collection": hclspec.NewAttr("disable_log_collection", "bool", false), @@ -623,6 +624,7 @@ type DriverConfig struct { infraImagePullTimeoutDuration time.Duration `codec:"-"` DisableLogCollection bool `codec:"disable_log_collection"` PullActivityTimeout string `codec:"pull_activity_timeout"` + PidsLimit int64 `codec:"pids_limit"` pullActivityTimeoutDuration time.Duration `codec:"-"` ExtraLabels []string `codec:"extra_labels"` Logging LoggingConfig `codec:"logging"` diff --git a/drivers/docker/driver.go b/drivers/docker/driver.go index cd00a359e..f86879ebc 100644 --- a/drivers/docker/driver.go +++ b/drivers/docker/driver.go @@ -826,6 +826,21 @@ func (d *Driver) createContainerConfig(task *drivers.TaskConfig, driverConfig *T memory, memoryReservation := memoryLimits(driverConfig.MemoryHardLimit, task.Resources.NomadResources.Memory) + var pidsLimit int64 + + // Pids limit defined in Nomad plugin config. Defaults to 0 (Unlimited). + if d.config.PidsLimit > 0 { + pidsLimit = d.config.PidsLimit + } + + // Override Nomad plugin config pids limit, by user defined pids limit. + if driverConfig.PidsLimit > 0 { + if d.config.PidsLimit > 0 && driverConfig.PidsLimit > d.config.PidsLimit { + return c, fmt.Errorf("pids_limit cannot be greater than nomad plugin config pids_limit: %d", d.config.PidsLimit) + } + pidsLimit = driverConfig.PidsLimit + } + hostConfig := &docker.HostConfig{ Memory: memory, // hard limit MemoryReservation: memoryReservation, // soft limit @@ -840,7 +855,7 @@ func (d *Driver) createContainerConfig(task *drivers.TaskConfig, driverConfig *T StorageOpt: driverConfig.StorageOpt, VolumeDriver: driverConfig.VolumeDriver, - PidsLimit: &driverConfig.PidsLimit, + PidsLimit: &pidsLimit, Runtime: containerRuntime, } diff --git a/drivers/docker/driver_linux_test.go b/drivers/docker/driver_linux_test.go index eb526edfa..ba79af839 100644 --- a/drivers/docker/driver_linux_test.go +++ b/drivers/docker/driver_linux_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/hashicorp/nomad/client/testutil" + "github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/helper/freeport" tu "github.com/hashicorp/nomad/testutil" "github.com/stretchr/testify/require" @@ -45,6 +46,31 @@ func TestDockerDriver_authFromHelper(t *testing.T) { require.Equal(t, "registry.local:5000", string(content)) } +func TestDockerDriver_PluginConfig_PidsLimit(t *testing.T) { + if !tu.IsCI() { + t.Parallel() + } + + dh := dockerDriverHarness(t, nil) + driver := dh.Impl().(*Driver) + driver.config.PidsLimit = 5 + + task, cfg, ports := dockerTask(t) + defer freeport.Return(ports) + require.NoError(t, task.EncodeConcreteDriverConfig(cfg)) + + cfg.PidsLimit = 7 + _, err := driver.createContainerConfig(task, cfg, "org/repo:0.1") + require.Error(t, err) + require.Contains(t, err.Error(), `pids_limit cannot be greater than nomad plugin config pids_limit`) + + // Task PidsLimit should override plugin PidsLimit. + cfg.PidsLimit = 3 + opts, err := driver.createContainerConfig(task, cfg, "org/repo:0.1") + require.NoError(t, err) + require.Equal(t, helper.Int64ToPtr(3), opts.HostConfig.PidsLimit) +} + func TestDockerDriver_PidsLimit(t *testing.T) { if !tu.IsCI() { t.Parallel() diff --git a/drivers/docker/fingerprint.go b/drivers/docker/fingerprint.go index bfe64df9c..be5fd19c0 100644 --- a/drivers/docker/fingerprint.go +++ b/drivers/docker/fingerprint.go @@ -120,6 +120,10 @@ func (d *Driver) buildFingerprint() *drivers.Fingerprint { fp.Attributes["driver.docker.privileged.enabled"] = pstructs.NewBoolAttribute(true) } + if d.config.PidsLimit > 0 { + fp.Attributes["driver.docker.pids.limit"] = pstructs.NewIntAttribute(d.config.PidsLimit, "") + } + if d.config.Volumes.Enabled { fp.Attributes["driver.docker.volumes.enabled"] = pstructs.NewBoolAttribute(true) } diff --git a/website/content/docs/drivers/docker.mdx b/website/content/docs/drivers/docker.mdx index 3aefd92a5..83bb534aa 100644 --- a/website/content/docs/drivers/docker.mdx +++ b/website/content/docs/drivers/docker.mdx @@ -836,6 +836,12 @@ plugin "docker" { from the Docker engine during an image pull within this timeframe, Nomad will timeout the request that initiated the pull command. (Minimum of `1m`) +- `pids_limit` - Defaults to unlimited (`0`). An integer value that specifies + the pid limit for all the Docker containers running on that Nomad client. You + can override this limit by setting [`pids_limit`] in your task config. If + this value is greater than `0`, your task `pids_limit` must be less than or + equal to the value defined here. + - `allow_caps` - A list of allowed Linux capabilities. Defaults to ```hcl @@ -1167,3 +1173,4 @@ Windows is relatively new and rapidly evolving you may want to consult the [allow_caps]: /docs/drivers/docker#allow_caps [Connect]: /docs/job-specification/connect [`bridge`]: docs/job-specification/network#bridge +[`pids_limit`]: /docs/drivers/docker#pids_limit