mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
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:
parent
b0be88c111
commit
8799c4fc0f
22 changed files with 728 additions and 2 deletions
|
@ -43,6 +43,7 @@ type apiClient interface {
|
|||
ContainerStop(containerID string, timeout int) error
|
||||
ContainerTop(containerID string, arguments []string) (types.ContainerProcessList, error)
|
||||
ContainerUnpause(containerID string) error
|
||||
ContainerUpdate(containerID string, hostConfig container.HostConfig) error
|
||||
ContainerWait(containerID string) (int, error)
|
||||
CopyFromContainer(containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
|
||||
CopyToContainer(options types.CopyToContainerOptions) error
|
||||
|
|
12
api/client/lib/container_update.go
Normal file
12
api/client/lib/container_update.go
Normal 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
104
api/client/update.go
Normal 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
|
||||
}
|
|
@ -42,6 +42,7 @@ type stateBackend interface {
|
|||
ContainerStart(name string, hostConfig *container.HostConfig) error
|
||||
ContainerStop(name string, seconds int) error
|
||||
ContainerUnpause(name string) error
|
||||
ContainerUpdate(name string, hostConfig *container.HostConfig) ([]string, error)
|
||||
ContainerWait(name string, timeout time.Duration) (int, error)
|
||||
Exists(id string) bool
|
||||
IsPaused(id string) bool
|
||||
|
|
|
@ -57,6 +57,7 @@ func (r *containerRouter) initRoutes() {
|
|||
local.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
|
||||
local.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
|
||||
local.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
|
||||
local.NewPostRoute("/containers/{name:.*}/update", r.postContainerUpdate),
|
||||
// PUT
|
||||
local.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
|
||||
// DELETE
|
||||
|
|
|
@ -323,6 +323,30 @@ func (s *containerRouter) postContainerRename(ctx context.Context, w http.Respon
|
|||
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 {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
|
|
|
@ -28,6 +28,13 @@ type ContainerExecCreateResponse struct {
|
|||
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:
|
||||
// POST "/auth"
|
||||
type AuthResponse struct {
|
||||
|
|
|
@ -64,6 +64,7 @@ var dockerCommands = []Command{
|
|||
{"tag", "Tag an image into a repository"},
|
||||
{"top", "Display the running processes of a container"},
|
||||
{"unpause", "Unpause all processes within a container"},
|
||||
{"update", "Update resources of one or more containers"},
|
||||
{"version", "Show the Docker version information"},
|
||||
{"volume", "Manage Docker volumes"},
|
||||
{"wait", "Block until a container stops, then print its exit code"},
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
derr "github.com/docker/docker/errors"
|
||||
|
@ -543,6 +544,75 @@ func (container *Container) IpcMounts() []execdriver.Mount {
|
|||
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 {
|
||||
return syscall.Unmount(path, syscall.MNT_DETACH)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/docker/volume"
|
||||
)
|
||||
|
@ -47,6 +48,11 @@ func (container *Container) TmpfsMounts() []execdriver.Mount {
|
|||
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.
|
||||
// Windows does not support network mounts (not to be confused with SMB network mounts), so
|
||||
// this is a no-op.
|
||||
|
|
|
@ -91,6 +91,9 @@ type Driver interface {
|
|||
// Stats returns resource stats for a running container
|
||||
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() bool
|
||||
}
|
||||
|
|
|
@ -398,6 +398,26 @@ func (d *Driver) Stats(id string) (*execdriver.ResourceStats, error) {
|
|||
}, 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.
|
||||
type TtyConsole struct {
|
||||
console libcontainer.Console
|
||||
|
|
14
daemon/execdriver/windows/update.go
Normal file
14
daemon/execdriver/windows/update.go
Normal 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
58
daemon/update.go
Normal 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
|
||||
}
|
|
@ -1003,6 +1003,50 @@ Status Codes:
|
|||
- **404** – no such container
|
||||
- **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
|
||||
|
||||
`POST /containers/(id)/rename`
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -59,6 +59,7 @@ You start the Docker daemon with the command line. How you start the daemon affe
|
|||
* [stop](stop.md)
|
||||
* [top](top.md)
|
||||
* [unpause](unpause.md)
|
||||
* [update](update.md)
|
||||
* [wait](wait.md)
|
||||
|
||||
### Hub and registry commands
|
||||
|
|
61
docs/reference/commandline/update.md
Normal file
61
docs/reference/commandline/update.md
Normal 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
|
||||
```
|
43
integration-cli/docker_api_update_unix_test.go
Normal file
43
integration-cli/docker_api_update_unix_test.go
Normal 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")
|
||||
}
|
|
@ -226,7 +226,7 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
|
|||
}
|
||||
|
||||
// Number of commands for standard release and experimental release
|
||||
standard := 40
|
||||
standard := 41
|
||||
experimental := 1
|
||||
expected := standard + experimental
|
||||
if isLocalDaemon {
|
||||
|
|
162
integration-cli/docker_cli_update_unix_test.go
Normal file
162
integration-cli/docker_cli_update_unix_test.go
Normal 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
93
man/docker-update.1.md
Normal 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
|
||||
```
|
Loading…
Reference in a new issue