Signed-off-by: Lei Jitang <leijitang@huawei.com>
| ... | ... |
@@ -356,6 +356,7 @@ func populateCommand(c *Container, env []string) error {
|
| 356 | 356 |
CpuShares: c.hostConfig.CpuShares, |
| 357 | 357 |
CpusetCpus: c.hostConfig.CpusetCpus, |
| 358 | 358 |
CpusetMems: c.hostConfig.CpusetMems, |
| 359 |
+ CpuQuota: c.hostConfig.CpuQuota, |
|
| 359 | 360 |
Rlimits: rlimits, |
| 360 | 361 |
} |
| 361 | 362 |
|
| ... | ... |
@@ -1246,6 +1246,10 @@ func (daemon *Daemon) verifyHostConfig(hostConfig *runconfig.HostConfig) ([]stri |
| 1246 | 1246 |
if hostConfig.Memory == 0 && hostConfig.MemorySwap > 0 {
|
| 1247 | 1247 |
return warnings, fmt.Errorf("You should always set the Memory limit when using Memoryswap limit, see usage.\n")
|
| 1248 | 1248 |
} |
| 1249 |
+ if hostConfig.CpuQuota > 0 && !daemon.SystemConfig().CpuCfsQuota {
|
|
| 1250 |
+ warnings = append(warnings, "Your kernel does not support CPU cfs quota. Quota discarded.") |
|
| 1251 |
+ hostConfig.CpuQuota = 0 |
|
| 1252 |
+ } |
|
| 1249 | 1253 |
|
| 1250 | 1254 |
return warnings, nil |
| 1251 | 1255 |
} |
| ... | ... |
@@ -111,6 +111,7 @@ type Resources struct {
|
| 111 | 111 |
CpuShares int64 `json:"cpu_shares"` |
| 112 | 112 |
CpusetCpus string `json:"cpuset_cpus"` |
| 113 | 113 |
CpusetMems string `json:"cpuset_mems"` |
| 114 |
+ CpuQuota int64 `json:"cpu_quota"` |
|
| 114 | 115 |
Rlimits []*ulimit.Rlimit `json:"rlimits"` |
| 115 | 116 |
} |
| 116 | 117 |
|
| ... | ... |
@@ -206,6 +207,7 @@ func SetupCgroups(container *configs.Config, c *Command) error {
|
| 206 | 206 |
container.Cgroups.MemorySwap = c.Resources.MemorySwap |
| 207 | 207 |
container.Cgroups.CpusetCpus = c.Resources.CpusetCpus |
| 208 | 208 |
container.Cgroups.CpusetMems = c.Resources.CpusetMems |
| 209 |
+ container.Cgroups.CpuQuota = c.Resources.CpuQuota |
|
| 209 | 210 |
} |
| 210 | 211 |
|
| 211 | 212 |
return nil |
| ... | ... |
@@ -113,6 +113,9 @@ lxc.cgroup.cpuset.cpus = {{.Resources.CpusetCpus}}
|
| 113 | 113 |
{{if .Resources.CpusetMems}}
|
| 114 | 114 |
lxc.cgroup.cpuset.mems = {{.Resources.CpusetMems}}
|
| 115 | 115 |
{{end}}
|
| 116 |
+{{if .Resources.CpuQuota}}
|
|
| 117 |
+lxc.cgroup.cpu.cfs_quota_us = {{.Resources.CpuQuota}}
|
|
| 118 |
+{{end}}
|
|
| 116 | 119 |
{{end}}
|
| 117 | 120 |
|
| 118 | 121 |
{{if .LxcConfig}}
|
| ... | ... |
@@ -60,6 +60,7 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
|
| 60 | 60 |
DriverStatus: daemon.GraphDriver().Status(), |
| 61 | 61 |
MemoryLimit: daemon.SystemConfig().MemoryLimit, |
| 62 | 62 |
SwapLimit: daemon.SystemConfig().SwapLimit, |
| 63 |
+ CpuCfsQuota: daemon.SystemConfig().CpuCfsQuota, |
|
| 63 | 64 |
IPv4Forwarding: !daemon.SystemConfig().IPv4ForwardingDisabled, |
| 64 | 65 |
Debug: os.Getenv("DEBUG") != "",
|
| 65 | 66 |
NFd: fileutils.GetTotalUsedFds(), |
| ... | ... |
@@ -14,6 +14,7 @@ docker-create - Create a new container |
| 14 | 14 |
[**--cidfile**[=*CIDFILE*]] |
| 15 | 15 |
[**--cpuset-cpus**[=*CPUSET-CPUS*]] |
| 16 | 16 |
[**--cpuset-mems**[=*CPUSET-MEMS*]] |
| 17 |
+[**--cpu-quota**[=*0*]] |
|
| 17 | 18 |
[**--device**[=*[]*]] |
| 18 | 19 |
[**--dns-search**[=*[]*]] |
| 19 | 20 |
[**--dns**[=*[]*]] |
| ... | ... |
@@ -82,6 +83,9 @@ IMAGE [COMMAND] [ARG...] |
| 82 | 82 |
then processes in your Docker container will only use memory from the first |
| 83 | 83 |
two memory nodes. |
| 84 | 84 |
|
| 85 |
+**-cpu-quota**=0 |
|
| 86 |
+ Limit the CPU CFS (Completely Fair Scheduler) quota |
|
| 87 |
+ |
|
| 85 | 88 |
**--device**=[] |
| 86 | 89 |
Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc:rwm) |
| 87 | 90 |
|
| ... | ... |
@@ -15,6 +15,7 @@ docker-run - Run a command in a new container |
| 15 | 15 |
[**--cpuset-cpus**[=*CPUSET-CPUS*]] |
| 16 | 16 |
[**--cpuset-mems**[=*CPUSET-MEMS*]] |
| 17 | 17 |
[**-d**|**--detach**[=*false*]] |
| 18 |
+[**--cpu-quota**[=*0*]] |
|
| 18 | 19 |
[**--device**[=*[]*]] |
| 19 | 20 |
[**--dns-search**[=*[]*]] |
| 20 | 21 |
[**--dns**[=*[]*]] |
| ... | ... |
@@ -142,6 +143,13 @@ division of CPU shares: |
| 142 | 142 |
then processes in your Docker container will only use memory from the first |
| 143 | 143 |
two memory nodes. |
| 144 | 144 |
|
| 145 |
+**--cpu-quota**=0 |
|
| 146 |
+ Limit the CPU CFS (Completely Fair Scheduler) quota |
|
| 147 |
+ |
|
| 148 |
+ Limit the container's CPU usage. By default, containers run with the full |
|
| 149 |
+CPU resource. This flag tell the kernel to restrict the container's CPU usage |
|
| 150 |
+to the quota you specify. |
|
| 151 |
+ |
|
| 145 | 152 |
**-d**, **--detach**=*true*|*false* |
| 146 | 153 |
Detached mode: run the container in the background and print the new container ID. The default is *false*. |
| 147 | 154 |
|
| ... | ... |
@@ -894,6 +894,7 @@ Creates a new container. |
| 894 | 894 |
--cidfile="" Write the container ID to the file |
| 895 | 895 |
--cpuset-cpus="" CPUs in which to allow execution (0-3, 0,1) |
| 896 | 896 |
--cpuset-mems="" Memory nodes (MEMs) in which to allow execution (0-3, 0,1) |
| 897 |
+ --cpu-quota=0 Limit the CPU CFS (Completely Fair Scheduler) quota |
|
| 897 | 898 |
--device=[] Add a host device to the container |
| 898 | 899 |
--dns=[] Set custom DNS servers |
| 899 | 900 |
--dns-search=[] Set custom DNS search domains |
| ... | ... |
@@ -1847,6 +1848,7 @@ To remove an image using its digest: |
| 1847 | 1847 |
--cidfile="" Write the container ID to the file |
| 1848 | 1848 |
--cpuset-cpus="" CPUs in which to allow execution (0-3, 0,1) |
| 1849 | 1849 |
--cpuset-mems="" Memory nodes (MEMs) in which to allow execution (0-3, 0,1) |
| 1850 |
+ --cpu-quota=0 Limit the CPU CFS (Completely Fair Scheduler) quota |
|
| 1850 | 1851 |
-d, --detach=false Run container in background and print container ID |
| 1851 | 1852 |
--device=[] Add a host device to the container |
| 1852 | 1853 |
--dns=[] Set custom DNS servers |
| ... | ... |
@@ -475,6 +475,7 @@ container: |
| 475 | 475 |
-c, --cpu-shares=0: CPU shares (relative weight) |
| 476 | 476 |
--cpuset-cpus="": CPUs in which to allow execution (0-3, 0,1) |
| 477 | 477 |
--cpuset-mems="": Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems. |
| 478 |
+ --cpu-quota=0: Limit the CPU CFS (Completely Fair Scheduler) quota |
|
| 478 | 479 |
|
| 479 | 480 |
### Memory constraints |
| 480 | 481 |
|
| ... | ... |
@@ -615,6 +616,15 @@ memory nodes 1 and 3. |
| 615 | 615 |
This example restricts the processes in the container to only use memory from |
| 616 | 616 |
memory nodes 0, 1 and 2. |
| 617 | 617 |
|
| 618 |
+### CPU quota constraint |
|
| 619 |
+ |
|
| 620 |
+The `--cpu-quota` flag limits the container's CPU usage. The default 0 value |
|
| 621 |
+allows the container to take 100% of a CPU resource (1 CPU). The CFS (Completely Fair |
|
| 622 |
+Scheduler) handles resource allocation for executing processes and is default |
|
| 623 |
+Linux Scheduler used by the kernel. Set this value to 50000 to limit the container |
|
| 624 |
+to 50% of a CPU resource. For multiple CPUs, adjust the `--cpu-quota` as necessary. |
|
| 625 |
+For more information, see the [CFS documentation on bandwidth limiting](https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt). |
|
| 626 |
+ |
|
| 618 | 627 |
## Runtime privilege, Linux capabilities, and LXC configuration |
| 619 | 628 |
|
| 620 | 629 |
--cap-add: Add Linux capabilities |
| ... | ... |
@@ -106,6 +106,36 @@ func TestRunEchoStdoutWithCPUAndMemoryLimit(t *testing.T) {
|
| 106 | 106 |
} |
| 107 | 107 |
|
| 108 | 108 |
// "test" should be printed |
| 109 |
+func TestRunEchoStdoutWitCPUQuota(t *testing.T) {
|
|
| 110 |
+ defer deleteAllContainers() |
|
| 111 |
+ |
|
| 112 |
+ runCmd := exec.Command(dockerBinary, "run", "--cpu-quota", "8000", "--name", "test", "busybox", "echo", "test") |
|
| 113 |
+ out, _, _, err := runCommandWithStdoutStderr(runCmd) |
|
| 114 |
+ if err != nil {
|
|
| 115 |
+ t.Fatalf("failed to run container: %v, output: %q", err, out)
|
|
| 116 |
+ } |
|
| 117 |
+ out = strings.TrimSpace(out) |
|
| 118 |
+ if strings.Contains(out, "Your kernel does not support CPU cfs quota") {
|
|
| 119 |
+ t.Skip("Your kernel does not support CPU cfs quota, skip this test")
|
|
| 120 |
+ } |
|
| 121 |
+ if out != "test" {
|
|
| 122 |
+ t.Errorf("container should've printed 'test'")
|
|
| 123 |
+ } |
|
| 124 |
+ |
|
| 125 |
+ cmd := exec.Command(dockerBinary, "inspect", "-f", "{{.HostConfig.CpuQuota}}", "test")
|
|
| 126 |
+ out, _, err = runCommandWithOutput(cmd) |
|
| 127 |
+ if err != nil {
|
|
| 128 |
+ t.Fatalf("failed to inspect container: %s, %v", out, err)
|
|
| 129 |
+ } |
|
| 130 |
+ out = strings.TrimSpace(out) |
|
| 131 |
+ if out != "8000" {
|
|
| 132 |
+ t.Errorf("setting the CPU CFS quota failed")
|
|
| 133 |
+ } |
|
| 134 |
+ |
|
| 135 |
+ logDone("run - echo with CPU quota")
|
|
| 136 |
+} |
|
| 137 |
+ |
|
| 138 |
+// "test" should be printed |
|
| 109 | 139 |
func TestRunEchoNamedContainer(t *testing.T) {
|
| 110 | 140 |
defer deleteAllContainers() |
| 111 | 141 |
|
| ... | ... |
@@ -13,6 +13,7 @@ import ( |
| 13 | 13 |
type SysInfo struct {
|
| 14 | 14 |
MemoryLimit bool |
| 15 | 15 |
SwapLimit bool |
| 16 |
+ CpuCfsQuota bool |
|
| 16 | 17 |
IPv4ForwardingDisabled bool |
| 17 | 18 |
AppArmor bool |
| 18 | 19 |
} |
| ... | ... |
@@ -39,6 +40,19 @@ func New(quiet bool) *SysInfo {
|
| 39 | 39 |
} |
| 40 | 40 |
} |
| 41 | 41 |
|
| 42 |
+ if cgroupCpuMountpoint, err := cgroups.FindCgroupMountpoint("cpu"); err != nil {
|
|
| 43 |
+ if !quiet {
|
|
| 44 |
+ logrus.Warnf("WARING: %s\n", err)
|
|
| 45 |
+ } |
|
| 46 |
+ } else {
|
|
| 47 |
+ _, err1 := ioutil.ReadFile(path.Join(cgroupCpuMountpoint, "cpu.cfs_quota_us")) |
|
| 48 |
+ logrus.Warnf("%s", cgroupCpuMountpoint)
|
|
| 49 |
+ sysInfo.CpuCfsQuota = err1 == nil |
|
| 50 |
+ if !sysInfo.CpuCfsQuota && !quiet {
|
|
| 51 |
+ logrus.Warnf("WARING: Your kernel does not support cgroup cfs quotas")
|
|
| 52 |
+ } |
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 42 | 55 |
// Check if AppArmor is supported. |
| 43 | 56 |
if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) {
|
| 44 | 57 |
sysInfo.AppArmor = false |
| ... | ... |
@@ -167,6 +167,7 @@ type HostConfig struct {
|
| 167 | 167 |
CpuShares int64 // CPU shares (relative weight vs. other containers) |
| 168 | 168 |
CpusetCpus string // CpusetCpus 0-2, 0,1 |
| 169 | 169 |
CpusetMems string // CpusetMems 0-2, 0,1 |
| 170 |
+ CpuQuota int64 |
|
| 170 | 171 |
Privileged bool |
| 171 | 172 |
PortBindings nat.PortMap |
| 172 | 173 |
Links []string |
| ... | ... |
@@ -65,6 +65,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe |
| 65 | 65 |
flCpuShares = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
|
| 66 | 66 |
flCpusetCpus = cmd.String([]string{"#-cpuset", "-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)")
|
| 67 | 67 |
flCpusetMems = cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)")
|
| 68 |
+ flCpuQuota = cmd.Int64([]string{"-cpu-quota"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
|
|
| 68 | 69 |
flNetMode = cmd.String([]string{"-net"}, "bridge", "Set the Network mode for the container")
|
| 69 | 70 |
flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)")
|
| 70 | 71 |
flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use")
|
| ... | ... |
@@ -312,6 +313,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe |
| 312 | 312 |
CpuShares: *flCpuShares, |
| 313 | 313 |
CpusetCpus: *flCpusetCpus, |
| 314 | 314 |
CpusetMems: *flCpusetMems, |
| 315 |
+ CpuQuota: *flCpuQuota, |
|
| 315 | 316 |
Privileged: *flPrivileged, |
| 316 | 317 |
PortBindings: portBindings, |
| 317 | 318 |
Links: flLinks.GetAll(), |