Browse Source

Separating cgroup Memory and MemoryReservation.

This will allow for these to be set independently. Keep the current Docker behavior where Memory and MemoryReservation are set to the value of Memory.

Docker-DCO-1.1-Signed-off-by: Victor Marmol <vmarmol@google.com> (github: vmarmol)
Victor Marmol 11 years ago
parent
commit
f188b9f623

+ 18 - 4
daemon/execdriver/native/configuration/parse.go

@@ -21,10 +21,11 @@ var actions = map[string]Action{
 
 
 	"net.join": joinNetNamespace, // join another containers net namespace
 	"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
 	"apparmor_profile": apparmorProfile, // set the apparmor profile to apply
 
 
@@ -70,6 +71,19 @@ func memory(container *libcontainer.Container, context interface{}, value string
 	return nil
 	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 {
 func memorySwap(container *libcontainer.Container, context interface{}, value string) error {
 	if container.Cgroups == nil {
 	if container.Cgroups == nil {
 		return fmt.Errorf("cannot set cgroups when they are disabled")
 		return fmt.Errorf("cannot set cgroups when they are disabled")

+ 17 - 1
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 (
 	var (
 		container = template.New()
 		container = template.New()
 		opts      = []string{
 		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) {
 func TestAddCap(t *testing.T) {
 	var (
 	var (
 		container = template.New()
 		container = template.New()

+ 1 - 0
daemon/execdriver/native/create.go

@@ -91,6 +91,7 @@ func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.C
 	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.MemorySwap = c.Resources.MemorySwap
 		container.Cgroups.MemorySwap = c.Resources.MemorySwap
 	}
 	}
 	return nil
 	return nil

+ 8 - 7
pkg/cgroups/cgroups.go

@@ -12,13 +12,14 @@ type Cgroup struct {
 	Name   string `json:"name,omitempty"`
 	Name   string `json:"name,omitempty"`
 	Parent string `json:"parent,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
 	UnitProperties [][2]string `json:"unit_properties,omitempty"` // systemd unit properties
 }
 }

+ 6 - 3
pkg/cgroups/fs/memory.go

@@ -13,7 +13,7 @@ type memoryGroup struct {
 func (s *memoryGroup) Set(d *data) error {
 func (s *memoryGroup) Set(d *data) error {
 	dir, err := d.join("memory")
 	dir, err := d.join("memory")
 	// only return an error for memory if it was not specified
 	// 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
 		return err
 	}
 	}
 	defer func() {
 	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 d.c.Memory != 0 {
 			if err := writeFile(dir, "memory.limit_in_bytes", strconv.FormatInt(d.c.Memory, 10)); err != nil {
 			if err := writeFile(dir, "memory.limit_in_bytes", strconv.FormatInt(d.c.Memory, 10)); err != nil {
 				return err
 				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
 				return err
 			}
 			}
 		}
 		}

+ 4 - 0
pkg/cgroups/systemd/apply_systemd.go

@@ -121,6 +121,10 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
 		properties = append(properties,
 		properties = append(properties,
 			systemd1.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))})
 			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
 	// TODO: MemorySwap not available in systemd
 
 
 	if c.CpuShares != 0 {
 	if c.CpuShares != 0 {