diff --git a/daemon/execdriver/native/configuration/parse.go b/daemon/execdriver/native/configuration/parse.go index 6d6c643919..c3846af910 100644 --- a/daemon/execdriver/native/configuration/parse.go +++ b/daemon/execdriver/native/configuration/parse.go @@ -21,10 +21,11 @@ var actions = map[string]Action{ "net.join": joinNetNamespace, // join another containers net namespace - "cgroups.cpu_shares": cpuShares, // set the cpu shares - "cgroups.memory": memory, // set the memory limit - "cgroups.memory_swap": memorySwap, // set the memory swap limit - "cgroups.cpuset.cpus": cpusetCpus, // set the cpus used + "cgroups.cpu_shares": cpuShares, // set the cpu shares + "cgroups.memory": memory, // set the memory limit + "cgroups.memory_reservation": memoryReservation, // set the memory reservation + "cgroups.memory_swap": memorySwap, // set the memory swap limit + "cgroups.cpuset.cpus": cpusetCpus, // set the cpus used "apparmor_profile": apparmorProfile, // set the apparmor profile to apply @@ -70,6 +71,19 @@ func memory(container *libcontainer.Container, context interface{}, value string return nil } +func memoryReservation(container *libcontainer.Container, context interface{}, value string) error { + if container.Cgroups == nil { + return fmt.Errorf("cannot set cgroups when they are disabled") + } + + v, err := utils.RAMInBytes(value) + if err != nil { + return err + } + container.Cgroups.MemoryReservation = v + return nil +} + func memorySwap(container *libcontainer.Container, context interface{}, value string) error { if container.Cgroups == nil { return fmt.Errorf("cannot set cgroups when they are disabled") diff --git a/daemon/execdriver/native/configuration/parse_test.go b/daemon/execdriver/native/configuration/parse_test.go index 9034867b7b..c28176f2ef 100644 --- a/daemon/execdriver/native/configuration/parse_test.go +++ b/daemon/execdriver/native/configuration/parse_test.go @@ -93,7 +93,7 @@ func TestCpuShares(t *testing.T) { } } -func TestCgroupMemory(t *testing.T) { +func TestMemory(t *testing.T) { var ( container = template.New() opts = []string{ @@ -109,6 +109,22 @@ func TestCgroupMemory(t *testing.T) { } } +func TestMemoryReservation(t *testing.T) { + var ( + container = template.New() + opts = []string{ + "cgroups.memory_reservation=500m", + } + ) + if err := ParseConfiguration(container, nil, opts); err != nil { + t.Fatal(err) + } + + if expected := int64(500 * 1024 * 1024); container.Cgroups.MemoryReservation != expected { + t.Fatalf("expected memory reservation %d got %d", expected, container.Cgroups.MemoryReservation) + } +} + func TestAddCap(t *testing.T) { var ( container = template.New() diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go index ef17ce7042..334a97ad4b 100644 --- a/daemon/execdriver/native/create.go +++ b/daemon/execdriver/native/create.go @@ -91,6 +91,7 @@ func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.C if c.Resources != nil { container.Cgroups.CpuShares = c.Resources.CpuShares container.Cgroups.Memory = c.Resources.Memory + container.Cgroups.MemoryReservation = c.Resources.Memory container.Cgroups.MemorySwap = c.Resources.MemorySwap } return nil diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index 9a498609b5..81e3eb551a 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -12,13 +12,14 @@ type Cgroup struct { Name string `json:"name,omitempty"` Parent string `json:"parent,omitempty"` - DeviceAccess bool `json:"device_access,omitempty"` // name of parent cgroup or slice - Memory int64 `json:"memory,omitempty"` // Memory limit (in bytes) - MemorySwap int64 `json:"memory_swap,omitempty"` // Total memory usage (memory + swap); set `-1' to disable swap - CpuShares int64 `json:"cpu_shares,omitempty"` // CPU shares (relative weight vs. other containers) - CpuQuota int64 `json:"cpu_quota,omitempty"` // CPU hardcap limit (in usecs). Allowed cpu time in a given period. - CpuPeriod int64 `json:"cpu_period,omitempty"` // CPU period to be used for hardcapping (in usecs). 0 to use system default. - CpusetCpus string `json:"cpuset_cpus,omitempty"` // CPU to use + DeviceAccess bool `json:"device_access,omitempty"` // name of parent cgroup or slice + Memory int64 `json:"memory,omitempty"` // Memory limit (in bytes) + MemoryReservation int64 `json:"memory_reservation,omitempty"` // Memory reservation or soft_limit (in bytes) + MemorySwap int64 `json:"memory_swap,omitempty"` // Total memory usage (memory + swap); set `-1' to disable swap + CpuShares int64 `json:"cpu_shares,omitempty"` // CPU shares (relative weight vs. other containers) + CpuQuota int64 `json:"cpu_quota,omitempty"` // CPU hardcap limit (in usecs). Allowed cpu time in a given period. + CpuPeriod int64 `json:"cpu_period,omitempty"` // CPU period to be used for hardcapping (in usecs). 0 to use system default. + CpusetCpus string `json:"cpuset_cpus,omitempty"` // CPU to use UnitProperties [][2]string `json:"unit_properties,omitempty"` // systemd unit properties } diff --git a/pkg/cgroups/fs/memory.go b/pkg/cgroups/fs/memory.go index cf4bf5ab73..5315291197 100644 --- a/pkg/cgroups/fs/memory.go +++ b/pkg/cgroups/fs/memory.go @@ -13,7 +13,7 @@ type memoryGroup struct { func (s *memoryGroup) Set(d *data) error { dir, err := d.join("memory") // only return an error for memory if it was not specified - if err != nil && (d.c.Memory != 0 || d.c.MemorySwap != 0) { + if err != nil && (d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0) { return err } defer func() { @@ -22,12 +22,15 @@ func (s *memoryGroup) Set(d *data) error { } }() - if d.c.Memory != 0 || d.c.MemorySwap != 0 { + // Only set values if some config was specified. + if d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0 { if d.c.Memory != 0 { if err := writeFile(dir, "memory.limit_in_bytes", strconv.FormatInt(d.c.Memory, 10)); err != nil { return err } - if err := writeFile(dir, "memory.soft_limit_in_bytes", strconv.FormatInt(d.c.Memory, 10)); err != nil { + } + if d.c.MemoryReservation != 0 { + if err := writeFile(dir, "memory.soft_limit_in_bytes", strconv.FormatInt(d.c.MemoryReservation, 10)); err != nil { return err } } diff --git a/pkg/cgroups/systemd/apply_systemd.go b/pkg/cgroups/systemd/apply_systemd.go index 7c26080d6e..e1246f6e70 100644 --- a/pkg/cgroups/systemd/apply_systemd.go +++ b/pkg/cgroups/systemd/apply_systemd.go @@ -121,6 +121,10 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) { properties = append(properties, systemd1.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))}) } + if c.MemoryReservation != 0 { + properties = append(properties, + systemd1.Property{"MemorySoftLimit", dbus.MakeVariant(uint64(c.MemoryReservation))}) + } // TODO: MemorySwap not available in systemd if c.CpuShares != 0 {