From 0b1e2b5a553565e99afd7ceda36beab098f506d0 Mon Sep 17 00:00:00 2001 From: Vishnu Kannan Date: Mon, 16 Mar 2015 22:42:15 +0000 Subject: [PATCH 1/3] Adding '--cgroup-parent' flag to docker run. This feature helps users implement more complex resource isolation policies on top of what native docker provides. Docker-DCO-1.1-Signed-off-by: Vishnu Kannan (github: vishh) --- daemon/container.go | 1 + daemon/execdriver/driver.go | 6 ++++++ runconfig/hostconfig.go | 2 ++ runconfig/parse.go | 2 ++ 4 files changed, 11 insertions(+) diff --git a/daemon/container.go b/daemon/container.go index 31bce1317a..e9b360083c 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -345,6 +345,7 @@ func populateCommand(c *Container, env []string) error { MountLabel: c.GetMountLabel(), LxcConfig: lxcConfig, AppArmorProfile: c.AppArmorProfile, + CgroupParent: c.hostConfig.CgroupParent, } return nil diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index 932b734d27..e937de3beb 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -164,6 +164,7 @@ type Command struct { MountLabel string `json:"mount_label"` LxcConfig []string `json:"lxc_config"` AppArmorProfile string `json:"apparmor_profile"` + CgroupParent string `json:"cgroup_parent"` // The parent cgroup for this command. } func InitContainer(c *Command) *configs.Config { @@ -179,6 +180,11 @@ func InitContainer(c *Command) *configs.Config { // check to see if we are running in ramdisk to disable pivot root container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != "" + + // Default parent cgroup is "docker". Override if required. + if c.CgroupParent != "" { + container.Cgroups.Parent = c.CgroupParent + } return container } diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 72a80dc5d1..84d636b5c4 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -131,6 +131,7 @@ type HostConfig struct { ReadonlyRootfs bool Ulimits []*ulimit.Ulimit LogConfig LogConfig + CgroupParent string // Parent cgroup. } // This is used by the create command when you want to set both the @@ -182,6 +183,7 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig { IpcMode: IpcMode(job.Getenv("IpcMode")), PidMode: PidMode(job.Getenv("PidMode")), ReadonlyRootfs: job.GetenvBool("ReadonlyRootfs"), + CgroupParent: job.Getenv("CgroupParent"), } // FIXME: This is for backward compatibility, if people use `Cpuset` diff --git a/runconfig/parse.go b/runconfig/parse.go index 34b0becf6a..ccd8056cf9 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -71,6 +71,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe flRestartPolicy = cmd.String([]string{"-restart"}, "no", "Restart policy to apply when a container exits") flReadonlyRootfs = cmd.Bool([]string{"-read-only"}, false, "Mount the container's root filesystem as read only") flLoggingDriver = cmd.String([]string{"-log-driver"}, "", "Logging driver for container") + flCgroupParent = cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container") ) cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR") @@ -332,6 +333,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe ReadonlyRootfs: *flReadonlyRootfs, Ulimits: flUlimits.GetList(), LogConfig: LogConfig{Type: *flLoggingDriver}, + CgroupParent: *flCgroupParent, } // When allocating stdin in attached mode, close stdin at client disconnect From f7dd1333b5a6dfa34ce8b2fb15533f1aca6efdc3 Mon Sep 17 00:00:00 2001 From: Vishnu Kannan Date: Mon, 16 Mar 2015 23:06:47 +0000 Subject: [PATCH 2/3] Adding documentation for '--cgroup-parent' option. Docker-DCO-1.1-Signed-off-by: Vishnu Kannan (github: vishh) --- docs/man/docker-create.1.md | 4 ++++ docs/man/docker-run.1.md | 4 ++++ docs/sources/reference/commandline/cli.md | 1 + 3 files changed, 9 insertions(+) diff --git a/docs/man/docker-create.1.md b/docs/man/docker-create.1.md index ddb234d377..62a4c60bb1 100644 --- a/docs/man/docker-create.1.md +++ b/docs/man/docker-create.1.md @@ -46,6 +46,7 @@ docker-create - Create a new container [**-v**|**--volume**[=*[]*]] [**--volumes-from**[=*[]*]] [**-w**|**--workdir**[=*WORKDIR*]] +[**--cgroup-parent**[=*CGROUP-PATH*]] IMAGE [COMMAND] [ARG...] # OPTIONS @@ -67,6 +68,9 @@ IMAGE [COMMAND] [ARG...] **--cidfile**="" Write the container ID to the file +**--cgroup-parent**="" + Path to cgroups under which the cgroup for the container will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist. + **--cpuset-cpus**="" CPUs in which to allow execution (0-3, 0,1) diff --git a/docs/man/docker-run.1.md b/docs/man/docker-run.1.md index 7e8187744f..c95c69e709 100644 --- a/docs/man/docker-run.1.md +++ b/docs/man/docker-run.1.md @@ -49,6 +49,7 @@ docker-run - Run a command in a new container [**-v**|**--volume**[=*[]*]] [**--volumes-from**[=*[]*]] [**-w**|**--workdir**[=*WORKDIR*]] +[**--cgroup-parent**[=*CGROUP-PATH*]] IMAGE [COMMAND] [ARG...] # DESCRIPTION @@ -124,6 +125,9 @@ division of CPU shares: **--cap-drop**=[] Drop Linux capabilities +**--cgroup-parent**="" + Path to cgroups under which the cgroup for the container will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist. + **--cidfile**="" Write the container ID to the file diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 0ab5cfe897..7b6d1bf60c 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -805,6 +805,7 @@ Creates a new container. -c, --cpu-shares=0 CPU shares (relative weight) --cap-add=[] Add Linux capabilities --cap-drop=[] Drop Linux capabilities + --cgroup-parent= Optional parent cgroup for the container --cidfile="" Write the container ID to the file --cpuset-cpus="" CPUs in which to allow execution (0-3, 0,1) --device=[] Add a host device to the container From c7267017e931cee8ecd0e8d88473f26a80a3314c Mon Sep 17 00:00:00 2001 From: Vishnu Kannan Date: Mon, 16 Mar 2015 23:41:38 +0000 Subject: [PATCH 3/3] Adding integration tests for --cgroup-parent feature. Docker-DCO-1.1-Signed-off-by: Vishnu Kannan (github: vishh) --- .../reference/api/docker_remote_api.md | 4 + .../reference/api/docker_remote_api_v1.18.md | 4 +- docs/sources/reference/commandline/cli.md | 2 +- integration-cli/docker_cli_run_unix_test.go | 79 +++++++++++++++++++ 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/docs/sources/reference/api/docker_remote_api.md b/docs/sources/reference/api/docker_remote_api.md index b4286d1fa2..122546cf75 100644 --- a/docs/sources/reference/api/docker_remote_api.md +++ b/docs/sources/reference/api/docker_remote_api.md @@ -72,6 +72,10 @@ Added a `RepoDigests` field to include image digest information. **New!** Builds can now set resource constraints for all containers created for the build. +**New!** +(`CgroupParent`) can be passed in the host config to setup container cgroups under a specific cgroup. + + ## v1.17 ### Full Documentation diff --git a/docs/sources/reference/api/docker_remote_api_v1.18.md b/docs/sources/reference/api/docker_remote_api_v1.18.md index 6518247c7e..3ebddb7d13 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.18.md +++ b/docs/sources/reference/api/docker_remote_api_v1.18.md @@ -162,7 +162,8 @@ Create a container "NetworkMode": "bridge", "Devices": [], "Ulimits": [{}], - "LogConfig": { "Type": "json-file", Config: {} } + "LogConfig": { "Type": "json-file", Config: {} }, + "CgroupParent": "" } } @@ -260,6 +261,7 @@ Json Parameters: `{ "Type": "", "Config": {"key1": "val1"}} Available types: `json-file`, `none`. `json-file` logging driver. + - **CgroupParent** - Path to cgroups under which the cgroup for the container will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist. Query Parameters: diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 7b6d1bf60c..81f6b04d77 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -805,7 +805,7 @@ Creates a new container. -c, --cpu-shares=0 CPU shares (relative weight) --cap-add=[] Add Linux capabilities --cap-drop=[] Drop Linux capabilities - --cgroup-parent= Optional parent cgroup for the container + --cgroup-parent="" Optional parent cgroup for the container --cidfile="" Write the container ID to the file --cpuset-cpus="" CPUs in which to allow execution (0-3, 0,1) --device=[] Add a host device to the container diff --git a/integration-cli/docker_cli_run_unix_test.go b/integration-cli/docker_cli_run_unix_test.go index 477325bf55..c2682b4ac6 100644 --- a/integration-cli/docker_cli_run_unix_test.go +++ b/integration-cli/docker_cli_run_unix_test.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "os" "os/exec" + "path" "path/filepath" "strings" "testing" @@ -107,3 +108,81 @@ func TestRunWithUlimits(t *testing.T) { logDone("run - ulimits are set") } + +func getCgroupPaths(test string) map[string]string { + cgroupPaths := map[string]string{} + for _, line := range strings.Split(test, "\n") { + parts := strings.Split(line, ":") + if len(parts) != 3 { + fmt.Printf("unexpected file format for /proc/self/cgroup - %q", line) + continue + } + cgroupPaths[parts[1]] = parts[2] + } + return cgroupPaths +} + +func TestRunContainerWithCgroupParent(t *testing.T) { + testRequires(t, NativeExecDriver) + defer deleteAllContainers() + + cgroupParent := "test" + data, err := ioutil.ReadFile("/proc/self/cgroup") + if err != nil { + t.Fatalf("failed to read '/proc/self/cgroup - %v", err) + } + selfCgroupPaths := getCgroupPaths(string(data)) + selfCpuCgroup, found := selfCgroupPaths["cpu"] + if !found { + t.Fatalf("unable to find self cpu cgroup path. CgroupsPath: %v", selfCgroupPaths) + } + + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "--cgroup-parent", cgroupParent, "--rm", "busybox", "cat", "/proc/self/cgroup")) + if err != nil { + t.Fatalf("unexpected failure when running container with --cgroup-parent option - %s\n%v", string(out), err) + } + cgroupPaths := getCgroupPaths(string(out)) + if len(cgroupPaths) == 0 { + t.Fatalf("unexpected output - %q", string(out)) + } + found = false + expectedCgroupPrefix := path.Join(selfCpuCgroup, cgroupParent) + for _, path := range cgroupPaths { + if strings.HasPrefix(path, expectedCgroupPrefix) { + found = true + break + } + } + if !found { + t.Fatalf("unexpected cgroup paths. Expected at least one cgroup path to have prefix %q. Cgroup Paths: %v", expectedCgroupPrefix, cgroupPaths) + } + logDone("run - cgroup parent") +} + +func TestRunContainerWithCgroupParentAbsPath(t *testing.T) { + testRequires(t, NativeExecDriver) + defer deleteAllContainers() + + cgroupParent := "/cgroup-parent/test" + + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "--cgroup-parent", cgroupParent, "--rm", "busybox", "cat", "/proc/self/cgroup")) + if err != nil { + t.Fatalf("unexpected failure when running container with --cgroup-parent option - %s\n%v", string(out), err) + } + cgroupPaths := getCgroupPaths(string(out)) + if len(cgroupPaths) == 0 { + t.Fatalf("unexpected output - %q", string(out)) + } + found := false + for _, path := range cgroupPaths { + if strings.HasPrefix(path, cgroupParent) { + found = true + break + } + } + if !found { + t.Fatalf("unexpected cgroup paths. Expected at least one cgroup path to have prefix %q. Cgroup Paths: %v", cgroupParent, cgroupPaths) + } + + logDone("run - cgroup parent with absolute cgroup path") +}