This fix tries to address the issue raised in 31032 where it was
not possible to specify `--cpus` for `docker update`.
This fix adds `--cpus` support for `docker update`. In case both
`--cpus` and `--cpu-period/--cpu-quota` have been specified,
an error will be returned.
Related docs has been updated.
Integration tests have been added.
This fix fixes 31032.
This fix is related to 27921, 27958.
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
| ... | ... |
@@ -28,6 +28,7 @@ type updateOptions struct {
|
| 28 | 28 |
memorySwap opts.MemSwapBytes |
| 29 | 29 |
kernelMemory opts.MemBytes |
| 30 | 30 |
restartPolicy string |
| 31 |
+ cpus opts.NanoCPUs |
|
| 31 | 32 |
|
| 32 | 33 |
nFlag int |
| 33 | 34 |
|
| ... | ... |
@@ -66,6 +67,9 @@ func NewUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
| 66 | 66 |
flags.Var(&opts.kernelMemory, "kernel-memory", "Kernel memory limit") |
| 67 | 67 |
flags.StringVar(&opts.restartPolicy, "restart", "", "Restart policy to apply when a container exits") |
| 68 | 68 |
|
| 69 |
+ flags.Var(&opts.cpus, "cpus", "Number of CPUs") |
|
| 70 |
+ flags.SetAnnotation("cpus", "version", []string{"1.29"})
|
|
| 71 |
+ |
|
| 69 | 72 |
return cmd |
| 70 | 73 |
} |
| 71 | 74 |
|
| ... | ... |
@@ -97,6 +101,7 @@ func runUpdate(dockerCli *command.DockerCli, opts *updateOptions) error {
|
| 97 | 97 |
CPUQuota: opts.cpuQuota, |
| 98 | 98 |
CPURealtimePeriod: opts.cpuRealtimePeriod, |
| 99 | 99 |
CPURealtimeRuntime: opts.cpuRealtimeRuntime, |
| 100 |
+ NanoCPUs: opts.cpus.Value(), |
|
| 100 | 101 |
} |
| 101 | 102 |
|
| 102 | 103 |
updateConfig := containertypes.UpdateConfig{
|
| ... | ... |
@@ -286,12 +286,33 @@ func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfi |
| 286 | 286 |
// update resources of container |
| 287 | 287 |
resources := hostConfig.Resources |
| 288 | 288 |
cResources := &container.HostConfig.Resources |
| 289 |
+ |
|
| 290 |
+ // validate NanoCPUs, CPUPeriod, and CPUQuota |
|
| 291 |
+ // Becuase NanoCPU effectively updates CPUPeriod/CPUQuota, |
|
| 292 |
+ // once NanoCPU is already set, updating CPUPeriod/CPUQuota will be blocked, and vice versa. |
|
| 293 |
+ // In the following we make sure the intended update (resources) does not conflict with the existing (cResource). |
|
| 294 |
+ if resources.NanoCPUs > 0 && cResources.CPUPeriod > 0 {
|
|
| 295 |
+ return fmt.Errorf("Conflicting options: Nano CPUs cannot be updated as CPU Period has already been set")
|
|
| 296 |
+ } |
|
| 297 |
+ if resources.NanoCPUs > 0 && cResources.CPUQuota > 0 {
|
|
| 298 |
+ return fmt.Errorf("Conflicting options: Nano CPUs cannot be updated as CPU Quota has already been set")
|
|
| 299 |
+ } |
|
| 300 |
+ if resources.CPUPeriod > 0 && cResources.NanoCPUs > 0 {
|
|
| 301 |
+ return fmt.Errorf("Conflicting options: CPU Period cannot be updated as NanoCPUs has already been set")
|
|
| 302 |
+ } |
|
| 303 |
+ if resources.CPUQuota > 0 && cResources.NanoCPUs > 0 {
|
|
| 304 |
+ return fmt.Errorf("Conflicting options: CPU Quota cannot be updated as NanoCPUs has already been set")
|
|
| 305 |
+ } |
|
| 306 |
+ |
|
| 289 | 307 |
if resources.BlkioWeight != 0 {
|
| 290 | 308 |
cResources.BlkioWeight = resources.BlkioWeight |
| 291 | 309 |
} |
| 292 | 310 |
if resources.CPUShares != 0 {
|
| 293 | 311 |
cResources.CPUShares = resources.CPUShares |
| 294 | 312 |
} |
| 313 |
+ if resources.NanoCPUs != 0 {
|
|
| 314 |
+ cResources.NanoCPUs = resources.NanoCPUs |
|
| 315 |
+ } |
|
| 295 | 316 |
if resources.CPUPeriod != 0 {
|
| 296 | 317 |
cResources.CPUPeriod = resources.CPUPeriod |
| 297 | 318 |
} |
| ... | ... |
@@ -3,6 +3,8 @@ |
| 3 | 3 |
package daemon |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
+ "time" |
|
| 7 |
+ |
|
| 6 | 8 |
"github.com/docker/docker/api/types/container" |
| 7 | 9 |
"github.com/docker/docker/libcontainerd" |
| 8 | 10 |
) |
| ... | ... |
@@ -11,8 +13,13 @@ func toContainerdResources(resources container.Resources) libcontainerd.Resource |
| 11 | 11 |
var r libcontainerd.Resources |
| 12 | 12 |
r.BlkioWeight = uint64(resources.BlkioWeight) |
| 13 | 13 |
r.CpuShares = uint64(resources.CPUShares) |
| 14 |
- r.CpuPeriod = uint64(resources.CPUPeriod) |
|
| 15 |
- r.CpuQuota = uint64(resources.CPUQuota) |
|
| 14 |
+ if resources.NanoCPUs != 0 {
|
|
| 15 |
+ r.CpuPeriod = uint64(100 * time.Millisecond / time.Microsecond) |
|
| 16 |
+ r.CpuQuota = uint64(resources.NanoCPUs) * r.CpuPeriod / 1e9 |
|
| 17 |
+ } else {
|
|
| 18 |
+ r.CpuPeriod = uint64(resources.CPUPeriod) |
|
| 19 |
+ r.CpuQuota = uint64(resources.CPUQuota) |
|
| 20 |
+ } |
|
| 16 | 21 |
r.CpusetCpus = resources.CpusetCpus |
| 17 | 22 |
r.CpusetMems = resources.CpusetMems |
| 18 | 23 |
r.MemoryLimit = uint64(resources.Memory) |
| ... | ... |
@@ -39,6 +39,7 @@ keywords: "API, Docker, rcli, REST, documentation" |
| 39 | 39 |
* `POST /services/create` and `POST /services/(id or name)/update` now accept a `rollback` value for `FailureAction`. |
| 40 | 40 |
* `POST /services/create` and `POST /services/(id or name)/update` now accept an optional `RollbackConfig` object which specifies rollback options. |
| 41 | 41 |
* `GET /services` now supports a `mode` filter to filter services based on the service mode (either `global` or `replicated`). |
| 42 |
+* `POST /containers/(name)/update` now supports updating `NanoCPUs` that represents CPU quota in units of 10<sup>-9</sup> CPUs. |
|
| 42 | 43 |
|
| 43 | 44 |
## v1.27 API changes |
| 44 | 45 |
|
| ... | ... |
@@ -21,12 +21,13 @@ Usage: docker update [OPTIONS] CONTAINER [CONTAINER...] |
| 21 | 21 |
Update configuration of one or more containers |
| 22 | 22 |
|
| 23 | 23 |
Options: |
| 24 |
- --blkio-weight value Block IO (relative weight), between 10 and 1000 |
|
| 24 |
+ --blkio-weight uint16 Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0) |
|
| 25 | 25 |
--cpu-period int Limit CPU CFS (Completely Fair Scheduler) period |
| 26 | 26 |
--cpu-quota int Limit CPU CFS (Completely Fair Scheduler) quota |
| 27 |
- -c, --cpu-shares int CPU shares (relative weight) |
|
| 28 | 27 |
--cpu-rt-period int Limit the CPU real-time period in microseconds |
| 29 | 28 |
--cpu-rt-runtime int Limit the CPU real-time runtime in microseconds |
| 29 |
+ -c, --cpu-shares int CPU shares (relative weight) |
|
| 30 |
+ --cpus decimal Number of CPUs (default 0.000) |
|
| 30 | 31 |
--cpuset-cpus string CPUs in which to allow execution (0-3, 0,1) |
| 31 | 32 |
--cpuset-mems string MEMs in which to allow execution (0-3, 0,1) |
| 32 | 33 |
--help Print usage |
| ... | ... |
@@ -282,3 +282,38 @@ func (s *DockerSuite) TestUpdateNotAffectMonitorRestartPolicy(c *check.C) {
|
| 282 | 282 |
c.Assert(err, checker.IsNil) |
| 283 | 283 |
c.Assert(waitRun(id), checker.IsNil) |
| 284 | 284 |
} |
| 285 |
+ |
|
| 286 |
+func (s *DockerSuite) TestUpdateWithNanoCPUs(c *check.C) {
|
|
| 287 |
+ testRequires(c, cpuCfsQuota, cpuCfsPeriod) |
|
| 288 |
+ |
|
| 289 |
+ file1 := "/sys/fs/cgroup/cpu/cpu.cfs_quota_us" |
|
| 290 |
+ file2 := "/sys/fs/cgroup/cpu/cpu.cfs_period_us" |
|
| 291 |
+ |
|
| 292 |
+ out, _ := dockerCmd(c, "run", "-d", "--cpus", "0.5", "--name", "top", "busybox", "top") |
|
| 293 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") |
|
| 294 |
+ |
|
| 295 |
+ out, _ = dockerCmd(c, "exec", "top", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2))
|
|
| 296 |
+ c.Assert(strings.TrimSpace(out), checker.Equals, "50000\n100000") |
|
| 297 |
+ |
|
| 298 |
+ out = inspectField(c, "top", "HostConfig.NanoCpus") |
|
| 299 |
+ c.Assert(out, checker.Equals, "5e+08", check.Commentf("setting the Nano CPUs failed"))
|
|
| 300 |
+ out = inspectField(c, "top", "HostConfig.CpuQuota") |
|
| 301 |
+ c.Assert(out, checker.Equals, "0", check.Commentf("CPU CFS quota should be 0"))
|
|
| 302 |
+ out = inspectField(c, "top", "HostConfig.CpuPeriod") |
|
| 303 |
+ c.Assert(out, checker.Equals, "0", check.Commentf("CPU CFS period should be 0"))
|
|
| 304 |
+ |
|
| 305 |
+ out, _, err := dockerCmdWithError("update", "--cpu-quota", "80000", "top")
|
|
| 306 |
+ c.Assert(err, checker.NotNil) |
|
| 307 |
+ c.Assert(out, checker.Contains, "Conflicting options: CPU Quota cannot be updated as NanoCPUs has already been set") |
|
| 308 |
+ |
|
| 309 |
+ out, _ = dockerCmd(c, "update", "--cpus", "0.8", "top") |
|
| 310 |
+ out = inspectField(c, "top", "HostConfig.NanoCpus") |
|
| 311 |
+ c.Assert(out, checker.Equals, "8e+08", check.Commentf("updating the Nano CPUs failed"))
|
|
| 312 |
+ out = inspectField(c, "top", "HostConfig.CpuQuota") |
|
| 313 |
+ c.Assert(out, checker.Equals, "0", check.Commentf("CPU CFS quota should be 0"))
|
|
| 314 |
+ out = inspectField(c, "top", "HostConfig.CpuPeriod") |
|
| 315 |
+ c.Assert(out, checker.Equals, "0", check.Commentf("CPU CFS period should be 0"))
|
|
| 316 |
+ |
|
| 317 |
+ out, _ = dockerCmd(c, "exec", "top", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2))
|
|
| 318 |
+ c.Assert(strings.TrimSpace(out), checker.Equals, "80000\n100000") |
|
| 319 |
+} |