Forráskód Böngészése

Add support for memory reservation

Signed-off-by: qhuang <qhuang@10.0.2.15>
qhuang 9 éve
szülő
commit
aa1780997f

+ 1 - 0
contrib/completion/bash/docker

@@ -1151,6 +1151,7 @@ _docker_run() {
 		--memory -m
 		--memory -m
 		--memory-swap
 		--memory-swap
 		--memory-swappiness
 		--memory-swappiness
+		--memory-reservation
 		--name
 		--name
 		--net
 		--net
 		--pid
 		--pid

+ 13 - 12
daemon/container_unix.go

@@ -254,18 +254,19 @@ func populateCommand(c *Container, env []string) error {
 	}
 	}
 
 
 	resources := &execdriver.Resources{
 	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 {
 	if c.hostConfig.MemorySwappiness != nil {

+ 11 - 0
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.
 		// By default, MemorySwap is set to twice the size of Memory.
 		hostConfig.MemorySwap = hostConfig.Memory * 2
 		hostConfig.MemorySwap = hostConfig.Memory * 2
 	}
 	}
+	if hostConfig.MemoryReservation == 0 && hostConfig.Memory > 0 {
+		hostConfig.MemoryReservation = hostConfig.Memory
+	}
 }
 }
 
 
 // verifyPlatformContainerSettings performs platform-specific validation of the
 // 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)
 			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 {
 	if hostConfig.KernelMemory > 0 && !sysInfo.KernelMemory {
 		warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities. Limitation discarded.")
 		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.")
 		logrus.Warnf("Your kernel does not support kernel memory limit capabilities. Limitation discarded.")

+ 13 - 12
daemon/execdriver/driver.go

@@ -141,18 +141,19 @@ type UTS struct {
 // Currently these are all for cgroup configs.
 // Currently these are all for cgroup configs.
 // TODO Windows: Factor out ulimit.Rlimit
 // TODO Windows: Factor out ulimit.Rlimit
 type Resources struct {
 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.
 // ResourceStats contains information about resource usage by a container.

+ 1 - 1
daemon/execdriver/driver_unix.go

@@ -64,7 +64,7 @@ func SetupCgroups(container *configs.Config, c *Command) error {
 	if c.Resources != nil {
 	if c.Resources != nil {
 		container.Cgroups.CpuShares = c.Resources.CPUShares
 		container.Cgroups.CpuShares = c.Resources.CPUShares
 		container.Cgroups.Memory = c.Resources.Memory
 		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.MemorySwap = c.Resources.MemorySwap
 		container.Cgroups.CpusetCpus = c.Resources.CpusetCpus
 		container.Cgroups.CpusetCpus = c.Resources.CpusetCpus
 		container.Cgroups.CpusetMems = c.Resources.CpusetMems
 		container.Cgroups.CpusetMems = c.Resources.CpusetMems

+ 3 - 1
daemon/execdriver/lxc/lxc_template.go

@@ -91,11 +91,13 @@ lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabS
 {{if .Resources}}
 {{if .Resources}}
 {{if .Resources.Memory}}
 {{if .Resources.Memory}}
 lxc.cgroup.memory.limit_in_bytes = {{.Resources.Memory}}
 lxc.cgroup.memory.limit_in_bytes = {{.Resources.Memory}}
-lxc.cgroup.memory.soft_limit_in_bytes = {{.Resources.Memory}}
 {{with $memSwap := getMemorySwap .Resources}}
 {{with $memSwap := getMemorySwap .Resources}}
 lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
 lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
 {{end}}
 {{end}}
 {{end}}
 {{end}}
+{{if gt .Resources.MemoryReservation 0}}
+lxc.cgroup.memory.soft_limit_in_bytes = {{.Resources.MemoryReservation}}
+{{end}}
 {{if gt .Resources.KernelMemory 0}}
 {{if gt .Resources.KernelMemory 0}}
 lxc.cgroup.memory.kmem.limit_in_bytes = {{.Resources.KernelMemory}}
 lxc.cgroup.memory.kmem.limit_in_bytes = {{.Resources.KernelMemory}}
 {{end}}
 {{end}}

+ 3 - 0
docs/reference/api/docker_remote_api_v1.21.md

@@ -173,6 +173,7 @@ Create a container
              "LxcConf": {"lxc.utsname":"docker"},
              "LxcConf": {"lxc.utsname":"docker"},
              "Memory": 0,
              "Memory": 0,
              "MemorySwap": 0,
              "MemorySwap": 0,
+             "MemoryReservation": 0,
              "KernelMemory": 0,
              "KernelMemory": 0,
              "CpuShares": 512,
              "CpuShares": 512,
              "CpuPeriod": 100000,
              "CpuPeriod": 100000,
@@ -223,6 +224,7 @@ Json Parameters:
 -   **Memory** - Memory limit in bytes.
 -   **Memory** - Memory limit in bytes.
 -   **MemorySwap** - Total memory limit (memory + swap); set `-1` to disable swap
 -   **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`.
       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.
 -   **KernelMemory** - Kernel memory limit in bytes.
 -   **CpuShares** - An integer value containing the container's CPU Shares
 -   **CpuShares** - An integer value containing the container's CPU Shares
       (ie. the relative weight vs other containers).
       (ie. the relative weight vs other containers).
@@ -398,6 +400,7 @@ Return low-level information on the container `id`
 			"LxcConf": [],
 			"LxcConf": [],
 			"Memory": 0,
 			"Memory": 0,
 			"MemorySwap": 0,
 			"MemorySwap": 0,
+			"MemoryReservation": 0,
 			"KernelMemory": 0,
 			"KernelMemory": 0,
 			"OomKillDisable": false,
 			"OomKillDisable": false,
 			"NetworkMode": "bridge",
 			"NetworkMode": "bridge",

+ 1 - 0
docs/reference/commandline/create.md

@@ -50,6 +50,7 @@ Creates a new container.
       --lxc-conf=[]                 Add custom lxc options
       --lxc-conf=[]                 Add custom lxc options
       -m, --memory=""               Memory limit
       -m, --memory=""               Memory limit
       --mac-address=""              Container MAC address (e.g. 92:d0:c6:0a:29:33)
       --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-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.
       --memory-swappiness=""        Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
       --name=""                     Assign a name to the container
       --name=""                     Assign a name to the container

+ 1 - 0
docs/reference/commandline/run.md

@@ -50,6 +50,7 @@ weight=1
       --lxc-conf=[]                 Add custom lxc options
       --lxc-conf=[]                 Add custom lxc options
       -m, --memory=""               Memory limit
       -m, --memory=""               Memory limit
       --mac-address=""              Container MAC address (e.g. 92:d0:c6:0a:29:33)
       --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-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.
       --memory-swappiness=""        Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
       --name=""                     Assign a name to the container
       --name=""                     Assign a name to the container

+ 38 - 0
docs/reference/run.md

@@ -544,6 +544,7 @@ container:
 |----------------------------|---------------------------------------------------------------------------------------------|
 |----------------------------|---------------------------------------------------------------------------------------------|
 | `-m`, `--memory="" `       | Memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)                        |
 | `-m`, `--memory="" `       | Memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)                        |
 | `--memory-swap=""`         | Total memory limit (memory + swap, format: `<number>[<unit>]`, where unit = b, k, m or g)   |
 | `--memory-swap=""`         | Total memory limit (memory + swap, format: `<number>[<unit>]`, where unit = b, k, m or g)   |
+| `--memory-reservation=""`  | Memory soft limit (format: `<number>[<unit>]`, where unit = b, k, m or g)                   |
 | `--kernel-memory=""`       | Kernel memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)                 |
 | `--kernel-memory=""`       | Kernel memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)                 |
 | `-c`, `--cpu-shares=0`     | CPU shares (relative weight)                                                                |
 | `-c`, `--cpu-shares=0`     | CPU shares (relative weight)                                                                |
 | `--cpu-period=0`           | Limit the CPU CFS (Completely Fair Scheduler) period                                        |
 | `--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
 We set both memory and swap memory, so the processes in the container can use
 300M memory and 700M swap memory.
 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)
 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.
 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
 Only disable the OOM killer on containers where you have also set the

+ 16 - 0
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) {
 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`)
 	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)
 	containerID := strings.TrimSpace(out)

+ 6 - 0
integration-cli/requirements_unix.go

@@ -45,6 +45,12 @@ var (
 		},
 		},
 		"Test requires an environment that supports cgroup memory limit.",
 		"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{
 	swapMemorySupport = testRequirement{
 		func() bool {
 		func() bool {
 			return SysInfo.SwapLimit
 			return SysInfo.SwapLimit

+ 10 - 0
man/docker-create.1.md

@@ -40,6 +40,7 @@ docker-create - Create a new container
 [**--lxc-conf**[=*[]*]]
 [**--lxc-conf**[=*[]*]]
 [**-m**|**--memory**[=*MEMORY*]]
 [**-m**|**--memory**[=*MEMORY*]]
 [**--mac-address**[=*MAC-ADDRESS*]]
 [**--mac-address**[=*MAC-ADDRESS*]]
+[**--memory-reservation**[=*MEMORY-RESERVATION*]]
 [**--memory-swap**[=*MEMORY-SWAP*]]
 [**--memory-swap**[=*MEMORY-SWAP*]]
 [**--memory-swappiness**[=*MEMORY-SWAPPINESS*]]
 [**--memory-swappiness**[=*MEMORY-SWAPPINESS*]]
 [**--name**[=*NAME*]]
 [**--name**[=*NAME*]]
@@ -196,6 +197,15 @@ system's page size (the value would be very large, that's millions of trillions)
 **--mac-address**=""
 **--mac-address**=""
    Container MAC address (e.g. 92:d0:c6:0a:29:33)
    Container MAC address (e.g. 92:d0:c6:0a:29:33)
 
 
+**--memory-reservation**=""
+   Memory soft limit (format: <number>[<unit>], 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**=""
 **--memory-swap**=""
    Total memory limit (memory + swap)
    Total memory limit (memory + swap)
 
 

+ 10 - 0
man/docker-run.1.md

@@ -41,6 +41,7 @@ docker-run - Run a command in a new container
 [**--lxc-conf**[=*[]*]]
 [**--lxc-conf**[=*[]*]]
 [**-m**|**--memory**[=*MEMORY*]]
 [**-m**|**--memory**[=*MEMORY*]]
 [**--mac-address**[=*MAC-ADDRESS*]]
 [**--mac-address**[=*MAC-ADDRESS*]]
+[**--memory-reservation**[=*MEMORY-RESERVATION*]]
 [**--memory-swap**[=*MEMORY-SWAP*]]
 [**--memory-swap**[=*MEMORY-SWAP*]]
 [**--memory-swappiness**[=*MEMORY-SWAPPINESS*]]
 [**--memory-swappiness**[=*MEMORY-SWAPPINESS*]]
 [**--name**[=*NAME*]]
 [**--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
 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).
 system's page size (the value would be very large, that's millions of trillions).
 
 
+**--memory-reservation**=""
+   Memory soft limit (format: <number>[<unit>], 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**=""
 **--memory-swap**=""
    Total memory limit (memory + swap)
    Total memory limit (memory + swap)
 
 

+ 3 - 0
pkg/sysinfo/sysinfo.go

@@ -31,6 +31,9 @@ type cgroupMemInfo struct {
 	// Whether swap limit is supported or not
 	// Whether swap limit is supported or not
 	SwapLimit bool
 	SwapLimit bool
 
 
+	// Whether soft limit is supported or not
+	MemoryReservation bool
+
 	// Whether OOM killer disalbe is supported or not
 	// Whether OOM killer disalbe is supported or not
 	OomKillDisable bool
 	OomKillDisable bool
 
 

+ 10 - 5
pkg/sysinfo/sysinfo_linux.go

@@ -49,6 +49,10 @@ func checkCgroupMem(quiet bool) cgroupMemInfo {
 	if !quiet && !swapLimit {
 	if !quiet && !swapLimit {
 		logrus.Warn("Your kernel does not support swap memory limit.")
 		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")
 	oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control")
 	if !quiet && !oomKillDisable {
 	if !quiet && !oomKillDisable {
 		logrus.Warnf("Your kernel does not support oom control.")
 		logrus.Warnf("Your kernel does not support oom control.")
@@ -63,11 +67,12 @@ func checkCgroupMem(quiet bool) cgroupMemInfo {
 	}
 	}
 
 
 	return 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,
 	}
 	}
 }
 }
 
 

+ 40 - 39
runconfig/hostconfig.go

@@ -214,45 +214,46 @@ func NewLxcConfig(values []KeyValuePair) *LxcConfig {
 // Here, "non-portable" means "dependent of the host we are running on".
 // Here, "non-portable" means "dependent of the host we are running on".
 // Portable information *should* appear in Config.
 // Portable information *should* appear in Config.
 type HostConfig struct {
 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.
 // DecodeHostConfig creates a HostConfig based on the specified Reader.

+ 80 - 70
runconfig/parse.go

@@ -64,38 +64,39 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 		flLabelsFile  = opts.NewListOpts(nil)
 		flLabelsFile  = opts.NewListOpts(nil)
 		flLoggingOpts = 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: <name|uid>[:<group|gid>])")
-		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: <name|uid>[:<group|gid>])")
+		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")
 	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
 	var memorySwap int64
 	if *flMemorySwap != "" {
 	if *flMemorySwap != "" {
 		if *flMemorySwap == "-1" {
 		if *flMemorySwap == "-1" {
@@ -329,44 +338,45 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 	}
 	}
 
 
 	hostConfig := &HostConfig{
 	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)
 	applyExperimentalFlags(expFlags, config, hostConfig)