From d3af7f283d8dc0be67be48e14cd740fbeb690f7a Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Tue, 13 Oct 2015 11:26:27 +0200 Subject: [PATCH] Add OomScoreAdj to configure container oom killer preferences libcontainer v0.0.4 introduces setting `/proc/self/oom_score_adj` to better tune oom killing preferences for container process. This patch simply integrates OomScoreAdj libcontainer's config option and adjust the cli with this new option. Signed-off-by: Antonio Murdaca Signed-off-by: Antonio Murdaca --- contrib/completion/bash/docker | 1 + contrib/completion/zsh/_docker | 1 + daemon/container_unix.go | 1 + daemon/daemon_unix.go | 3 ++ daemon/execdriver/driver_unix.go | 1 + daemon/execdriver/native/create.go | 2 ++ docs/reference/api/docker_remote_api.md | 12 ++++--- docs/reference/api/docker_remote_api_v1.22.md | 6 +++- docs/reference/commandline/create.md | 1 + docs/reference/commandline/run.md | 1 + integration-cli/docker_api_containers_test.go | 31 +++++++++++++++++++ integration-cli/docker_cli_run_test.go | 28 +++++++++++++++++ man/docker-create.1.md | 4 +++ man/docker-run.1.md | 4 +++ runconfig/hostconfig.go | 1 + runconfig/parse.go | 4 ++- 16 files changed, 94 insertions(+), 7 deletions(-) diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index f2648f70c0..9a2446b820 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -1389,6 +1389,7 @@ _docker_run() { --memory-reservation --name --net + --oom-score-adj --pid --publish -p --restart diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index 91eb563584..221bfd08cc 100644 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -482,6 +482,7 @@ __docker_subcommand() { "($help)--name=[Container name]:name: " "($help)--net=[Connect a container to a network]:network mode:(bridge none container host)" "($help)--oom-kill-disable[Disable OOM Killer]" + "($help)--oom-score-adj[Tune the host's OOM preferences for containers (accepts -1000 to 1000)]" "($help -P --publish-all)"{-P,--publish-all}"[Publish all exposed ports]" "($help)*"{-p=,--publish=}"[Expose a container's port to the host]:port:_ports" "($help)--pid=[PID namespace to use]:PID: " diff --git a/daemon/container_unix.go b/daemon/container_unix.go index 6d6ee0e7ad..9427fa0e91 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -345,6 +345,7 @@ func (daemon *Daemon) populateCommand(c *Container, env []string) error { GIDMapping: gidMap, GroupAdd: c.hostConfig.GroupAdd, Ipc: ipc, + OomScoreAdj: c.hostConfig.OomScoreAdj, Pid: pid, ReadonlyRootfs: c.hostConfig.ReadonlyRootfs, RemappedRoot: remappedRoot, diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index 520ccf087c..be11f49547 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -246,6 +246,9 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *runconfig.HostC return warnings, fmt.Errorf("Your kernel does not support oom kill disable.") } + if hostConfig.OomScoreAdj < -1000 || hostConfig.OomScoreAdj > 1000 { + return warnings, fmt.Errorf("Invalid value %d, range for oom score adj is [-1000, 1000].", hostConfig.OomScoreAdj) + } if sysInfo.IPv4ForwardingDisabled { warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.") logrus.Warnf("IPv4 forwarding is disabled. Networking will not work") diff --git a/daemon/execdriver/driver_unix.go b/daemon/execdriver/driver_unix.go index 93c710985f..f3a9d5ed80 100644 --- a/daemon/execdriver/driver_unix.go +++ b/daemon/execdriver/driver_unix.go @@ -113,6 +113,7 @@ type Command struct { GIDMapping []idtools.IDMap `json:"gidmapping"` GroupAdd []string `json:"group_add"` Ipc *Ipc `json:"ipc"` + OomScoreAdj int `json:"oom_score_adj"` Pid *Pid `json:"pid"` ReadonlyRootfs bool `json:"readonly_rootfs"` RemappedRoot *User `json:"remap_root"` diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go index 03f89f371b..e222f2e18d 100644 --- a/daemon/execdriver/native/create.go +++ b/daemon/execdriver/native/create.go @@ -83,6 +83,8 @@ func (d *Driver) createContainer(c *execdriver.Command, hooks execdriver.Hooks) return nil, err } + container.OomScoreAdj = c.OomScoreAdj + if container.Readonlyfs { for i := range container.Mounts { switch container.Mounts[i].Destination { diff --git a/docs/reference/api/docker_remote_api.md b/docs/reference/api/docker_remote_api.md index cdd0a08792..835ad7fda9 100644 --- a/docs/reference/api/docker_remote_api.md +++ b/docs/reference/api/docker_remote_api.md @@ -107,12 +107,11 @@ This section lists each version from latest to oldest. Each listing includes a * `POST /volumes/create` to create a volume. * `GET /volumes/(name)` get low-level information about a volume. * `DELETE /volumes/(name)`remove a volume with the specified name. -* `VolumeDriver` has been moved from config to hostConfig to make the configuration portable. -* `GET /images/(name)/json` now returns information about tags and digests of the image. +* `VolumeDriver` was moved from `config` to `HostConfig` to make the configuration portable. +* `GET /images/(name)/json` now returns information about an image's `RepoTags` and `RepoDigests`. * The `config` option now accepts the field `StopSignal`, which specifies the signal to use to kill a container. * `GET /containers/(id)/stats` will return networking information respectively for each interface. -* The `hostConfig` option now accepts the field `DnsOptions`, which specifies a -list of DNS options to be used in the container. +* The `HostConfig` option now includes the `DnsOptions` field to configure the container's DNS options. * `POST /build` now optionally takes a serialized map of build-time variables. * `GET /events` now includes a `timenano` field, in addition to the existing `time` field. * `GET /events` now supports filtering by image and container labels. @@ -130,6 +129,9 @@ list of DNS options to be used in the container. `NetworkSettings.Gateway`, `NetworkSettings.IPAddress`, `NetworkSettings.IPPrefixLen`, and `NetworkSettings.MacAddress` fields, which are still returned for backward-compatibility, but will be removed in a future version. +* The `HostConfig` option now includes the `OomScoreAdj` field for adjusting the + badness heuristic. This heuristic selects which processes the OOM killer kills + under out-of-memory conditions. ### v1.20 API changes @@ -217,7 +219,7 @@ container. Previously this was only available when starting a container. [Docker Remote API v1.14](docker_remote_api_v1.14.md) documentation * `DELETE /containers/(id)` when using `force`, the container will be immediately killed with SIGKILL. -* `POST /containers/(id)/start` the `hostConfig` option accepts the field `CapAdd`, which specifies a list of capabilities +* `POST /containers/(id)/start` the `HostConfig` option accepts the field `CapAdd`, which specifies a list of capabilities to add, and the field `CapDrop`, which specifies a list of capabilities to drop. * `POST /images/create` th `fromImage` and `repo` parameters support the `repo:tag` format. Consequently, the `tag` parameter is now obsolete. Using the diff --git a/docs/reference/api/docker_remote_api_v1.22.md b/docs/reference/api/docker_remote_api_v1.22.md index 18ca4452ee..df832899c7 100644 --- a/docs/reference/api/docker_remote_api_v1.22.md +++ b/docs/reference/api/docker_remote_api_v1.22.md @@ -190,6 +190,7 @@ Create a container "BlkioWeightDevice": [{}], "MemorySwappiness": 60, "OomKillDisable": false, + "OomScoreAdj": 500, "PortBindings": { "22/tcp": [{ "HostPort": "11022" }] }, "PublishAllPorts": false, "Privileged": false, @@ -243,9 +244,10 @@ Json Parameters: - **CpusetCpus** - String value containing the `cgroups CpusetCpus` to use. - **CpusetMems** - Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems. - **BlkioWeight** - Block IO weight (relative weight) accepts a weight value between 10 and 1000. - - **BlkioWeightDevice** - Block IO weight (relative device weight) in the form of: `"BlkioWeightDevice": [{"Path": "device_path", "Weight": weight}]` +- **BlkioWeightDevice** - Block IO weight (relative device weight) in the form of: `"BlkioWeightDevice": [{"Path": "device_path", "Weight": weight}]` - **MemorySwappiness** - Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100. - **OomKillDisable** - Boolean value, whether to disable OOM Killer for the container or not. +- **OomScoreAdj** - An integer value containing the score given to the container in order to tune OOM killer preferences. - **AttachStdin** - Boolean value, attaches to `stdin`. - **AttachStdout** - Boolean value, attaches to `stdout`. - **AttachStderr** - Boolean value, attaches to `stderr`. @@ -416,6 +418,7 @@ Return low-level information on the container `id` "MemoryReservation": 0, "KernelMemory": 0, "OomKillDisable": false, + "OomScoreAdj": 500, "NetworkMode": "bridge", "PortBindings": {}, "Privileged": false, @@ -1950,6 +1953,7 @@ Display system-wide information "NoProxy": "9.81.1.160", "OomKillDisable": true, "OSType": "linux", + "OomScoreAdj": 500, "OperatingSystem": "Boot2Docker", "RegistryConfig": { "IndexConfigs": { diff --git a/docs/reference/commandline/create.md b/docs/reference/commandline/create.md index d096179642..f3960127bf 100644 --- a/docs/reference/commandline/create.md +++ b/docs/reference/commandline/create.md @@ -57,6 +57,7 @@ Creates a new container. --name="" Assign a name to the container --net="default" Set the Network mode for the container --oom-kill-disable=false Whether to disable OOM Killer for the container or not + --oom-score-adj=0 Tune the host's OOM preferences for containers (accepts -1000 to 1000) -P, --publish-all=false Publish all exposed ports to random ports -p, --publish=[] Publish a container's port(s) to the host --pid="" PID namespace to use diff --git a/docs/reference/commandline/run.md b/docs/reference/commandline/run.md index a7dbc2f112..9e1cb5f548 100644 --- a/docs/reference/commandline/run.md +++ b/docs/reference/commandline/run.md @@ -61,6 +61,7 @@ parent = "smn_cli" 'host': use the host network stack inside the container 'NETWORK': connects the container to user-created network using `docker network create` command --oom-kill-disable=false Whether to disable OOM Killer for the container or not + --oom-score-adj=0 Tune the host's OOM preferences for containers (accepts -1000 to 1000) -P, --publish-all=false Publish all exposed ports to random ports -p, --publish=[] Publish a container's port(s) to the host --pid="" PID namespace to use diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index d092beb0bc..c4e0b4c2df 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -1553,3 +1553,34 @@ func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C) expected = "Invalid value 42-3,1-- for cpuset mems.\n" c.Assert(string(body), check.Equals, expected, check.Commentf("Expected output to contain %q, got %q", expected, string(body))) } + +// check validation is done daemon side and not only in cli +func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *check.C) { + testRequires(c, DaemonIsLinux) + + config := struct { + Image string + OomScoreAdj int + }{"busybox", 1001} + name := "oomscoreadj-over" + status, b, err := sockRequest("POST", "/containers/create?name="+name, config) + c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusInternalServerError) + expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]." + if !strings.Contains(string(b), expected) { + c.Fatalf("Expected output to contain %q, got %q", expected, string(b)) + } + + config = struct { + Image string + OomScoreAdj int + }{"busybox", -1001} + name = "oomscoreadj-low" + status, b, err = sockRequest("POST", "/containers/create?name="+name, config) + c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusInternalServerError) + expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]." + if !strings.Contains(string(b), expected) { + c.Fatalf("Expected output to contain %q, got %q", expected, string(b)) + } +} diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 546ad4d516..7123c74c89 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -3761,3 +3761,31 @@ func (s *DockerSuite) TestRunInvalidReference(c *check.C) { c.Fatalf(`Expected "invalid reference format" in output; got: %s`, out) } } + +func (s *DockerSuite) TestRunWithOomScoreAdj(c *check.C) { + testRequires(c, DaemonIsLinux) + + expected := "642" + out, _ := dockerCmd(c, "run", "--oom-score-adj", expected, "busybox", "cat", "/proc/self/oom_score_adj") + oomScoreAdj := strings.TrimSpace(out) + if oomScoreAdj != "642" { + c.Fatalf("Expected oom_score_adj set to %q, got %q instead", expected, oomScoreAdj) + } +} + +func (s *DockerSuite) TestRunWithOomScoreAdjInvalidRange(c *check.C) { + testRequires(c, DaemonIsLinux) + + out, _, err := dockerCmdWithError("run", "--oom-score-adj", "1001", "busybox", "true") + c.Assert(err, check.NotNil) + expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]." + if !strings.Contains(out, expected) { + c.Fatalf("Expected output to contain %q, got %q instead", expected, out) + } + out, _, err = dockerCmdWithError("run", "--oom-score-adj", "-1001", "busybox", "true") + c.Assert(err, check.NotNil) + expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]." + if !strings.Contains(out, expected) { + c.Fatalf("Expected output to contain %q, got %q instead", expected, out) + } +} diff --git a/man/docker-create.1.md b/man/docker-create.1.md index fed6278afe..596963751f 100644 --- a/man/docker-create.1.md +++ b/man/docker-create.1.md @@ -46,6 +46,7 @@ docker-create - Create a new container [**--name**[=*NAME*]] [**--net**[=*"bridge"*]] [**--oom-kill-disable**[=*false*]] +[**--oom-score-adj**[=*0*]] [**-P**|**--publish-all**[=*false*]] [**-p**|**--publish**[=*[]*]] [**--pid**[=*[]*]] @@ -229,6 +230,9 @@ This value should always larger than **-m**, so you should always use this with **--oom-kill-disable**=*true*|*false* Whether to disable OOM Killer for the container or not. +**--oom-score-adj**="" + Tune the host's OOM preferences for containers (accepts -1000 to 1000) + **-P**, **--publish-all**=*true*|*false* Publish all exposed ports to random ports on the host interfaces. The default is *false*. diff --git a/man/docker-run.1.md b/man/docker-run.1.md index 2be6b1b5c0..7529d95dff 100644 --- a/man/docker-run.1.md +++ b/man/docker-run.1.md @@ -47,6 +47,7 @@ docker-run - Run a command in a new container [**--name**[=*NAME*]] [**--net**[=*"bridge"*]] [**--oom-kill-disable**[=*false*]] +[**--oom-score-adj**[=*0*]] [**-P**|**--publish-all**[=*false*]] [**-p**|**--publish**[=*[]*]] [**--pid**[=*[]*]] @@ -341,6 +342,9 @@ and foreground Docker containers. **--oom-kill-disable**=*true*|*false* Whether to disable OOM Killer for the container or not. +**--oom-score-adj**="" + Tune the host's OOM preferences for containers (accepts -1000 to 1000) + **-P**, **--publish-all**=*true*|*false* Publish all exposed ports to random ports on the host interfaces. The default is *false*. diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 946e8294ce..2cca3140ad 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -211,6 +211,7 @@ type HostConfig struct { GroupAdd []string // List of additional groups that the container process will run as IpcMode IpcMode // IPC namespace to use for the container Links []string // List of links (in the name:alias form) + OomScoreAdj int // Container preference for OOM-killing OomKillDisable bool // Whether to disable OOM Killer or not PidMode PidMode // PID namespace to use for the container Privileged bool // Is the container in privileged mode diff --git a/runconfig/parse.go b/runconfig/parse.go index 2acaf8ba36..5dd0dc644e 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -79,6 +79,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep STDIN open even if not attached") flTty = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-TTY") flOomKillDisable = cmd.Bool([]string{"-oom-kill-disable"}, false, "Disable OOM Killer") + flOomScoreAdj = cmd.Int([]string{"-oom-score-adj"}, 0, "Tune host's OOM preferences (-1000 to 1000)") flContainerIDFile = cmd.String([]string{"-cidfile"}, "", "Write the container ID to the file") flEntrypoint = cmd.String([]string{"-entrypoint"}, "", "Overwrite the default ENTRYPOINT of the image") flHostname = cmd.String([]string{"h", "-hostname"}, "", "Container host name") @@ -94,7 +95,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe flCpusetCpus = cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)") flCpusetMems = cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)") flBlkioWeight = cmd.Uint16([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000") - flSwappiness = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tuning container memory swappiness (0 to 100)") + flSwappiness = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tune container memory swappiness (0 to 100)") flNetMode = cmd.String([]string{"-net"}, "default", "Set the Network for the container") flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)") flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use") @@ -369,6 +370,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe hostConfig := &HostConfig{ Binds: binds, ContainerIDFile: *flContainerIDFile, + OomScoreAdj: *flOomScoreAdj, OomKillDisable: *flOomKillDisable, Privileged: *flPrivileged, PortBindings: portBindings,