Skip to content

Commit

Permalink
feature: cli supports --memory-reservation
Browse files Browse the repository at this point in the history
Signed-off-by: KevinBetterQ <1093850932@qq.com>
  • Loading branch information
KevinBetterQ committed May 29, 2019
1 parent 7235f82 commit 41c9d02
Show file tree
Hide file tree
Showing 11 changed files with 241 additions and 16 deletions.
11 changes: 11 additions & 0 deletions apis/opts/memory_reservation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package opts

import units "github.com/docker/go-units"

// ParseMemoryReservation parses the memory-reservation param of container.
func ParseMemoryReservation(memoryReservation string) (int64, error) {
if memoryReservation == "" {
return 0, nil
}
return units.RAMInBytes(memoryReservation)
}
56 changes: 56 additions & 0 deletions apis/opts/memory_reservation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package opts

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

func TestParseMemoryReservation(t *testing.T) {
type result struct {
memoryReservation int64
err error
}
type TestCase struct {
input string
expected result
}

testCases := []TestCase{
{
input: "",
expected: result{
memoryReservation: 0,
err: nil,
},
},
{
input: "0",
expected: result{
memoryReservation: 0,
err: nil,
},
},
{
input: "100m",
expected: result{
memoryReservation: 104857600,
err: nil,
},
},
{
input: "10invalid",
expected: result{
memoryReservation: -1,
err: fmt.Errorf("invalid size: '%s'", "10invalid"),
},
},
}

for _, testCase := range testCases {
memoryReservation, err := ParseMemoryReservation(testCase.input)
assert.Equal(t, testCase.expected.err, err)
assert.Equal(t, testCase.expected.memoryReservation, memoryReservation)
}
}
1 change: 1 addition & 0 deletions cli/common_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func addCommonFlags(flagSet *pflag.FlagSet) *container {

// memory
flagSet.StringVarP(&c.memory, "memory", "m", "", "Memory limit")
flagSet.StringVar(&c.memoryReservation, "memory-reservation", "", "Memory soft limit")
flagSet.StringVar(&c.memorySwap, "memory-swap", "", "Swap limit equal to memory + swap, '-1' to enable unlimited swap")
flagSet.Int64Var(&c.memorySwappiness, "memory-swappiness", 0, "Container memory swappiness [0, 100]")
flagSet.StringVar(&c.kernelMemory, "kernel-memory", "", "Kernel memory limit (in bytes)")
Expand Down
23 changes: 15 additions & 8 deletions cli/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ type container struct {
cpuperiod int64
cpuquota int64

memory string
memorySwap string
memorySwappiness int64
kernelMemory string
memory string
memoryReservation string
memorySwap string
memorySwappiness int64
kernelMemory string

memoryWmarkRatio int64
memoryExtra int64
Expand Down Expand Up @@ -108,6 +109,11 @@ func (c *container) config() (*types.ContainerCreateConfig, error) {
return nil, err
}

memoryReservation, err := opts.ParseMemoryReservation(c.memoryReservation)
if err != nil {
return nil, err
}

memorySwap, err := opts.ParseMemorySwap(c.memorySwap)
if err != nil {
return nil, err
Expand Down Expand Up @@ -233,10 +239,11 @@ func (c *container) config() (*types.ContainerCreateConfig, error) {
CPUQuota: c.cpuquota,

// memory
Memory: memory,
MemorySwap: memorySwap,
MemorySwappiness: &c.memorySwappiness,
KernelMemory: kmemory,
Memory: memory,
MemoryReservation: memoryReservation,
MemorySwap: memorySwap,
MemorySwappiness: &c.memorySwappiness,
KernelMemory: kmemory,
// FIXME: validate in client side
MemoryWmarkRatio: &c.memoryWmarkRatio,
MemoryExtra: &c.memoryExtra,
Expand Down
3 changes: 3 additions & 0 deletions daemon/mgr/container_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ var (
// MemoryWarn is warning for flag --memory
MemoryWarn = "Current Kernel does not support memory limit, discard --memory"

// MemoryReservationWarn is warning for flag --memory-reservation
MemoryReservationWarn = "Current Kernel does not support memory soft limit, discard --memory-reservation"

// MemorySwapWarn is warning for flag --memory-swap
MemorySwapWarn = "Current Kernel does not support memory swap, discard --memory-swap"

Expand Down
11 changes: 11 additions & 0 deletions daemon/mgr/container_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,17 @@ func validateResource(r *types.Resources, update bool) ([]string, error) {
r.Memory = 0
r.MemorySwap = 0
}
if r.MemoryReservation > 0 && !cgroupInfo.Memory.MemoryReservation {
logrus.Warn(MemoryReservationWarn)
warnings = append(warnings, MemoryReservationWarn)
r.MemoryReservation = 0
}
if r.MemoryReservation != 0 && r.MemoryReservation < MinMemory {
return warnings, fmt.Errorf("Minimal memory reservation should greater than 4M")
}
if r.Memory > 0 && r.MemoryReservation > 0 && r.Memory < r.MemoryReservation {
return warnings, fmt.Errorf("Minimum memory limit should be larger than memory reservation limit")
}
if r.MemorySwap > 0 && !cgroupInfo.Memory.MemorySwap {
logrus.Warn(MemorySwapWarn)
warnings = append(warnings, MemorySwapWarn)
Expand Down
71 changes: 71 additions & 0 deletions daemon/mgr/container_validation_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mgr

import (
"fmt"
"testing"

"github.com/alibaba/pouch/apis/types"
Expand Down Expand Up @@ -110,3 +111,73 @@ func TestValidateNvidiaDriver(t *testing.T) {
assert.Equal(t, tc.errExpected, err)
}
}

func TestValidateResource(t *testing.T) {

type tCase struct {
r types.Resources
update bool
warningsExpected []string
errExpected error
}

for _, tc := range []tCase{
{
r: types.Resources{
MemoryReservation: 8388608, //8m
},
warningsExpected: []string{},
errExpected: nil,
},
{
r: types.Resources{
MemoryReservation: 2097152, //2m
},
warningsExpected: []string{},
errExpected: fmt.Errorf("Minimal memory reservation should greater than 4M"),
},
{
r: types.Resources{
Memory: 8388608,
MemoryReservation: 10485760,
},
warningsExpected: []string{},
errExpected: fmt.Errorf("Minimum memory limit should be larger than memory reservation limit"),
},
{
r: types.Resources{
Memory: 8388608,
MemoryReservation: 10485760,
},
warningsExpected: []string{},
errExpected: fmt.Errorf("Minimum memory limit should be larger than memory reservation limit"),
},
{
r: types.Resources{
MemorySwap: 8388608,
Memory: 10485760,
},
warningsExpected: []string{},
errExpected: fmt.Errorf("Minimum memoryswap limit should be larger than memory limit"),
},
{
r: types.Resources{
MemorySwap: 8388608,
Memory: 0,
},
warningsExpected: []string{},
errExpected: fmt.Errorf("You should always set the Memory limit when using Memoryswap limit"),
},
{
r: types.Resources{
Memory: 2097152,
},
warningsExpected: []string{},
errExpected: fmt.Errorf("Minimal memory should greater than 4M"),
},
} {
warnings, err := validateResource(&tc.r, tc.update)
assert.Equal(t, tc.warningsExpected, warnings)
assert.Equal(t, tc.errExpected, err)
}
}
5 changes: 5 additions & 0 deletions daemon/mgr/spec_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ func setupMemory(ctx context.Context, r types.Resources, s *specs.Spec) {
memory.Limit = &v
}

if r.MemoryReservation > 0 {
v := r.MemoryReservation
memory.Reservation = &v
}

if r.MemorySwap != 0 {
v := r.MemorySwap
memory.Swap = &v
Expand Down
18 changes: 10 additions & 8 deletions pkg/system/cgroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import (

// MemoryCgroupInfo defines memory cgroup information on current machine
type MemoryCgroupInfo struct {
MemoryLimit bool
MemorySwap bool
MemorySwappiness bool
OOMKillDisable bool
MemoryLimit bool
MemoryReservation bool
MemorySwap bool
MemorySwappiness bool
OOMKillDisable bool
}

// CPUCgroupInfo defines cpu cgroup information on current machine
Expand Down Expand Up @@ -66,10 +67,11 @@ func NewCgroupInfo() *CgroupInfo {
func getMemoryCgroupInfo(root string) *MemoryCgroupInfo {
path := path.Join(root, "memory")
return &MemoryCgroupInfo{
MemoryLimit: isCgroupEnable(path, "memory.limit_in_bytes"),
MemorySwap: isCgroupEnable(path, "memory.memsw.limit_in_bytes"),
MemorySwappiness: isCgroupEnable(path, "memory.swappiness"),
OOMKillDisable: isCgroupEnable(path, "memory.oom_control"),
MemoryLimit: isCgroupEnable(path, "memory.limit_in_bytes"),
MemoryReservation: isCgroupEnable(path, "memory.soft_limit_in_bytes"),
MemorySwap: isCgroupEnable(path, "memory.memsw.limit_in_bytes"),
MemorySwappiness: isCgroupEnable(path, "memory.swappiness"),
OOMKillDisable: isCgroupEnable(path, "memory.oom_control"),
}
}

Expand Down
53 changes: 53 additions & 0 deletions test/cli_run_memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,59 @@ func (suite *PouchRunMemorySuite) TestRunWithMemoryswappiness(c *check.C) {
c.Assert(out, check.Equals, memSwappiness)
}

// TestRunWithMemoryReservation is to verify the valid running container
// with memory-reservation
func (suite *PouchRunMemorySuite) TestRunWithMemoryReservation(c *check.C) {
SkipIfFalse(c, environment.IsMemoryReservationSupport)

// test run with invalid memory reservation
cname := "TestRunWithMemoryReservationInvalid"
res := command.PouchRun("run", "-d",
"-m", "5m",
"--memory-reservation", "8m",
"--name", cname, busyboxImage, "true")
DelContainerForceMultyTime(c, cname)
c.Assert(res.ExitCode, check.Not(check.Equals), 0)

cname = "TestRunWithMemoryReservationInvalid2"
res = command.PouchRun("run", "-d",
"--memory-reservation", "1k",
"--name", cname, busyboxImage, "true")
DelContainerForceMultyTime(c, cname)
c.Assert(res.ExitCode, check.Not(check.Equals), 0)

// test run with memory reservation
cname = "TestRunWithMemoryReservation"
memReservation := "200m"
expected := "209715200"

res = command.PouchRun("run", "-d",
"--memory-reservation", memReservation,
"--name", cname,
busyboxImage,
"sleep", "10000")
defer DelContainerForceMultyTime(c, cname)
res.Assert(c, icmd.Success)

// test if the value is in inspect result
memoryReservationResult, err := inspectFilter(cname, ".HostConfig.MemoryReservation")
c.Assert(err, check.IsNil)
c.Assert(memoryReservationResult, check.Equals, expected)

// test if cgroup has record the real value
containerID, err := inspectFilter(cname, ".ID")
c.Assert(err, check.IsNil)
path := fmt.Sprintf("/sys/fs/cgroup/memory/default/%s/memory.soft_limit_in_bytes", containerID)
checkFileContains(c, path, expected)

// test if the value is correct in container
memReservationFile := "/sys/fs/cgroup/memory/memory.soft_limit_in_bytes"
res = command.PouchRun("exec", cname, "cat", memReservationFile)
res.Assert(c, icmd.Success)
out := strings.Trim(res.Stdout(), "\n")
c.Assert(out, check.Equals, expected)
}

// TestRunWithLimitedMemory is to verify the valid running container with -m
func (suite *PouchRunMemorySuite) TestRunWithLimitedMemory(c *check.C) {
SkipIfFalse(c, environment.IsMemorySupport)
Expand Down
5 changes: 5 additions & 0 deletions test/environment/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ var (
return cgroupInfo.Memory.MemoryLimit
}

// IsMemoryReservationSupport checks if memory reservation cgroup is available
IsMemoryReservationSupport = func() bool {
return cgroupInfo.Memory.MemoryReservation
}

// IsMemorySwapSupport checks if memory swap cgroup is avaible
IsMemorySwapSupport = func() bool {
return cgroupInfo.Memory.MemorySwap
Expand Down

0 comments on commit 41c9d02

Please sign in to comment.