Implemet docker update command

It's used for updating properties of one or more containers, we only
support resource configs for now. It can be extended in the future.

Signed-off-by: Qiang Huang <h.huangqiang@huawei.com>
This commit is contained in:
Qiang Huang 2015-12-28 19:19:26 +08:00
parent b0be88c111
commit 8799c4fc0f
22 changed files with 728 additions and 2 deletions

View file

@ -43,6 +43,7 @@ type apiClient interface {
ContainerStop(containerID string, timeout int) error ContainerStop(containerID string, timeout int) error
ContainerTop(containerID string, arguments []string) (types.ContainerProcessList, error) ContainerTop(containerID string, arguments []string) (types.ContainerProcessList, error)
ContainerUnpause(containerID string) error ContainerUnpause(containerID string) error
ContainerUpdate(containerID string, hostConfig container.HostConfig) error
ContainerWait(containerID string) (int, error) ContainerWait(containerID string) (int, error)
CopyFromContainer(containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) CopyFromContainer(containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
CopyToContainer(options types.CopyToContainerOptions) error CopyToContainer(options types.CopyToContainerOptions) error

View file

@ -0,0 +1,12 @@
package lib
import (
"github.com/docker/docker/api/types/container"
)
// ContainerUpdate updates resources of a container
func (cli *Client) ContainerUpdate(containerID string, hostConfig container.HostConfig) error {
resp, err := cli.post("/containers/"+containerID+"/update", nil, hostConfig, nil)
ensureReaderClosed(resp)
return err
}

104
api/client/update.go Normal file
View file

@ -0,0 +1,104 @@
package client
import (
"fmt"
"github.com/docker/docker/api/types/container"
Cli "github.com/docker/docker/cli"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/go-units"
)
// CmdUpdate updates resources of one or more containers.
//
// Usage: docker update [OPTIONS] CONTAINER [CONTAINER...]
func (cli *DockerCli) CmdUpdate(args ...string) error {
cmd := Cli.Subcmd("update", []string{"CONTAINER [CONTAINER...]"}, Cli.DockerCommands["update"].Description, true)
flBlkioWeight := cmd.Uint16([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000")
flCPUPeriod := cmd.Int64([]string{"-cpu-period"}, 0, "Limit CPU CFS (Completely Fair Scheduler) period")
flCPUQuota := cmd.Int64([]string{"-cpu-quota"}, 0, "Limit CPU CFS (Completely Fair Scheduler) quota")
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)")
flCPUShares := cmd.Int64([]string{"#c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
flMemoryString := cmd.String([]string{"m", "-memory"}, "", "Memory limit")
flMemoryReservation := cmd.String([]string{"-memory-reservation"}, "", "Memory soft limit")
flMemorySwap := cmd.String([]string{"-memory-swap"}, "", "Total memory (memory + swap), '-1' to disable swap")
flKernelMemory := cmd.String([]string{"-kernel-memory"}, "", "Kernel memory limit")
cmd.Require(flag.Min, 1)
cmd.ParseFlags(args, true)
if cmd.NFlag() == 0 {
return fmt.Errorf("You must provide one or more flags when using this command.")
}
var err error
var flMemory int64
if *flMemoryString != "" {
flMemory, err = units.RAMInBytes(*flMemoryString)
if err != nil {
return err
}
}
var memoryReservation int64
if *flMemoryReservation != "" {
memoryReservation, err = units.RAMInBytes(*flMemoryReservation)
if err != nil {
return err
}
}
var memorySwap int64
if *flMemorySwap != "" {
if *flMemorySwap == "-1" {
memorySwap = -1
} else {
memorySwap, err = units.RAMInBytes(*flMemorySwap)
if err != nil {
return err
}
}
}
var kernelMemory int64
if *flKernelMemory != "" {
kernelMemory, err = units.RAMInBytes(*flKernelMemory)
if err != nil {
return err
}
}
resources := container.Resources{
BlkioWeight: *flBlkioWeight,
CpusetCpus: *flCpusetCpus,
CpusetMems: *flCpusetMems,
CPUShares: *flCPUShares,
Memory: flMemory,
MemoryReservation: memoryReservation,
MemorySwap: memorySwap,
KernelMemory: kernelMemory,
CPUPeriod: *flCPUPeriod,
CPUQuota: *flCPUQuota,
}
hostConfig := container.HostConfig{
Resources: resources,
}
names := cmd.Args()
var errNames []string
for _, name := range names {
if err := cli.client.ContainerUpdate(name, hostConfig); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
errNames = append(errNames, name)
} else {
fmt.Fprintf(cli.out, "%s\n", name)
}
}
if len(errNames) > 0 {
return fmt.Errorf("Error: failed to update resources of containers: %v", errNames)
}
return nil
}

View file

@ -42,6 +42,7 @@ type stateBackend interface {
ContainerStart(name string, hostConfig *container.HostConfig) error ContainerStart(name string, hostConfig *container.HostConfig) error
ContainerStop(name string, seconds int) error ContainerStop(name string, seconds int) error
ContainerUnpause(name string) error ContainerUnpause(name string) error
ContainerUpdate(name string, hostConfig *container.HostConfig) ([]string, error)
ContainerWait(name string, timeout time.Duration) (int, error) ContainerWait(name string, timeout time.Duration) (int, error)
Exists(id string) bool Exists(id string) bool
IsPaused(id string) bool IsPaused(id string) bool

View file

@ -57,6 +57,7 @@ func (r *containerRouter) initRoutes() {
local.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart), local.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
local.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize), local.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
local.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename), local.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
local.NewPostRoute("/containers/{name:.*}/update", r.postContainerUpdate),
// PUT // PUT
local.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive), local.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
// DELETE // DELETE

View file

@ -323,6 +323,30 @@ func (s *containerRouter) postContainerRename(ctx context.Context, w http.Respon
return nil return nil
} }
func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
if err := httputils.CheckForJSON(r); err != nil {
return err
}
_, hostConfig, err := runconfig.DecodeContainerConfig(r.Body)
if err != nil {
return err
}
name := vars["name"]
warnings, err := s.backend.ContainerUpdate(name, hostConfig)
if err != nil {
return err
}
return httputils.WriteJSON(w, http.StatusOK, &types.ContainerUpdateResponse{
Warnings: warnings,
})
}
func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil { if err := httputils.ParseForm(r); err != nil {
return err return err

View file

@ -28,6 +28,13 @@ type ContainerExecCreateResponse struct {
ID string `json:"Id"` ID string `json:"Id"`
} }
// ContainerUpdateResponse contains response of Remote API:
// POST /containers/{name:.*}/update
type ContainerUpdateResponse struct {
// Warnings are any warnings encountered during the updating of the container.
Warnings []string `json:"Warnings"`
}
// AuthResponse contains response of Remote API: // AuthResponse contains response of Remote API:
// POST "/auth" // POST "/auth"
type AuthResponse struct { type AuthResponse struct {

View file

@ -64,6 +64,7 @@ var dockerCommands = []Command{
{"tag", "Tag an image into a repository"}, {"tag", "Tag an image into a repository"},
{"top", "Display the running processes of a container"}, {"top", "Display the running processes of a container"},
{"unpause", "Unpause all processes within a container"}, {"unpause", "Unpause all processes within a container"},
{"update", "Update resources of one or more containers"},
{"version", "Show the Docker version information"}, {"version", "Show the Docker version information"},
{"volume", "Manage Docker volumes"}, {"volume", "Manage Docker volumes"},
{"wait", "Block until a container stops, then print its exit code"}, {"wait", "Block until a container stops, then print its exit code"},

View file

@ -13,6 +13,7 @@ import (
"syscall" "syscall"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
"github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/daemon/execdriver"
derr "github.com/docker/docker/errors" derr "github.com/docker/docker/errors"
@ -543,6 +544,75 @@ func (container *Container) IpcMounts() []execdriver.Mount {
return mounts return mounts
} }
func updateCommand(c *execdriver.Command, resources container.Resources) {
c.Resources.BlkioWeight = resources.BlkioWeight
c.Resources.CPUShares = resources.CPUShares
c.Resources.CPUPeriod = resources.CPUPeriod
c.Resources.CPUQuota = resources.CPUQuota
c.Resources.CpusetCpus = resources.CpusetCpus
c.Resources.CpusetMems = resources.CpusetMems
c.Resources.Memory = resources.Memory
c.Resources.MemorySwap = resources.MemorySwap
c.Resources.MemoryReservation = resources.MemoryReservation
c.Resources.KernelMemory = resources.KernelMemory
}
// UpdateContainer updates resources of a container.
func (container *Container) UpdateContainer(hostConfig *container.HostConfig) error {
container.Lock()
resources := hostConfig.Resources
cResources := &container.HostConfig.Resources
if resources.BlkioWeight != 0 {
cResources.BlkioWeight = resources.BlkioWeight
}
if resources.CPUShares != 0 {
cResources.CPUShares = resources.CPUShares
}
if resources.CPUPeriod != 0 {
cResources.CPUPeriod = resources.CPUPeriod
}
if resources.CPUQuota != 0 {
cResources.CPUQuota = resources.CPUQuota
}
if resources.CpusetCpus != "" {
cResources.CpusetCpus = resources.CpusetCpus
}
if resources.CpusetMems != "" {
cResources.CpusetMems = resources.CpusetMems
}
if resources.Memory != 0 {
cResources.Memory = resources.Memory
}
if resources.MemorySwap != 0 {
cResources.MemorySwap = resources.MemorySwap
}
if resources.MemoryReservation != 0 {
cResources.MemoryReservation = resources.MemoryReservation
}
if resources.KernelMemory != 0 {
cResources.KernelMemory = resources.KernelMemory
}
container.Unlock()
// If container is not running, update hostConfig struct is enough,
// resources will be updated when the container is started again.
// If container is running (including paused), we need to update
// the command so we can update configs to the real world.
if container.IsRunning() {
container.Lock()
updateCommand(container.Command, resources)
container.Unlock()
}
if err := container.ToDiskLocking(); err != nil {
logrus.Errorf("Error saving updated container: %v", err)
return err
}
return nil
}
func detachMounted(path string) error { func detachMounted(path string) error {
return syscall.Unmount(path, syscall.MNT_DETACH) return syscall.Unmount(path, syscall.MNT_DETACH)
} }

View file

@ -3,6 +3,7 @@
package container package container
import ( import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/volume" "github.com/docker/docker/volume"
) )
@ -47,6 +48,11 @@ func (container *Container) TmpfsMounts() []execdriver.Mount {
return nil return nil
} }
// UpdateContainer updates resources of a container
func (container *Container) UpdateContainer(hostConfig *container.HostConfig) error {
return nil
}
// appendNetworkMounts appends any network mounts to the array of mount points passed in. // appendNetworkMounts appends any network mounts to the array of mount points passed in.
// Windows does not support network mounts (not to be confused with SMB network mounts), so // Windows does not support network mounts (not to be confused with SMB network mounts), so
// this is a no-op. // this is a no-op.

View file

@ -91,6 +91,9 @@ type Driver interface {
// Stats returns resource stats for a running container // Stats returns resource stats for a running container
Stats(id string) (*ResourceStats, error) Stats(id string) (*ResourceStats, error)
// Update updates resource configs for a container
Update(c *Command) error
// SupportsHooks refers to the driver capability to exploit pre/post hook functionality // SupportsHooks refers to the driver capability to exploit pre/post hook functionality
SupportsHooks() bool SupportsHooks() bool
} }

View file

@ -398,6 +398,26 @@ func (d *Driver) Stats(id string) (*execdriver.ResourceStats, error) {
}, nil }, nil
} }
// Update updates configs for a container
func (d *Driver) Update(c *execdriver.Command) error {
d.Lock()
cont := d.activeContainers[c.ID]
d.Unlock()
if cont == nil {
return execdriver.ErrNotRunning
}
config := cont.Config()
if err := execdriver.SetupCgroups(&config, c); err != nil {
return err
}
if err := cont.Set(config); err != nil {
return err
}
return nil
}
// TtyConsole implements the exec driver Terminal interface. // TtyConsole implements the exec driver Terminal interface.
type TtyConsole struct { type TtyConsole struct {
console libcontainer.Console console libcontainer.Console

View file

@ -0,0 +1,14 @@
// +build windows
package windows
import (
"fmt"
"github.com/docker/docker/daemon/execdriver"
)
// Update updates resource configs for a container.
func (d *Driver) Update(c *execdriver.Command) error {
return fmt.Errorf("Windows: Update not implemented")
}

58
daemon/update.go Normal file
View file

@ -0,0 +1,58 @@
package daemon
import (
"fmt"
"github.com/docker/docker/api/types/container"
)
// ContainerUpdate updates resources of the container
func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig) ([]string, error) {
var warnings []string
warnings, err := daemon.verifyContainerSettings(hostConfig, nil)
if err != nil {
return warnings, err
}
if err := daemon.update(name, hostConfig); err != nil {
return warnings, err
}
return warnings, nil
}
func (daemon *Daemon) update(name string, hostConfig *container.HostConfig) error {
if hostConfig == nil {
return nil
}
container, err := daemon.GetContainer(name)
if err != nil {
return err
}
if container.RemovalInProgress || container.Dead {
return fmt.Errorf("Container is marked for removal and cannot be \"update\".")
}
if container.IsRunning() && hostConfig.KernelMemory != 0 {
return fmt.Errorf("Can not update kernel memory to a running container, please stop it first.")
}
if err := container.UpdateContainer(hostConfig); err != nil {
return err
}
// If container is not running, update hostConfig struct is enough,
// resources will be updated when the container is started again.
// If container is running (including paused), we need to update configs
// to the real world.
if container.IsRunning() {
if err := daemon.execDriver.Update(container.Command); err != nil {
return err
}
}
return nil
}

View file

@ -1003,6 +1003,50 @@ Status Codes:
- **404** no such container - **404** no such container
- **500** server error - **500** server error
### Update a container
`POST /containers/(id)/update`
Update resource configs of one or more containers.
**Example request**:
POST /containers/(id)/update HTTP/1.1
Content-Type: application/json
{
"HostConfig": {
"Resources": {
"BlkioWeight": 300,
"CpuShares": 512,
"CpuPeriod": 100000,
"CpuQuota": 50000,
"CpusetCpus": "0,1",
"CpusetMems": "0",
"Memory": 314572800,
"MemorySwap": 514288000,
"MemoryReservation": 209715200,
"KernelMemory": 52428800,
}
}
}
**Example response**:
HTTP/1.1 200 OK
Content-Type: application/json
{
"Warnings": []
}
Status Codes:
- **200** no error
- **400** bad parameter
- **404** no such container
- **500** server error
### Rename a container ### Rename a container
`POST /containers/(id)/rename` `POST /containers/(id)/rename`

File diff suppressed because one or more lines are too long

View file

@ -59,6 +59,7 @@ You start the Docker daemon with the command line. How you start the daemon affe
* [stop](stop.md) * [stop](stop.md)
* [top](top.md) * [top](top.md)
* [unpause](unpause.md) * [unpause](unpause.md)
* [update](update.md)
* [wait](wait.md) * [wait](wait.md)
### Hub and registry commands ### Hub and registry commands

View file

@ -0,0 +1,61 @@
<!--[metadata]>
+++
title = "update"
description = "The update command description and usage"
keywords = ["resources, update, dynamically"]
[menu.main]
parent = "smn_cli"
+++
<![end-metadata]-->
## update
Usage: docker update [OPTIONS] CONTAINER [CONTAINER...]
Updates container resource limits
--help=false Print usage
--blkio-weight=0 Block IO (relative weight), between 10 and 1000
--cpu-shares=0 CPU shares (relative weight)
--cpu-period=0 Limit the CPU CFS (Completely Fair Scheduler) period
--cpu-quota=0 Limit the CPU CFS (Completely Fair Scheduler) quota
--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)
-m, --memory="" Memory limit
--memory-reservation="" Memory soft limit
--memory-swap="" Total memory (memory + swap), '-1' to disable swap
--kernel-memory="" Kernel memory limit: container must be stopped
The `docker update` command dynamically updates container resources. Use this
command to prevent containers from consuming too many resources from their
Docker host. With a single command, you can place limits on a single
container or on many. To specify more than one container, provide
space-separated list of container names or IDs.
With the exception of the `--kernel-memory` value, you can specify these
options on a running or a stopped container. You can only update
`--kernel-memory` on a stopped container. When you run `docker update` on
stopped container, the next time you restart it, the container uses those
values.
## EXAMPLES
The following sections illustrate ways to use this command.
### Update a container with cpu-shares=512
To limit a container's cpu-shares to 512, first identify the container
name or ID. You can use **docker ps** to find these values. You can also
use the ID returned from the **docker run** command. Then, do the following:
```bash
$ docker update --cpu-shares 512 abebf7571666
```
### Update a container with cpu-shares and memory
To update multiple resource configurations for multiple containers:
```bash
$ docker update --cpu-shares 512 -m 300M abebf7571666 hopeful_morse
```

View file

@ -0,0 +1,43 @@
// +build !windows
package main
import (
"strings"
"github.com/docker/docker/pkg/integration/checker"
"github.com/go-check/check"
)
func (s *DockerSuite) TestApiUpdateContainer(c *check.C) {
testRequires(c, DaemonIsLinux)
testRequires(c, memoryLimitSupport)
testRequires(c, swapMemorySupport)
name := "apiUpdateContainer"
hostConfig := map[string]interface{}{
"Memory": 314572800,
"MemorySwap": 524288000,
}
dockerCmd(c, "run", "-d", "--name", name, "-m", "200M", "busybox", "top")
_, _, err := sockRequest("POST", "/containers/"+name+"/update", hostConfig)
c.Assert(err, check.IsNil)
memory, err := inspectField(name, "HostConfig.Memory")
c.Assert(err, check.IsNil)
if memory != "314572800" {
c.Fatalf("Got the wrong memory value, we got %d, expected 314572800(300M).", memory)
}
file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
out, _ := dockerCmd(c, "exec", name, "cat", file)
c.Assert(strings.TrimSpace(out), checker.Equals, "314572800")
memorySwap, err := inspectField(name, "HostConfig.MemorySwap")
c.Assert(err, check.IsNil)
if memorySwap != "524288000" {
c.Fatalf("Got the wrong memorySwap value, we got %d, expected 524288000(500M).", memorySwap)
}
file = "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"
out, _ = dockerCmd(c, "exec", name, "cat", file)
c.Assert(strings.TrimSpace(out), checker.Equals, "524288000")
}

View file

@ -226,7 +226,7 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
} }
// Number of commands for standard release and experimental release // Number of commands for standard release and experimental release
standard := 40 standard := 41
experimental := 1 experimental := 1
expected := standard + experimental expected := standard + experimental
if isLocalDaemon { if isLocalDaemon {

View file

@ -0,0 +1,162 @@
// +build !windows
package main
import (
"strings"
"github.com/docker/docker/pkg/integration/checker"
"github.com/go-check/check"
)
func (s *DockerSuite) TestUpdateRunningContainer(c *check.C) {
testRequires(c, DaemonIsLinux)
testRequires(c, memoryLimitSupport)
name := "test-update-container"
dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "top")
dockerCmd(c, "update", "-m", "500M", name)
memory, err := inspectField(name, "HostConfig.Memory")
c.Assert(err, check.IsNil)
if memory != "524288000" {
c.Fatalf("Got the wrong memory value, we got %d, expected 524288000(500M).", memory)
}
file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
out, _ := dockerCmd(c, "exec", name, "cat", file)
c.Assert(strings.TrimSpace(out), checker.Equals, "524288000")
}
func (s *DockerSuite) TestUpdateRunningContainerWithRestart(c *check.C) {
testRequires(c, DaemonIsLinux)
testRequires(c, memoryLimitSupport)
name := "test-update-container"
dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "top")
dockerCmd(c, "update", "-m", "500M", name)
dockerCmd(c, "restart", name)
memory, err := inspectField(name, "HostConfig.Memory")
c.Assert(err, check.IsNil)
if memory != "524288000" {
c.Fatalf("Got the wrong memory value, we got %d, expected 524288000(500M).", memory)
}
file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
out, _ := dockerCmd(c, "exec", name, "cat", file)
c.Assert(strings.TrimSpace(out), checker.Equals, "524288000")
}
func (s *DockerSuite) TestUpdateStoppedContainer(c *check.C) {
testRequires(c, DaemonIsLinux)
testRequires(c, memoryLimitSupport)
name := "test-update-container"
file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
dockerCmd(c, "run", "--name", name, "-m", "300M", "busybox", "cat", file)
dockerCmd(c, "update", "-m", "500M", name)
memory, err := inspectField(name, "HostConfig.Memory")
c.Assert(err, check.IsNil)
if memory != "524288000" {
c.Fatalf("Got the wrong memory value, we got %d, expected 524288000(500M).", memory)
}
out, _ := dockerCmd(c, "start", "-a", name)
c.Assert(strings.TrimSpace(out), checker.Equals, "524288000")
}
func (s *DockerSuite) TestUpdatePausedContainer(c *check.C) {
testRequires(c, DaemonIsLinux)
testRequires(c, cpuShare)
name := "test-update-container"
dockerCmd(c, "run", "-d", "--name", name, "--cpu-shares", "1000", "busybox", "top")
dockerCmd(c, "pause", name)
dockerCmd(c, "update", "--cpu-shares", "500", name)
out, err := inspectField(name, "HostConfig.CPUShares")
c.Assert(err, check.IsNil)
if out != "500" {
c.Fatalf("Got the wrong cpu shares value, we got %d, expected 500.", out)
}
dockerCmd(c, "unpause", name)
file := "/sys/fs/cgroup/cpu/cpu.shares"
out, _ = dockerCmd(c, "exec", name, "cat", file)
c.Assert(strings.TrimSpace(out), checker.Equals, "500")
}
func (s *DockerSuite) TestUpdateWithUntouchedFields(c *check.C) {
testRequires(c, DaemonIsLinux)
testRequires(c, memoryLimitSupport)
testRequires(c, cpuShare)
name := "test-update-container"
dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "--cpu-shares", "800", "busybox", "top")
dockerCmd(c, "update", "-m", "500M", name)
// Update memory and not touch cpus, `cpuset.cpus` should still have the old value
out, err := inspectField(name, "HostConfig.CPUShares")
c.Assert(err, check.IsNil)
c.Assert(out, check.Equals, "800")
file := "/sys/fs/cgroup/cpu/cpu.shares"
out, _ = dockerCmd(c, "exec", name, "cat", file)
c.Assert(strings.TrimSpace(out), checker.Equals, "800")
}
func (s *DockerSuite) TestUpdateContainerInvalidValue(c *check.C) {
testRequires(c, DaemonIsLinux)
testRequires(c, memoryLimitSupport)
name := "test-update-container"
dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "true")
out, _, err := dockerCmdWithError("update", "-m", "2M", name)
c.Assert(err, check.NotNil)
expected := "Minimum memory limit allowed is 4MB"
c.Assert(out, checker.Contains, expected)
}
func (s *DockerSuite) TestUpdateContainerWithoutFlags(c *check.C) {
testRequires(c, DaemonIsLinux)
testRequires(c, memoryLimitSupport)
name := "test-update-container"
dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "true")
_, _, err := dockerCmdWithError("update", name)
c.Assert(err, check.NotNil)
}
func (s *DockerSuite) TestUpdateKernelMemory(c *check.C) {
testRequires(c, DaemonIsLinux)
testRequires(c, kernelMemorySupport)
name := "test-update-container"
dockerCmd(c, "run", "-d", "--name", name, "--kernel-memory", "50M", "busybox", "top")
_, _, err := dockerCmdWithError("update", "--kernel-memory", "100M", name)
// Update kernel memory to a running container is not allowed.
c.Assert(err, check.NotNil)
out, err := inspectField(name, "HostConfig.KernelMemory")
c.Assert(err, check.IsNil)
// Update kernel memory to a running container with failure should not change HostConfig
if out != "52428800" {
c.Fatalf("Got the wrong memory value, we got %d, expected 52428800(50M).", out)
}
dockerCmd(c, "stop", name)
dockerCmd(c, "update", "--kernel-memory", "100M", name)
dockerCmd(c, "start", name)
out, err = inspectField(name, "HostConfig.KernelMemory")
c.Assert(err, check.IsNil)
if out != "104857600" {
c.Fatalf("Got the wrong memory value, we got %d, expected 104857600(100M).", out)
}
file := "/sys/fs/cgroup/memory/memory.kmem.limit_in_bytes"
out, _ = dockerCmd(c, "exec", name, "cat", file)
c.Assert(strings.TrimSpace(out), checker.Equals, "104857600")
}

93
man/docker-update.1.md Normal file
View file

@ -0,0 +1,93 @@
% DOCKER(1) Docker User Manuals
% Docker Community
% JUNE 2014
# NAME
docker-update - Update resource configs of one or more containers
# SYNOPSIS
**docker update**
[**--blkio-weight**[=*[BLKIO-WEIGHT]*]]
[**--cpu-shares**[=*0*]]
[**--cpu-period**[=*0*]]
[**--cpu-quota**[=*0*]]
[**--cpuset-cpus**[=*CPUSET-CPUS*]]
[**--cpuset-mems**[=*CPUSET-MEMS*]]
[**--help**]
[**--kernel-memory**[=*KERNEL-MEMORY*]]
[**-m**|**--memory**[=*MEMORY*]]
[**--memory-reservation**[=*MEMORY-RESERVATION*]]
[**--memory-swap**[=*MEMORY-SWAP*]]
CONTAINER [CONTAINER...]
# DESCRIPTION
The `docker update` command dynamically updates container resources. Use this
command to prevent containers from consuming too many resources from their
Docker host. With a single command, you can place limits on a single
container or on many. To specify more than one container, provide
space-separated list of container names or IDs.
With the exception of the `--kernel-memory` value, you can specify these
options on a running or a stopped container. You can only update
`--kernel-memory` on a stopped container. When you run `docker update` on
stopped container, the next time you restart it, the container uses those
values.
# OPTIONS
**--blkio-weight**=0
Block IO weight (relative weight) accepts a weight value between 10 and 1000.
**--cpu-shares**=0
CPU shares (relative weight)
**--cpu-period**=0
Limit the CPU CFS (Completely Fair Scheduler) period
**--cpu-quota**=0
Limit the CPU CFS (Completely Fair Scheduler) quota
**--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.
**--help**
Print usage statement
**--kernel-memory**=""
Kernel memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)
Note that you can not update kernel memory to a running container, it can only
be updated to a stopped container, and affect after it's started.
**-m**, **--memory**=""
Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
**--memory-reservation**=""
Memory soft limit (format: <number>[<unit>], where unit = b, k, m or g)
**--memory-swap**=""
Total memory limit (memory + swap)
# EXAMPLES
The following sections illustrate ways to use this command.
### Update a container with cpu-shares=512
To limit a container's cpu-shares to 512, first identify the container
name or ID. You can use **docker ps** to find these values. You can also
use the ID returned from the **docker run** command. Then, do the following:
```bash
$ docker update --cpu-shares 512 abebf7571666
```
### Update a container with cpu-shares and memory
To update multiple resource configurations for multiple containers:
```bash
$ docker update --cpu-shares 512 -m 300M abebf7571666 hopeful_morse
```