diff --git a/client/driver/docker.go b/client/driver/docker.go index b5984722a..070f6e3e3 100644 --- a/client/driver/docker.go +++ b/client/driver/docker.go @@ -235,6 +235,7 @@ type DockerDriverConfig struct { ReadonlyRootfs bool `mapstructure:"readonly_rootfs"` // Mount the container’s root filesystem as read only AdvertiseIPv6Address bool `mapstructure:"advertise_ipv6_address"` // Flag to use the GlobalIPv6Address from the container as the detected IP CPUHardLimit bool `mapstructure:"cpu_hard_limit"` // Enforce CPU hard limit. + CPUCFSPeriod int64 `mapstructure:"cpu_cfs_period"` // Set the period for the CFS scheduler for the cgroup. PidsLimit int64 `mapstructure:"pids_limit"` // Enforce Docker Pids limit } @@ -741,6 +742,9 @@ func (d *DockerDriver) Validate(config map[string]interface{}) error { "cpu_hard_limit": { Type: fields.TypeBool, }, + "cpu_cfs_period": { + Type: fields.TypeInt, + }, "pids_limit": { Type: fields.TypeInt, }, @@ -1238,7 +1242,14 @@ func (d *DockerDriver) createContainerConfig(ctx *ExecContext, task *structs.Tas if driverConfig.CPUHardLimit { numCores := runtime.NumCPU() percentTicks := float64(task.Resources.CPU) / float64(d.node.Resources.CPU) - hostConfig.CPUQuota = int64(percentTicks*defaultCFSPeriodUS) * int64(numCores) + if driverConfig.CPUCFSPeriod < 0 || driverConfig.CPUCFSPeriod > 1000000 { + return c, fmt.Errorf("invalid value for cpu_cfs_period") + } + if driverConfig.CPUCFSPeriod == 0 { + driverConfig.CPUCFSPeriod = defaultCFSPeriodUS + } + hostConfig.CPUPeriod = driverConfig.CPUCFSPeriod + hostConfig.CPUQuota = int64(percentTicks*float64(driverConfig.CPUCFSPeriod)) * int64(numCores) } // Windows does not support MemorySwap/MemorySwappiness #2193 diff --git a/client/driver/docker_test.go b/client/driver/docker_test.go index 674cae9b2..04c74fdad 100644 --- a/client/driver/docker_test.go +++ b/client/driver/docker_test.go @@ -2524,3 +2524,25 @@ func TestDockerImageRef(t *testing.T) { }) } } + +func TestDockerDriver_CPUCFSPeriod(t *testing.T) { + if !tu.IsTravis() { + t.Parallel() + } + if !testutil.DockerIsConnected(t) { + t.Skip("Docker not connected") + } + + task, _, _ := dockerTask(t) + task.Config["cpu_hard_limit"] = true + task.Config["cpu_cfs_period"] = 1000000 + + client, handle, cleanup := dockerSetup(t, task) + defer cleanup() + + waitForExist(t, client, handle) + + container, err := client.InspectContainer(handle.ContainerID()) + assert.Nil(t, err, "Error inspecting container: %v", err) + assert.Equal(t, int64(1000000), container.HostConfig.CPUPeriod, "cpu_cfs_period option incorrectly set") +} diff --git a/website/source/docs/drivers/docker.html.md b/website/source/docs/drivers/docker.html.md index 9528cbc7e..890c3ffbf 100644 --- a/website/source/docs/drivers/docker.html.md +++ b/website/source/docs/drivers/docker.html.md @@ -74,7 +74,7 @@ The `docker` driver supports the following configuration in the job spec. Only command = "my-command" } ``` - + * `dns_search_domains` - (Optional) A list of DNS search domains for the container to use. @@ -361,7 +361,12 @@ The `docker` driver supports the following configuration in the job spec. Only soft limiting is used and containers are able to burst above their CPU limit when there is idle capacity. -* `advertise_ipv6_address` - (Optional) `true` or `false` (default). Use the container's +* `cpu_cfs_period` - (Optional) An integer value that specifies the duration in microseconds of the period + during which the CPU usage quota is measured. The default is 100000 (0.1 second) and the maximum allowed + value is 1000000 (1 second). See [here](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/sec-cpu#sect-cfs) + for more details. + +* `advertise_ipv6_address` - (Optional) `true` or `false` (default). Use the container's IPv6 address (GlobalIPv6Address in Docker) when registering services and checks. See [IPv6 Docker containers](/docs/job-specification/service.html#IPv6 Docker containers) for details. @@ -639,10 +644,10 @@ options](/docs/agent/configuration/client.html#options): * `docker.caps.whitelist`: A list of allowed Linux capabilities. Defaults to `"CHOWN,DAC_OVERRIDE,FSETID,FOWNER,MKNOD,NET_RAW,SETGID,SETUID,SETFCAP,SETPCAP,NET_BIND_SERVICE,SYS_CHROOT,KILL,AUDIT_WRITE"`, - which is the list of capabilities allowed by docker by default, as + which is the list of capabilities allowed by docker by default, as [defined here](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities). - Allows the operator to control which capabilities can be obtained by - tasks using `cap_add` and `cap_drop` options. Supports the value `"ALL"` as a + Allows the operator to control which capabilities can be obtained by + tasks using `cap_add` and `cap_drop` options. Supports the value `"ALL"` as a shortcut for whitelisting all capabilities. Note: When testing or using the `-dev` flag you can use `DOCKER_HOST`,