diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index c04326036a..6055ede511 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -1151,6 +1151,7 @@ _docker_run() { --memory -m --memory-swap --memory-swappiness + --memory-reservation --name --net --pid diff --git a/daemon/container_unix.go b/daemon/container_unix.go index bee8078745..3c516480d1 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -254,18 +254,19 @@ func populateCommand(c *Container, env []string) error { } resources := &execdriver.Resources{ - Memory: c.hostConfig.Memory, - MemorySwap: c.hostConfig.MemorySwap, - KernelMemory: c.hostConfig.KernelMemory, - CPUShares: c.hostConfig.CPUShares, - CpusetCpus: c.hostConfig.CpusetCpus, - CpusetMems: c.hostConfig.CpusetMems, - CPUPeriod: c.hostConfig.CPUPeriod, - CPUQuota: c.hostConfig.CPUQuota, - BlkioWeight: c.hostConfig.BlkioWeight, - Rlimits: rlimits, - OomKillDisable: c.hostConfig.OomKillDisable, - MemorySwappiness: -1, + Memory: c.hostConfig.Memory, + MemorySwap: c.hostConfig.MemorySwap, + MemoryReservation: c.hostConfig.MemoryReservation, + KernelMemory: c.hostConfig.KernelMemory, + CPUShares: c.hostConfig.CPUShares, + CpusetCpus: c.hostConfig.CpusetCpus, + CpusetMems: c.hostConfig.CpusetMems, + CPUPeriod: c.hostConfig.CPUPeriod, + CPUQuota: c.hostConfig.CPUQuota, + BlkioWeight: c.hostConfig.BlkioWeight, + Rlimits: rlimits, + OomKillDisable: c.hostConfig.OomKillDisable, + MemorySwappiness: -1, } if c.hostConfig.MemorySwappiness != nil { diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index af46270170..2a623fb808 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -110,6 +110,9 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *runconfig.HostConfig, a // By default, MemorySwap is set to twice the size of Memory. hostConfig.MemorySwap = hostConfig.Memory * 2 } + if hostConfig.MemoryReservation == 0 && hostConfig.Memory > 0 { + hostConfig.MemoryReservation = hostConfig.Memory + } } // verifyPlatformContainerSettings performs platform-specific validation of the @@ -154,6 +157,14 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *runconfig.HostC return warnings, fmt.Errorf("Invalid value: %v, valid memory swappiness range is 0-100.", swappiness) } } + if hostConfig.MemoryReservation > 0 && !sysInfo.MemoryReservation { + warnings = append(warnings, "Your kernel does not support memory soft limit capabilities. Limitation discarded.") + logrus.Warnf("Your kernel does not support memory soft limit capabilities. Limitation discarded.") + hostConfig.MemoryReservation = 0 + } + if hostConfig.Memory > 0 && hostConfig.MemoryReservation > 0 && hostConfig.Memory < hostConfig.MemoryReservation { + return warnings, fmt.Errorf("Minimum memory limit should be larger than memory reservation limit, see usage.") + } if hostConfig.KernelMemory > 0 && !sysInfo.KernelMemory { warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities. Limitation discarded.") logrus.Warnf("Your kernel does not support kernel memory limit capabilities. Limitation discarded.") diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index 7e8998d272..15c61a72ba 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -141,18 +141,19 @@ type UTS struct { // Currently these are all for cgroup configs. // TODO Windows: Factor out ulimit.Rlimit type Resources struct { - Memory int64 `json:"memory"` - MemorySwap int64 `json:"memory_swap"` - KernelMemory int64 `json:"kernel_memory"` - CPUShares int64 `json:"cpu_shares"` - CpusetCpus string `json:"cpuset_cpus"` - CpusetMems string `json:"cpuset_mems"` - CPUPeriod int64 `json:"cpu_period"` - CPUQuota int64 `json:"cpu_quota"` - BlkioWeight int64 `json:"blkio_weight"` - Rlimits []*ulimit.Rlimit `json:"rlimits"` - OomKillDisable bool `json:"oom_kill_disable"` - MemorySwappiness int64 `json:"memory_swappiness"` + Memory int64 `json:"memory"` + MemorySwap int64 `json:"memory_swap"` + MemoryReservation int64 `json:"memory_reservation"` + KernelMemory int64 `json:"kernel_memory"` + CPUShares int64 `json:"cpu_shares"` + CpusetCpus string `json:"cpuset_cpus"` + CpusetMems string `json:"cpuset_mems"` + CPUPeriod int64 `json:"cpu_period"` + CPUQuota int64 `json:"cpu_quota"` + BlkioWeight int64 `json:"blkio_weight"` + Rlimits []*ulimit.Rlimit `json:"rlimits"` + OomKillDisable bool `json:"oom_kill_disable"` + MemorySwappiness int64 `json:"memory_swappiness"` } // ResourceStats contains information about resource usage by a container. diff --git a/daemon/execdriver/driver_unix.go b/daemon/execdriver/driver_unix.go index 8d2f75ac05..2fde9485ab 100644 --- a/daemon/execdriver/driver_unix.go +++ b/daemon/execdriver/driver_unix.go @@ -64,7 +64,7 @@ func SetupCgroups(container *configs.Config, c *Command) error { if c.Resources != nil { container.Cgroups.CpuShares = c.Resources.CPUShares container.Cgroups.Memory = c.Resources.Memory - container.Cgroups.MemoryReservation = c.Resources.Memory + container.Cgroups.MemoryReservation = c.Resources.MemoryReservation container.Cgroups.MemorySwap = c.Resources.MemorySwap container.Cgroups.CpusetCpus = c.Resources.CpusetCpus container.Cgroups.CpusetMems = c.Resources.CpusetMems diff --git a/daemon/execdriver/lxc/lxc_template.go b/daemon/execdriver/lxc/lxc_template.go index f0096f7f08..975c5f19e0 100644 --- a/daemon/execdriver/lxc/lxc_template.go +++ b/daemon/execdriver/lxc/lxc_template.go @@ -91,11 +91,13 @@ lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabS {{if .Resources}} {{if .Resources.Memory}} lxc.cgroup.memory.limit_in_bytes = {{.Resources.Memory}} -lxc.cgroup.memory.soft_limit_in_bytes = {{.Resources.Memory}} {{with $memSwap := getMemorySwap .Resources}} lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}} {{end}} {{end}} +{{if gt .Resources.MemoryReservation 0}} +lxc.cgroup.memory.soft_limit_in_bytes = {{.Resources.MemoryReservation}} +{{end}} {{if gt .Resources.KernelMemory 0}} lxc.cgroup.memory.kmem.limit_in_bytes = {{.Resources.KernelMemory}} {{end}} diff --git a/docs/reference/api/docker_remote_api_v1.21.md b/docs/reference/api/docker_remote_api_v1.21.md index b328d58295..e547b61d4e 100644 --- a/docs/reference/api/docker_remote_api_v1.21.md +++ b/docs/reference/api/docker_remote_api_v1.21.md @@ -173,6 +173,7 @@ Create a container "LxcConf": {"lxc.utsname":"docker"}, "Memory": 0, "MemorySwap": 0, + "MemoryReservation": 0, "KernelMemory": 0, "CpuShares": 512, "CpuPeriod": 100000, @@ -223,6 +224,7 @@ Json Parameters: - **Memory** - Memory limit in bytes. - **MemorySwap** - Total memory limit (memory + swap); set `-1` to disable swap You must use this with `memory` and make the swap value larger than `memory`. +- **MemoryReservation** - Memory soft limit in bytes. - **KernelMemory** - Kernel memory limit in bytes. - **CpuShares** - An integer value containing the container's CPU Shares (ie. the relative weight vs other containers). @@ -398,6 +400,7 @@ Return low-level information on the container `id` "LxcConf": [], "Memory": 0, "MemorySwap": 0, + "MemoryReservation": 0, "KernelMemory": 0, "OomKillDisable": false, "NetworkMode": "bridge", diff --git a/docs/reference/commandline/create.md b/docs/reference/commandline/create.md index ba8b33a387..34ae6dfef7 100644 --- a/docs/reference/commandline/create.md +++ b/docs/reference/commandline/create.md @@ -50,6 +50,7 @@ Creates a new container. --lxc-conf=[] Add custom lxc options -m, --memory="" Memory limit --mac-address="" Container MAC address (e.g. 92:d0:c6:0a:29:33) + --memory-reservation="" Memory soft limit --memory-swap="" Total memory (memory + swap), '-1' to disable swap --memory-swappiness="" Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100. --name="" Assign a name to the container diff --git a/docs/reference/commandline/run.md b/docs/reference/commandline/run.md index 313f61558b..766ca205dc 100644 --- a/docs/reference/commandline/run.md +++ b/docs/reference/commandline/run.md @@ -50,6 +50,7 @@ weight=1 --lxc-conf=[] Add custom lxc options -m, --memory="" Memory limit --mac-address="" Container MAC address (e.g. 92:d0:c6:0a:29:33) + --memory-reservation="" Memory soft limit --memory-swap="" Total memory (memory + swap), '-1' to disable swap --memory-swappiness="" Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100. --name="" Assign a name to the container diff --git a/docs/reference/run.md b/docs/reference/run.md index 6e109341f2..46dec11115 100644 --- a/docs/reference/run.md +++ b/docs/reference/run.md @@ -544,6 +544,7 @@ container: |----------------------------|---------------------------------------------------------------------------------------------| | `-m`, `--memory="" ` | Memory limit (format: `[]`, where unit = b, k, m or g) | | `--memory-swap=""` | Total memory limit (memory + swap, format: `[]`, where unit = b, k, m or g) | +| `--memory-reservation=""` | Memory soft limit (format: `[]`, where unit = b, k, m or g) | | `--kernel-memory=""` | Kernel memory limit (format: `[]`, where unit = b, k, m or g) | | `-c`, `--cpu-shares=0` | CPU shares (relative weight) | | `--cpu-period=0` | Limit the CPU CFS (Completely Fair Scheduler) period | @@ -629,6 +630,43 @@ would be 2*300M, so processes can use 300M swap memory as well. We set both memory and swap memory, so the processes in the container can use 300M memory and 700M swap memory. +Memory reservation is a kind of memory soft limit that allows for greater +sharing of memory. Under normal circumstances, containers can use as much of +the memory as needed and are constrained only by the hard limits set with the +`-m`/`--memory` option. When memory reservation is set, Docker detects memory +contention or low memory and forces containers to restrict their consumption to +a reservation limit. + +Always set the memory reservation value below the hard limit, otherwise the hard +limit takes precedence. A reservation of 0 is the same as setting no +reservation. By default (without reservation set), memory reservation is the +same as the hard memory limit. + +Memory reservation is a soft-limit feature and does not guarantee the limit +won't be exceeded. Instead, the feature attempts to ensure that, when memory is +heavily contended for, memory is allocated based on the reservation hints/setup. + +The following example limits the memory (`-m`) to 500M and sets the memory +reservation to 200M. + +```bash +$ docker run -ti -m 500M --memory-reservation 200M ubuntu:14.04 /bin/bash +``` + +Under this configuration, when the container consumes memory more than 200M and +less than 500M, the next system memory reclaim attempts to shrink container +memory below 200M. + +The following example set memory reservation to 1G without a hard memory limit. + +```bash +$ docker run -ti --memory-reservation 1G ubuntu:14.04 /bin/bash +``` + +The container can use as much memory as it needs. The memory reservation setting +ensures the container doesn't consume too much memory for long time, because +every memory reclaim shrinks the container's consumption to the reservation. + By default, kernel kills processes in a container if an out-of-memory (OOM) error occurs. To change this behaviour, use the `--oom-kill-disable` option. Only disable the OOM killer on containers where you have also set the diff --git a/integration-cli/docker_cli_run_unix_test.go b/integration-cli/docker_cli_run_unix_test.go index 65cff3bc77..6a728f864a 100644 --- a/integration-cli/docker_cli_run_unix_test.go +++ b/integration-cli/docker_cli_run_unix_test.go @@ -323,6 +323,22 @@ func (s *DockerSuite) TestRunWithSwappinessInvalid(c *check.C) { } } +func (s *DockerSuite) TestRunWithMemoryReservation(c *check.C) { + testRequires(c, memoryReservationSupport) + dockerCmd(c, "run", "--memory-reservation", "200M", "busybox", "true") +} + +func (s *DockerSuite) TestRunWithMemoryReservationInvalid(c *check.C) { + testRequires(c, memoryLimitSupport) + testRequires(c, memoryReservationSupport) + out, _, err := dockerCmdWithError("run", "-m", "500M", "--memory-reservation", "800M", "busybox", "true") + c.Assert(err, check.NotNil) + expected := "Minimum memory limit should be larger than memory reservation limit" + if !strings.Contains(strings.TrimSpace(out), expected) { + c.Fatalf("run container should fail with invalid memory reservation, output: %q", out) + } +} + func (s *DockerSuite) TestStopContainerSignal(c *check.C) { out, _ := dockerCmd(c, "run", "--stop-signal", "SIGUSR1", "-d", "busybox", "/bin/sh", "-c", `trap 'echo "exit trapped"; exit 0' USR1; while true; do sleep 1; done`) containerID := strings.TrimSpace(out) diff --git a/integration-cli/requirements_unix.go b/integration-cli/requirements_unix.go index aee5207fa9..72c396f3a7 100644 --- a/integration-cli/requirements_unix.go +++ b/integration-cli/requirements_unix.go @@ -45,6 +45,12 @@ var ( }, "Test requires an environment that supports cgroup memory limit.", } + memoryReservationSupport = testRequirement{ + func() bool { + return SysInfo.MemoryReservation + }, + "Test requires an environment that supports cgroup memory reservation.", + } swapMemorySupport = testRequirement{ func() bool { return SysInfo.SwapLimit diff --git a/man/docker-create.1.md b/man/docker-create.1.md index e82ac3381c..e845befe97 100644 --- a/man/docker-create.1.md +++ b/man/docker-create.1.md @@ -40,6 +40,7 @@ docker-create - Create a new container [**--lxc-conf**[=*[]*]] [**-m**|**--memory**[=*MEMORY*]] [**--mac-address**[=*MAC-ADDRESS*]] +[**--memory-reservation**[=*MEMORY-RESERVATION*]] [**--memory-swap**[=*MEMORY-SWAP*]] [**--memory-swappiness**[=*MEMORY-SWAPPINESS*]] [**--name**[=*NAME*]] @@ -196,6 +197,15 @@ system's page size (the value would be very large, that's millions of trillions) **--mac-address**="" Container MAC address (e.g. 92:d0:c6:0a:29:33) +**--memory-reservation**="" + Memory soft limit (format: [], where unit = b, k, m or g) + + After setting memory reservation, when the system detects memory contention +or low memory, containers are forced to restrict their consumption to their +reservation. So you should always set the value below **--memory**, otherwise the +hard limit will take precedence. By default, memory reservation will be the same +as memory limit. + **--memory-swap**="" Total memory limit (memory + swap) diff --git a/man/docker-run.1.md b/man/docker-run.1.md index ebb65d71c7..960ac9f27d 100644 --- a/man/docker-run.1.md +++ b/man/docker-run.1.md @@ -41,6 +41,7 @@ docker-run - Run a command in a new container [**--lxc-conf**[=*[]*]] [**-m**|**--memory**[=*MEMORY*]] [**--mac-address**[=*MAC-ADDRESS*]] +[**--memory-reservation**[=*MEMORY-RESERVATION*]] [**--memory-swap**[=*MEMORY-SWAP*]] [**--memory-swappiness**[=*MEMORY-SWAPPINESS*]] [**--name**[=*NAME*]] @@ -290,6 +291,15 @@ RAM. If a limit of 0 is specified (not using **-m**), the container's memory is not limited. The actual limit may be rounded up to a multiple of the operating system's page size (the value would be very large, that's millions of trillions). +**--memory-reservation**="" + Memory soft limit (format: [], where unit = b, k, m or g) + + After setting memory reservation, when the system detects memory contention +or low memory, containers are forced to restrict their consumption to their +reservation. So you should always set the value below **--memory**, otherwise the +hard limit will take precedence. By default, memory reservation will be the same +as memory limit. + **--memory-swap**="" Total memory limit (memory + swap) diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go index c4ca4a2fc3..e62eadfcf4 100644 --- a/pkg/sysinfo/sysinfo.go +++ b/pkg/sysinfo/sysinfo.go @@ -31,6 +31,9 @@ type cgroupMemInfo struct { // Whether swap limit is supported or not SwapLimit bool + // Whether soft limit is supported or not + MemoryReservation bool + // Whether OOM killer disalbe is supported or not OomKillDisable bool diff --git a/pkg/sysinfo/sysinfo_linux.go b/pkg/sysinfo/sysinfo_linux.go index 84e3ceb3f5..44f10fae40 100644 --- a/pkg/sysinfo/sysinfo_linux.go +++ b/pkg/sysinfo/sysinfo_linux.go @@ -49,6 +49,10 @@ func checkCgroupMem(quiet bool) cgroupMemInfo { if !quiet && !swapLimit { logrus.Warn("Your kernel does not support swap memory limit.") } + memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes") + if !quiet && !memoryReservation { + logrus.Warn("Your kernel does not support memory reservation.") + } oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control") if !quiet && !oomKillDisable { logrus.Warnf("Your kernel does not support oom control.") @@ -63,11 +67,12 @@ func checkCgroupMem(quiet bool) cgroupMemInfo { } return cgroupMemInfo{ - MemoryLimit: true, - SwapLimit: swapLimit, - OomKillDisable: oomKillDisable, - MemorySwappiness: memorySwappiness, - KernelMemory: kernelMemory, + MemoryLimit: true, + SwapLimit: swapLimit, + MemoryReservation: memoryReservation, + OomKillDisable: oomKillDisable, + MemorySwappiness: memorySwappiness, + KernelMemory: kernelMemory, } } diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 5bc8822581..7ef99328f9 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -214,45 +214,46 @@ func NewLxcConfig(values []KeyValuePair) *LxcConfig { // Here, "non-portable" means "dependent of the host we are running on". // Portable information *should* appear in Config. type HostConfig struct { - Binds []string // List of volume bindings for this container - ContainerIDFile string // File (path) where the containerId is written - LxcConf *LxcConfig // Additional lxc configuration - Memory int64 // Memory limit (in bytes) - MemorySwap int64 // Total memory usage (memory + swap); set `-1` to disable swap - KernelMemory int64 // Kernel memory limit (in bytes) - CPUShares int64 `json:"CpuShares"` // CPU shares (relative weight vs. other containers) - CPUPeriod int64 `json:"CpuPeriod"` // CPU CFS (Completely Fair Scheduler) period - CpusetCpus string // CpusetCpus 0-2, 0,1 - CpusetMems string // CpusetMems 0-2, 0,1 - CPUQuota int64 `json:"CpuQuota"` // CPU CFS (Completely Fair Scheduler) quota - BlkioWeight int64 // Block IO weight (relative weight vs. other containers) - OomKillDisable bool // Whether to disable OOM Killer or not - MemorySwappiness *int64 // Tuning container memory swappiness behaviour - Privileged bool // Is the container in privileged mode - PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host - Links []string // List of links (in the name:alias form) - PublishAllPorts bool // Should docker publish all exposed port for the container - DNS []string `json:"Dns"` // List of DNS server to lookup - DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for - DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for - ExtraHosts []string // List of extra hosts - VolumesFrom []string // List of volumes to take from other container - Devices []DeviceMapping // List of devices to map inside the container - NetworkMode NetworkMode // Network namespace to use for the container - IpcMode IpcMode // IPC namespace to use for the container - PidMode PidMode // PID namespace to use for the container - UTSMode UTSMode // UTS namespace to use for the container - CapAdd *stringutils.StrSlice // List of kernel capabilities to add to the container - CapDrop *stringutils.StrSlice // List of kernel capabilities to remove from the container - GroupAdd []string // List of additional groups that the container process will run as - RestartPolicy RestartPolicy // Restart policy to be used for the container - SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux. - ReadonlyRootfs bool // Is the container root filesystem in read-only - Ulimits []*ulimit.Ulimit // List of ulimits to be set in the container - LogConfig LogConfig // Configuration of the logs for this container - CgroupParent string // Parent cgroup. - ConsoleSize [2]int // Initial console size on Windows - VolumeDriver string // Name of the volume driver used to mount volumes + Binds []string // List of volume bindings for this container + ContainerIDFile string // File (path) where the containerId is written + LxcConf *LxcConfig // Additional lxc configuration + Memory int64 // Memory limit (in bytes) + MemoryReservation int64 // Memory soft limit (in bytes) + MemorySwap int64 // Total memory usage (memory + swap); set `-1` to disable swap + KernelMemory int64 // Kernel memory limit (in bytes) + CPUShares int64 `json:"CpuShares"` // CPU shares (relative weight vs. other containers) + CPUPeriod int64 `json:"CpuPeriod"` // CPU CFS (Completely Fair Scheduler) period + CpusetCpus string // CpusetCpus 0-2, 0,1 + CpusetMems string // CpusetMems 0-2, 0,1 + CPUQuota int64 `json:"CpuQuota"` // CPU CFS (Completely Fair Scheduler) quota + BlkioWeight int64 // Block IO weight (relative weight vs. other containers) + OomKillDisable bool // Whether to disable OOM Killer or not + MemorySwappiness *int64 // Tuning container memory swappiness behaviour + Privileged bool // Is the container in privileged mode + PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host + Links []string // List of links (in the name:alias form) + PublishAllPorts bool // Should docker publish all exposed port for the container + DNS []string `json:"Dns"` // List of DNS server to lookup + DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for + DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for + ExtraHosts []string // List of extra hosts + VolumesFrom []string // List of volumes to take from other container + Devices []DeviceMapping // List of devices to map inside the container + NetworkMode NetworkMode // Network namespace to use for the container + IpcMode IpcMode // IPC namespace to use for the container + PidMode PidMode // PID namespace to use for the container + UTSMode UTSMode // UTS namespace to use for the container + CapAdd *stringutils.StrSlice // List of kernel capabilities to add to the container + CapDrop *stringutils.StrSlice // List of kernel capabilities to remove from the container + GroupAdd []string // List of additional groups that the container process will run as + RestartPolicy RestartPolicy // Restart policy to be used for the container + SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux. + ReadonlyRootfs bool // Is the container root filesystem in read-only + Ulimits []*ulimit.Ulimit // List of ulimits to be set in the container + LogConfig LogConfig // Configuration of the logs for this container + CgroupParent string // Parent cgroup. + ConsoleSize [2]int // Initial console size on Windows + VolumeDriver string // Name of the volume driver used to mount volumes } // DecodeHostConfig creates a HostConfig based on the specified Reader. diff --git a/runconfig/parse.go b/runconfig/parse.go index 3fff16ad76..ac44ba7345 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -64,38 +64,39 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe flLabelsFile = opts.NewListOpts(nil) flLoggingOpts = opts.NewListOpts(nil) - flNetwork = cmd.Bool([]string{"#n", "#-networking"}, true, "Enable networking for this container") - flPrivileged = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container") - flPidMode = cmd.String([]string{"-pid"}, "", "PID namespace to use") - flUTSMode = cmd.String([]string{"-uts"}, "", "UTS namespace to use") - flPublishAll = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to random ports") - flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep STDIN open even if not attached") - flTty = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-TTY") - flOomKillDisable = cmd.Bool([]string{"-oom-kill-disable"}, false, "Disable OOM Killer") - flContainerIDFile = cmd.String([]string{"#cidfile", "-cidfile"}, "", "Write the container ID to the file") - flEntrypoint = cmd.String([]string{"#entrypoint", "-entrypoint"}, "", "Overwrite the default ENTRYPOINT of the image") - flHostname = cmd.String([]string{"h", "-hostname"}, "", "Container host name") - flMemoryString = cmd.String([]string{"m", "-memory"}, "", "Memory limit") - flMemorySwap = cmd.String([]string{"-memory-swap"}, "", "Total memory (memory + swap), '-1' to disable swap") - flKernelMemory = cmd.String([]string{"-kernel-memory"}, "", "Kernel memory limit") - flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID (format: [:])") - flWorkingDir = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container") - flCPUShares = cmd.Int64([]string{"#c", "-cpu-shares"}, 0, "CPU shares (relative weight)") - 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", "-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)") - flBlkioWeight = cmd.Int64([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000") - flSwappiness = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tuning container memory swappiness (0 to 100)") - flNetMode = cmd.String([]string{"-net"}, "default", "Set the Network mode for the container") - flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)") - flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use") - flRestartPolicy = cmd.String([]string{"-restart"}, "no", "Restart policy to apply when a container exits") - flReadonlyRootfs = cmd.Bool([]string{"-read-only"}, false, "Mount the container's root filesystem as read only") - flLoggingDriver = cmd.String([]string{"-log-driver"}, "", "Logging driver for container") - flCgroupParent = cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container") - flVolumeDriver = cmd.String([]string{"-volume-driver"}, "", "Optional volume driver for the container") - flStopSignal = cmd.String([]string{"-stop-signal"}, signal.DefaultStopSignal, fmt.Sprintf("Signal to stop a container, %v by default", signal.DefaultStopSignal)) + flNetwork = cmd.Bool([]string{"#n", "#-networking"}, true, "Enable networking for this container") + flPrivileged = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container") + flPidMode = cmd.String([]string{"-pid"}, "", "PID namespace to use") + flUTSMode = cmd.String([]string{"-uts"}, "", "UTS namespace to use") + flPublishAll = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to random ports") + flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep STDIN open even if not attached") + flTty = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-TTY") + flOomKillDisable = cmd.Bool([]string{"-oom-kill-disable"}, false, "Disable OOM Killer") + flContainerIDFile = cmd.String([]string{"#cidfile", "-cidfile"}, "", "Write the container ID to the file") + flEntrypoint = cmd.String([]string{"#entrypoint", "-entrypoint"}, "", "Overwrite the default ENTRYPOINT of the image") + flHostname = cmd.String([]string{"h", "-hostname"}, "", "Container host name") + 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") + flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID (format: [:])") + flWorkingDir = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container") + flCPUShares = cmd.Int64([]string{"#c", "-cpu-shares"}, 0, "CPU shares (relative weight)") + 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", "-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)") + flBlkioWeight = cmd.Int64([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000") + flSwappiness = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tuning container memory swappiness (0 to 100)") + flNetMode = cmd.String([]string{"-net"}, "default", "Set the Network mode for the container") + flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)") + flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use") + flRestartPolicy = cmd.String([]string{"-restart"}, "no", "Restart policy to apply when a container exits") + flReadonlyRootfs = cmd.Bool([]string{"-read-only"}, false, "Mount the container's root filesystem as read only") + flLoggingDriver = cmd.String([]string{"-log-driver"}, "", "Logging driver for container") + flCgroupParent = cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container") + flVolumeDriver = cmd.String([]string{"-volume-driver"}, "", "Optional volume driver for the container") + flStopSignal = cmd.String([]string{"-stop-signal"}, signal.DefaultStopSignal, fmt.Sprintf("Signal to stop a container, %v by default", signal.DefaultStopSignal)) ) cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR") @@ -160,6 +161,14 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe } } + var MemoryReservation int64 + if *flMemoryReservation != "" { + MemoryReservation, err = units.RAMInBytes(*flMemoryReservation) + if err != nil { + return nil, nil, cmd, err + } + } + var memorySwap int64 if *flMemorySwap != "" { if *flMemorySwap == "-1" { @@ -329,44 +338,45 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe } hostConfig := &HostConfig{ - Binds: binds, - ContainerIDFile: *flContainerIDFile, - LxcConf: lxcConf, - Memory: flMemory, - MemorySwap: memorySwap, - KernelMemory: KernelMemory, - CPUShares: *flCPUShares, - CPUPeriod: *flCPUPeriod, - CpusetCpus: *flCpusetCpus, - CpusetMems: *flCpusetMems, - CPUQuota: *flCPUQuota, - BlkioWeight: *flBlkioWeight, - OomKillDisable: *flOomKillDisable, - MemorySwappiness: flSwappiness, - Privileged: *flPrivileged, - PortBindings: portBindings, - Links: flLinks.GetAll(), - PublishAllPorts: *flPublishAll, - DNS: flDNS.GetAll(), - DNSSearch: flDNSSearch.GetAll(), - DNSOptions: flDNSOptions.GetAll(), - ExtraHosts: flExtraHosts.GetAll(), - VolumesFrom: flVolumesFrom.GetAll(), - NetworkMode: NetworkMode(*flNetMode), - IpcMode: ipcMode, - PidMode: pidMode, - UTSMode: utsMode, - Devices: deviceMappings, - CapAdd: stringutils.NewStrSlice(flCapAdd.GetAll()...), - CapDrop: stringutils.NewStrSlice(flCapDrop.GetAll()...), - GroupAdd: flGroupAdd.GetAll(), - RestartPolicy: restartPolicy, - SecurityOpt: flSecurityOpt.GetAll(), - ReadonlyRootfs: *flReadonlyRootfs, - Ulimits: flUlimits.GetList(), - LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts}, - CgroupParent: *flCgroupParent, - VolumeDriver: *flVolumeDriver, + Binds: binds, + ContainerIDFile: *flContainerIDFile, + LxcConf: lxcConf, + Memory: flMemory, + MemoryReservation: MemoryReservation, + MemorySwap: memorySwap, + KernelMemory: KernelMemory, + CPUShares: *flCPUShares, + CPUPeriod: *flCPUPeriod, + CpusetCpus: *flCpusetCpus, + CpusetMems: *flCpusetMems, + CPUQuota: *flCPUQuota, + BlkioWeight: *flBlkioWeight, + OomKillDisable: *flOomKillDisable, + MemorySwappiness: flSwappiness, + Privileged: *flPrivileged, + PortBindings: portBindings, + Links: flLinks.GetAll(), + PublishAllPorts: *flPublishAll, + DNS: flDNS.GetAll(), + DNSSearch: flDNSSearch.GetAll(), + DNSOptions: flDNSOptions.GetAll(), + ExtraHosts: flExtraHosts.GetAll(), + VolumesFrom: flVolumesFrom.GetAll(), + NetworkMode: NetworkMode(*flNetMode), + IpcMode: ipcMode, + PidMode: pidMode, + UTSMode: utsMode, + Devices: deviceMappings, + CapAdd: stringutils.NewStrSlice(flCapAdd.GetAll()...), + CapDrop: stringutils.NewStrSlice(flCapDrop.GetAll()...), + GroupAdd: flGroupAdd.GetAll(), + RestartPolicy: restartPolicy, + SecurityOpt: flSecurityOpt.GetAll(), + ReadonlyRootfs: *flReadonlyRootfs, + Ulimits: flUlimits.GetList(), + LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts}, + CgroupParent: *flCgroupParent, + VolumeDriver: *flVolumeDriver, } applyExperimentalFlags(expFlags, config, hostConfig)