Преглед изворни кода

Merge pull request #10736 from coolljt0725/add_cpu_limit

Add support cpu cfs_quota
Jessie Frazelle пре 10 година
родитељ
комит
17d5450bc3

+ 1 - 0
api/types/types.go

@@ -132,6 +132,7 @@ type Info struct {
 	DriverStatus       [][2]string
 	MemoryLimit        bool
 	SwapLimit          bool
+	CpuCfsQuota        bool
 	IPv4Forwarding     bool
 	Debug              bool
 	NFd                int

+ 1 - 0
contrib/completion/bash/docker

@@ -770,6 +770,7 @@ _docker_run() {
 		--cidfile
 		--cpuset
 		--cpu-shares -c
+		--cpu-quota
 		--device
 		--dns
 		--dns-search

+ 1 - 0
daemon/container.go

@@ -356,6 +356,7 @@ func populateCommand(c *Container, env []string) error {
 		CpuShares:  c.hostConfig.CpuShares,
 		CpusetCpus: c.hostConfig.CpusetCpus,
 		CpusetMems: c.hostConfig.CpusetMems,
+		CpuQuota:   c.hostConfig.CpuQuota,
 		Rlimits:    rlimits,
 	}
 

+ 4 - 0
daemon/daemon.go

@@ -1250,6 +1250,10 @@ func (daemon *Daemon) verifyHostConfig(hostConfig *runconfig.HostConfig) ([]stri
 	if hostConfig.Memory == 0 && hostConfig.MemorySwap > 0 {
 		return warnings, fmt.Errorf("You should always set the Memory limit when using Memoryswap limit, see usage.")
 	}
+	if hostConfig.CpuQuota > 0 && !daemon.SystemConfig().CpuCfsQuota {
+		warnings = append(warnings, "Your kernel does not support CPU cfs quota. Quota discarded.")
+		hostConfig.CpuQuota = 0
+	}
 
 	return warnings, nil
 }

+ 2 - 0
daemon/execdriver/driver.go

@@ -111,6 +111,7 @@ type Resources struct {
 	CpuShares  int64            `json:"cpu_shares"`
 	CpusetCpus string           `json:"cpuset_cpus"`
 	CpusetMems string           `json:"cpuset_mems"`
+	CpuQuota   int64            `json:"cpu_quota"`
 	Rlimits    []*ulimit.Rlimit `json:"rlimits"`
 }
 
@@ -206,6 +207,7 @@ func SetupCgroups(container *configs.Config, c *Command) error {
 		container.Cgroups.MemorySwap = c.Resources.MemorySwap
 		container.Cgroups.CpusetCpus = c.Resources.CpusetCpus
 		container.Cgroups.CpusetMems = c.Resources.CpusetMems
+		container.Cgroups.CpuQuota = c.Resources.CpuQuota
 	}
 
 	return nil

+ 3 - 0
daemon/execdriver/lxc/lxc_template.go

@@ -113,6 +113,9 @@ lxc.cgroup.cpuset.cpus = {{.Resources.CpusetCpus}}
 {{if .Resources.CpusetMems}}
 lxc.cgroup.cpuset.mems = {{.Resources.CpusetMems}}
 {{end}}
+{{if .Resources.CpuQuota}}
+lxc.cgroup.cpu.cfs_quota_us = {{.Resources.CpuQuota}}
+{{end}}
 {{end}}
 
 {{if .LxcConfig}}

+ 1 - 0
daemon/info.go

@@ -60,6 +60,7 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
 		DriverStatus:       daemon.GraphDriver().Status(),
 		MemoryLimit:        daemon.SystemConfig().MemoryLimit,
 		SwapLimit:          daemon.SystemConfig().SwapLimit,
+		CpuCfsQuota:        daemon.SystemConfig().CpuCfsQuota,
 		IPv4Forwarding:     !daemon.SystemConfig().IPv4ForwardingDisabled,
 		Debug:              os.Getenv("DEBUG") != "",
 		NFd:                fileutils.GetTotalUsedFds(),

+ 4 - 0
docs/man/docker-create.1.md

@@ -14,6 +14,7 @@ docker-create - Create a new container
 [**--cidfile**[=*CIDFILE*]]
 [**--cpuset-cpus**[=*CPUSET-CPUS*]]
 [**--cpuset-mems**[=*CPUSET-MEMS*]]
+[**--cpu-quota**[=*0*]]
 [**--device**[=*[]*]]
 [**--dns-search**[=*[]*]]
 [**--dns**[=*[]*]]
@@ -82,6 +83,9 @@ IMAGE [COMMAND] [ARG...]
 then processes in your Docker container will only use memory from the first
 two memory nodes.
 
+**-cpu-quota**=0
+   Limit the CPU CFS (Completely Fair Scheduler) quota
+
 **--device**=[]
    Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc:rwm)
 

+ 8 - 0
docs/man/docker-run.1.md

@@ -15,6 +15,7 @@ docker-run - Run a command in a new container
 [**--cpuset-cpus**[=*CPUSET-CPUS*]]
 [**--cpuset-mems**[=*CPUSET-MEMS*]]
 [**-d**|**--detach**[=*false*]]
+[**--cpu-quota**[=*0*]]
 [**--device**[=*[]*]]
 [**--dns-search**[=*[]*]]
 [**--dns**[=*[]*]]
@@ -142,6 +143,13 @@ division of CPU shares:
 then processes in your Docker container will only use memory from the first
 two memory nodes.
 
+**--cpu-quota**=0
+   Limit the CPU CFS (Completely Fair Scheduler) quota
+
+   Limit the container's CPU usage. By default, containers run with the full
+CPU resource. This flag tell the kernel to restrict the container's CPU usage
+to the quota you specify.
+
 **-d**, **--detach**=*true*|*false*
    Detached mode: run the container in the background and print the new container ID. The default is *false*.
 

+ 2 - 0
docs/sources/reference/commandline/cli.md

@@ -925,6 +925,7 @@ Creates a new container.
       --cidfile=""               Write the container ID to the file
       --cpuset-cpus=""           CPUs in which to allow execution (0-3, 0,1)
       --cpuset-mems=""           Memory nodes (MEMs) in which to allow execution (0-3, 0,1)
+      --cpu-quota=0              Limit the CPU CFS (Completely Fair Scheduler) quota
       --device=[]                Add a host device to the container
       --dns=[]                   Set custom DNS servers
       --dns-search=[]            Set custom DNS search domains
@@ -1879,6 +1880,7 @@ To remove an image using its digest:
       --cidfile=""               Write the container ID to the file
       --cpuset-cpus=""           CPUs in which to allow execution (0-3, 0,1)
       --cpuset-mems=""           Memory nodes (MEMs) in which to allow execution (0-3, 0,1)
+      --cpu-quota=0              Limit the CPU CFS (Completely Fair Scheduler) quota
       -d, --detach=false         Run container in background and print container ID
       --device=[]                Add a host device to the container
       --dns=[]                   Set custom DNS servers

+ 10 - 0
docs/sources/reference/run.md

@@ -475,6 +475,7 @@ container:
     -c, --cpu-shares=0: CPU shares (relative weight)
     --cpuset-cpus="": CPUs in which to allow execution (0-3, 0,1)
     --cpuset-mems="": Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.
+    --cpu-quota=0: Limit the CPU CFS (Completely Fair Scheduler) quota
 
 ### Memory constraints
 
@@ -615,6 +616,15 @@ memory nodes 1 and 3.
 This example restricts the processes in the container to only use memory from
 memory nodes 0, 1 and 2.
 
+### CPU quota constraint
+
+The `--cpu-quota` flag limits the container's CPU usage. The default 0 value
+allows the container to take 100% of a CPU resource (1 CPU). The CFS (Completely Fair
+Scheduler) handles resource allocation for executing processes and is default
+Linux Scheduler used by the kernel. Set this value to 50000 to limit the container
+to 50% of a CPU resource. For multiple CPUs, adjust the `--cpu-quota` as necessary.
+For more information, see the [CFS documentation on bandwidth limiting](https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt).
+
 ## Runtime privilege, Linux capabilities, and LXC configuration
 
     --cap-add: Add Linux capabilities

+ 30 - 0
integration-cli/docker_cli_run_test.go

@@ -105,6 +105,36 @@ func TestRunEchoStdoutWithCPUAndMemoryLimit(t *testing.T) {
 	logDone("run - echo with CPU and memory limit")
 }
 
+// "test" should be printed
+func TestRunEchoStdoutWitCPUQuota(t *testing.T) {
+	defer deleteAllContainers()
+
+	runCmd := exec.Command(dockerBinary, "run", "--cpu-quota", "8000", "--name", "test", "busybox", "echo", "test")
+	out, _, _, err := runCommandWithStdoutStderr(runCmd)
+	if err != nil {
+		t.Fatalf("failed to run container: %v, output: %q", err, out)
+	}
+	out = strings.TrimSpace(out)
+	if strings.Contains(out, "Your kernel does not support CPU cfs quota") {
+		t.Skip("Your kernel does not support CPU cfs quota, skip this test")
+	}
+	if out != "test" {
+		t.Errorf("container should've printed 'test'")
+	}
+
+	cmd := exec.Command(dockerBinary, "inspect", "-f", "{{.HostConfig.CpuQuota}}", "test")
+	out, _, err = runCommandWithOutput(cmd)
+	if err != nil {
+		t.Fatalf("failed to inspect container: %s, %v", out, err)
+	}
+	out = strings.TrimSpace(out)
+	if out != "8000" {
+		t.Errorf("setting the CPU CFS quota failed")
+	}
+
+	logDone("run - echo with CPU quota")
+}
+
 // "test" should be printed
 func TestRunEchoNamedContainer(t *testing.T) {
 	defer deleteAllContainers()

+ 14 - 0
pkg/sysinfo/sysinfo.go

@@ -13,6 +13,7 @@ import (
 type SysInfo struct {
 	MemoryLimit            bool
 	SwapLimit              bool
+	CpuCfsQuota            bool
 	IPv4ForwardingDisabled bool
 	AppArmor               bool
 }
@@ -39,6 +40,19 @@ func New(quiet bool) *SysInfo {
 		}
 	}
 
+	if cgroupCpuMountpoint, err := cgroups.FindCgroupMountpoint("cpu"); err != nil {
+		if !quiet {
+			logrus.Warnf("WARING: %s\n", err)
+		}
+	} else {
+		_, err1 := ioutil.ReadFile(path.Join(cgroupCpuMountpoint, "cpu.cfs_quota_us"))
+		logrus.Warnf("%s", cgroupCpuMountpoint)
+		sysInfo.CpuCfsQuota = err1 == nil
+		if !sysInfo.CpuCfsQuota && !quiet {
+			logrus.Warnf("WARING: Your kernel does not support cgroup cfs quotas")
+		}
+	}
+
 	// Check if AppArmor is supported.
 	if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) {
 		sysInfo.AppArmor = false

+ 1 - 0
runconfig/hostconfig.go

@@ -167,6 +167,7 @@ type HostConfig struct {
 	CpuShares       int64  // CPU shares (relative weight vs. other containers)
 	CpusetCpus      string // CpusetCpus 0-2, 0,1
 	CpusetMems      string // CpusetMems 0-2, 0,1
+	CpuQuota        int64
 	Privileged      bool
 	PortBindings    nat.PortMap
 	Links           []string

+ 2 - 0
runconfig/parse.go

@@ -65,6 +65,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
 		flCpusetCpus      = cmd.String([]string{"#-cpuset", "-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)")
+		flCpuQuota        = cmd.Int64([]string{"-cpu-quota"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
 		flNetMode         = cmd.String([]string{"-net"}, "bridge", "Set the Network mode 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")
@@ -312,6 +313,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 		CpuShares:       *flCpuShares,
 		CpusetCpus:      *flCpusetCpus,
 		CpusetMems:      *flCpusetMems,
+		CpuQuota:        *flCpuQuota,
 		Privileged:      *flPrivileged,
 		PortBindings:    portBindings,
 		Links:           flLinks.GetAll(),