Parcourir la source

Add support for memory reservation

Signed-off-by: qhuang <qhuang@10.0.2.15>
qhuang il y a 9 ans
Parent
commit
aa1780997f

+ 1 - 0
contrib/completion/bash/docker

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

+ 13 - 12
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 {

+ 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.
 		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.")

+ 13 - 12
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.

+ 1 - 1
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

+ 3 - 1
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}}

+ 3 - 0
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",

+ 1 - 0
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

+ 1 - 0
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

+ 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)                        |
 | `--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)                 |
 | `-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

+ 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) {
 	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)

+ 6 - 0
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

+ 10 - 0
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: <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**=""
    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**[=*[]*]]
 [**-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: <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**=""
    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
 	SwapLimit bool
 
+	// Whether soft limit is supported or not
+	MemoryReservation bool
+
 	// Whether OOM killer disalbe is supported or not
 	OomKillDisable bool
 

+ 10 - 5
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,
 	}
 }
 

+ 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".
 // 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.

+ 80 - 70
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: <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")
@@ -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)