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
|
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
|
||||||
|
|
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
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"},
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
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
|
- **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
|
@ -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
|
||||||
|
|
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
|
// 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 {
|
||||||
|
|
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