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)
| ... | ... |
@@ -21,10 +21,11 @@ var actions = map[string]Action{
|
| 21 | 21 |
|
| 22 | 22 |
"net.join": joinNetNamespace, // join another containers net namespace |
| 23 | 23 |
|
| 24 |
- "cgroups.cpu_shares": cpuShares, // set the cpu shares |
|
| 25 |
- "cgroups.memory": memory, // set the memory limit |
|
| 26 |
- "cgroups.memory_swap": memorySwap, // set the memory swap limit |
|
| 27 |
- "cgroups.cpuset.cpus": cpusetCpus, // set the cpus used |
|
| 24 |
+ "cgroups.cpu_shares": cpuShares, // set the cpu shares |
|
| 25 |
+ "cgroups.memory": memory, // set the memory limit |
|
| 26 |
+ "cgroups.memory_reservation": memoryReservation, // set the memory reservation |
|
| 27 |
+ "cgroups.memory_swap": memorySwap, // set the memory swap limit |
|
| 28 |
+ "cgroups.cpuset.cpus": cpusetCpus, // set the cpus used |
|
| 28 | 29 |
|
| 29 | 30 |
"apparmor_profile": apparmorProfile, // set the apparmor profile to apply |
| 30 | 31 |
|
| ... | ... |
@@ -70,6 +71,19 @@ func memory(container *libcontainer.Container, context interface{}, value string
|
| 70 | 70 |
return nil |
| 71 | 71 |
} |
| 72 | 72 |
|
| 73 |
+func memoryReservation(container *libcontainer.Container, context interface{}, value string) error {
|
|
| 74 |
+ if container.Cgroups == nil {
|
|
| 75 |
+ return fmt.Errorf("cannot set cgroups when they are disabled")
|
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ v, err := utils.RAMInBytes(value) |
|
| 79 |
+ if err != nil {
|
|
| 80 |
+ return err |
|
| 81 |
+ } |
|
| 82 |
+ container.Cgroups.MemoryReservation = v |
|
| 83 |
+ return nil |
|
| 84 |
+} |
|
| 85 |
+ |
|
| 73 | 86 |
func memorySwap(container *libcontainer.Container, context interface{}, value string) error {
|
| 74 | 87 |
if container.Cgroups == nil {
|
| 75 | 88 |
return fmt.Errorf("cannot set cgroups when they are disabled")
|
| ... | ... |
@@ -93,7 +93,7 @@ func TestCpuShares(t *testing.T) {
|
| 93 | 93 |
} |
| 94 | 94 |
} |
| 95 | 95 |
|
| 96 |
-func TestCgroupMemory(t *testing.T) {
|
|
| 96 |
+func TestMemory(t *testing.T) {
|
|
| 97 | 97 |
var ( |
| 98 | 98 |
container = template.New() |
| 99 | 99 |
opts = []string{
|
| ... | ... |
@@ -109,6 +109,22 @@ func TestCgroupMemory(t *testing.T) {
|
| 109 | 109 |
} |
| 110 | 110 |
} |
| 111 | 111 |
|
| 112 |
+func TestMemoryReservation(t *testing.T) {
|
|
| 113 |
+ var ( |
|
| 114 |
+ container = template.New() |
|
| 115 |
+ opts = []string{
|
|
| 116 |
+ "cgroups.memory_reservation=500m", |
|
| 117 |
+ } |
|
| 118 |
+ ) |
|
| 119 |
+ if err := ParseConfiguration(container, nil, opts); err != nil {
|
|
| 120 |
+ t.Fatal(err) |
|
| 121 |
+ } |
|
| 122 |
+ |
|
| 123 |
+ if expected := int64(500 * 1024 * 1024); container.Cgroups.MemoryReservation != expected {
|
|
| 124 |
+ t.Fatalf("expected memory reservation %d got %d", expected, container.Cgroups.MemoryReservation)
|
|
| 125 |
+ } |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 112 | 128 |
func TestAddCap(t *testing.T) {
|
| 113 | 129 |
var ( |
| 114 | 130 |
container = template.New() |
| ... | ... |
@@ -91,6 +91,7 @@ func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.C |
| 91 | 91 |
if c.Resources != nil {
|
| 92 | 92 |
container.Cgroups.CpuShares = c.Resources.CpuShares |
| 93 | 93 |
container.Cgroups.Memory = c.Resources.Memory |
| 94 |
+ container.Cgroups.MemoryReservation = c.Resources.Memory |
|
| 94 | 95 |
container.Cgroups.MemorySwap = c.Resources.MemorySwap |
| 95 | 96 |
} |
| 96 | 97 |
return nil |
| ... | ... |
@@ -12,13 +12,14 @@ type Cgroup struct {
|
| 12 | 12 |
Name string `json:"name,omitempty"` |
| 13 | 13 |
Parent string `json:"parent,omitempty"` |
| 14 | 14 |
|
| 15 |
- DeviceAccess bool `json:"device_access,omitempty"` // name of parent cgroup or slice |
|
| 16 |
- Memory int64 `json:"memory,omitempty"` // Memory limit (in bytes) |
|
| 17 |
- MemorySwap int64 `json:"memory_swap,omitempty"` // Total memory usage (memory + swap); set `-1' to disable swap |
|
| 18 |
- CpuShares int64 `json:"cpu_shares,omitempty"` // CPU shares (relative weight vs. other containers) |
|
| 19 |
- CpuQuota int64 `json:"cpu_quota,omitempty"` // CPU hardcap limit (in usecs). Allowed cpu time in a given period. |
|
| 20 |
- CpuPeriod int64 `json:"cpu_period,omitempty"` // CPU period to be used for hardcapping (in usecs). 0 to use system default. |
|
| 21 |
- CpusetCpus string `json:"cpuset_cpus,omitempty"` // CPU to use |
|
| 15 |
+ DeviceAccess bool `json:"device_access,omitempty"` // name of parent cgroup or slice |
|
| 16 |
+ Memory int64 `json:"memory,omitempty"` // Memory limit (in bytes) |
|
| 17 |
+ MemoryReservation int64 `json:"memory_reservation,omitempty"` // Memory reservation or soft_limit (in bytes) |
|
| 18 |
+ MemorySwap int64 `json:"memory_swap,omitempty"` // Total memory usage (memory + swap); set `-1' to disable swap |
|
| 19 |
+ CpuShares int64 `json:"cpu_shares,omitempty"` // CPU shares (relative weight vs. other containers) |
|
| 20 |
+ CpuQuota int64 `json:"cpu_quota,omitempty"` // CPU hardcap limit (in usecs). Allowed cpu time in a given period. |
|
| 21 |
+ CpuPeriod int64 `json:"cpu_period,omitempty"` // CPU period to be used for hardcapping (in usecs). 0 to use system default. |
|
| 22 |
+ CpusetCpus string `json:"cpuset_cpus,omitempty"` // CPU to use |
|
| 22 | 23 |
|
| 23 | 24 |
UnitProperties [][2]string `json:"unit_properties,omitempty"` // systemd unit properties |
| 24 | 25 |
} |
| ... | ... |
@@ -13,7 +13,7 @@ type memoryGroup struct {
|
| 13 | 13 |
func (s *memoryGroup) Set(d *data) error {
|
| 14 | 14 |
dir, err := d.join("memory")
|
| 15 | 15 |
// only return an error for memory if it was not specified |
| 16 |
- if err != nil && (d.c.Memory != 0 || d.c.MemorySwap != 0) {
|
|
| 16 |
+ if err != nil && (d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0) {
|
|
| 17 | 17 |
return err |
| 18 | 18 |
} |
| 19 | 19 |
defer func() {
|
| ... | ... |
@@ -22,12 +22,15 @@ func (s *memoryGroup) Set(d *data) error {
|
| 22 | 22 |
} |
| 23 | 23 |
}() |
| 24 | 24 |
|
| 25 |
- if d.c.Memory != 0 || d.c.MemorySwap != 0 {
|
|
| 25 |
+ // Only set values if some config was specified. |
|
| 26 |
+ if d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0 {
|
|
| 26 | 27 |
if d.c.Memory != 0 {
|
| 27 | 28 |
if err := writeFile(dir, "memory.limit_in_bytes", strconv.FormatInt(d.c.Memory, 10)); err != nil {
|
| 28 | 29 |
return err |
| 29 | 30 |
} |
| 30 |
- if err := writeFile(dir, "memory.soft_limit_in_bytes", strconv.FormatInt(d.c.Memory, 10)); err != nil {
|
|
| 31 |
+ } |
|
| 32 |
+ if d.c.MemoryReservation != 0 {
|
|
| 33 |
+ if err := writeFile(dir, "memory.soft_limit_in_bytes", strconv.FormatInt(d.c.MemoryReservation, 10)); err != nil {
|
|
| 31 | 34 |
return err |
| 32 | 35 |
} |
| 33 | 36 |
} |
| ... | ... |
@@ -121,6 +121,10 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
|
| 121 | 121 |
properties = append(properties, |
| 122 | 122 |
systemd1.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))})
|
| 123 | 123 |
} |
| 124 |
+ if c.MemoryReservation != 0 {
|
|
| 125 |
+ properties = append(properties, |
|
| 126 |
+ systemd1.Property{"MemorySoftLimit", dbus.MakeVariant(uint64(c.MemoryReservation))})
|
|
| 127 |
+ } |
|
| 124 | 128 |
// TODO: MemorySwap not available in systemd |
| 125 | 129 |
|
| 126 | 130 |
if c.CpuShares != 0 {
|