From a3a946703ba93e7d937680897ed89eb99c52acef Mon Sep 17 00:00:00 2001 From: Sam Alba Date: Mon, 11 Mar 2013 14:28:11 -0700 Subject: [PATCH 1/6] Set the memory soft limit to the same value than the hard limit --- lxc_template.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lxc_template.go b/lxc_template.go index 931095c99..47f2058dc 100755 --- a/lxc_template.go +++ b/lxc_template.go @@ -87,6 +87,7 @@ lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod net_raw se # limits {{if .Config.Ram}} lxc.cgroup.memory.limit_in_bytes = {{.Config.Ram}} +lxc.cgroup.memory.soft_limit_in_bytes = {{.Config.Ram}} {{end}} ` From 75d04a5a7561cabba0aaf2875ef4479381096f91 Mon Sep 17 00:00:00 2001 From: Sam Alba Date: Mon, 11 Mar 2013 17:40:54 -0700 Subject: [PATCH 2/6] Added support for RamSwap in the generated LXC config (to limit the swap and have the right default settings) --- container.go | 3 ++- lxc_template.go | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/container.go b/container.go index 530c08bc3..c83750054 100644 --- a/container.go +++ b/container.go @@ -53,7 +53,8 @@ type Container struct { type Config struct { Hostname string User string - Ram int64 + Ram int64 // Memory limit (in bytes) + RamSwap int64 // Total memory usage (ram + swap); set `-1' to disable swap Ports []int Tty bool // Attach standard streams to a tty, including stdin if it is not closed. OpenStdin bool // Open stdin diff --git a/lxc_template.go b/lxc_template.go index 47f2058dc..5a37624a3 100755 --- a/lxc_template.go +++ b/lxc_template.go @@ -88,14 +88,29 @@ lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod net_raw se {{if .Config.Ram}} lxc.cgroup.memory.limit_in_bytes = {{.Config.Ram}} lxc.cgroup.memory.soft_limit_in_bytes = {{.Config.Ram}} +{{with $ramSwap := getRamSwap .Config}} +lxc.cgroup.memory.memsw.limit_in_bytes = {{$ramSwap}} +{{end}} {{end}} ` var LxcTemplateCompiled *template.Template +func getRamSwap(config *Config) int64 { + // By default, RamSwap is set to twice the size of RAM. + // If you want to omit RamSwap, set it to `-1'. + if config.RamSwap < 0 { + return 0 + } + return config.Ram * 2 +} + func init() { var err error - LxcTemplateCompiled, err = template.New("lxc").Parse(LxcTemplate) + funcMap := template.FuncMap{ + "getRamSwap": getRamSwap, + } + LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate) if err != nil { panic(err) } From 4e5ae883722ad814ef19e3f9d333f6218db752a5 Mon Sep 17 00:00:00 2001 From: Sam Alba Date: Mon, 11 Mar 2013 19:15:29 -0700 Subject: [PATCH 3/6] Implemented unit tests for the generated LXC config --- container_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++ rcli/http.go | 9 +++----- rcli/tcp.go | 12 +++++------ rcli/types.go | 10 ++++----- 4 files changed, 67 insertions(+), 19 deletions(-) diff --git a/container_test.go b/container_test.go index 8c187ecb9..f79abe336 100644 --- a/container_test.go +++ b/container_test.go @@ -1,9 +1,12 @@ package docker import ( + "bufio" "fmt" "io" "io/ioutil" + "math/rand" + "os" "sort" "strings" "testing" @@ -561,6 +564,58 @@ func TestEnv(t *testing.T) { } } +func grepFile(t *testing.T, path string, pattern string) { + f, err := os.Open(path) + if err != nil { + t.Fatal(err) + } + defer f.Close() + r := bufio.NewReader(f) + var ( + line string + ) + err = nil + for err == nil { + line, err = r.ReadString('\n') + if strings.Contains(line, pattern) == true { + return + } + } + t.Fatalf("grepFile: pattern \"%s\" not found in \"%s\"", pattern, path) +} + +func TestLXCConfig(t *testing.T) { + docker, err := newTestDocker() + if err != nil { + t.Fatal(err) + } + // Ram is allocated randomly for testing + rand.Seed(time.Now().UTC().UnixNano()) + ramMin := 33554432 + ramMax := 536870912 + ram := ramMin + rand.Intn(ramMax-ramMin) + container, err := docker.Create( + "config_test", + "/bin/true", + []string{}, + []string{testLayerPath}, + &Config{ + Hostname: "foobar", + Ram: int64(ram), + }, + ) + if err != nil { + t.Fatal(err) + } + defer docker.Destroy(container) + container.generateLXCConfig() + grepFile(t, container.lxcConfigPath, "lxc.utsname = foobar") + grepFile(t, container.lxcConfigPath, + fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", ram)) + grepFile(t, container.lxcConfigPath, + fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", ram*2)) +} + func BenchmarkRunSequencial(b *testing.B) { docker, err := newTestDocker() if err != nil { diff --git a/rcli/http.go b/rcli/http.go index e6cb5657d..cc8d3b149 100644 --- a/rcli/http.go +++ b/rcli/http.go @@ -1,13 +1,12 @@ package rcli import ( + "fmt" "net/http" "net/url" "path" - "fmt" ) - // Use this key to encode an RPC call into an URL, // eg. domain.tld/path/to/method?q=get_user&q=gordon const ARG_URL_KEY = "q" @@ -16,18 +15,16 @@ func URLToCall(u *url.URL) (method string, args []string) { return path.Base(u.Path), u.Query()[ARG_URL_KEY] } - func ListenAndServeHTTP(addr string, service Service) error { return http.ListenAndServe(addr, http.HandlerFunc( - func (w http.ResponseWriter, r *http.Request) { + func(w http.ResponseWriter, r *http.Request) { cmd, args := URLToCall(r.URL) if err := call(service, r.Body, &AutoFlush{w}, append([]string{cmd}, args...)...); err != nil { - fmt.Fprintf(w, "Error: " + err.Error() + "\n") + fmt.Fprintf(w, "Error: "+err.Error()+"\n") } })) } - type AutoFlush struct { http.ResponseWriter } diff --git a/rcli/tcp.go b/rcli/tcp.go index 0a06d459c..869a3bcdb 100644 --- a/rcli/tcp.go +++ b/rcli/tcp.go @@ -1,13 +1,13 @@ package rcli import ( + "bufio" + "encoding/json" + "fmt" "io" "io/ioutil" - "net" "log" - "fmt" - "encoding/json" - "bufio" + "net" ) // Connect to a remote endpoint using protocol `proto` and address `addr`, @@ -44,7 +44,7 @@ func ListenAndServe(proto, addr string, service Service) error { go func() { if err := Serve(conn, service); err != nil { log.Printf("Error: " + err.Error() + "\n") - fmt.Fprintf(conn, "Error: " + err.Error() + "\n") + fmt.Fprintf(conn, "Error: "+err.Error()+"\n") } conn.Close() }() @@ -53,7 +53,6 @@ func ListenAndServe(proto, addr string, service Service) error { return nil } - // Parse an rcli call on a new connection, and pass it to `service` if it // is valid. func Serve(conn io.ReadWriter, service Service) error { @@ -68,4 +67,3 @@ func Serve(conn io.ReadWriter, service Service) error { } return nil } - diff --git a/rcli/types.go b/rcli/types.go index b8572cd89..52079291b 100644 --- a/rcli/types.go +++ b/rcli/types.go @@ -8,13 +8,13 @@ package rcli // are the usual suspects. import ( + "errors" + "flag" "fmt" "io" - "reflect" - "flag" "log" + "reflect" "strings" - "errors" ) type Service interface { @@ -25,7 +25,6 @@ type Service interface { type Cmd func(io.ReadCloser, io.Writer, ...string) error type CmdMethod func(Service, io.ReadCloser, io.Writer, ...string) error - func call(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error { if len(args) == 0 { args = []string{"help"} @@ -63,7 +62,7 @@ func getMethod(service Service, name string) Cmd { return nil } } - methodName := "Cmd"+strings.ToUpper(name[:1])+strings.ToLower(name[1:]) + methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:]) method, exists := reflect.TypeOf(service).MethodByName(methodName) if !exists { return nil @@ -91,4 +90,3 @@ func Subcmd(output io.Writer, name, signature, description string) *flag.FlagSet } return flags } - From 948961831ab6cfb0c94e1e80552f4438d1f434a7 Mon Sep 17 00:00:00 2001 From: Sam Alba Date: Mon, 11 Mar 2013 19:25:02 -0700 Subject: [PATCH 4/6] Renamed Container property Ram to Memory before it is too late --- container.go | 14 +++++++------- container_test.go | 18 +++++++++--------- lxc_template.go | 20 ++++++++++---------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/container.go b/container.go index c83750054..9aad176ed 100644 --- a/container.go +++ b/container.go @@ -51,13 +51,13 @@ type Container struct { } type Config struct { - Hostname string - User string - Ram int64 // Memory limit (in bytes) - RamSwap int64 // Total memory usage (ram + swap); set `-1' to disable swap - Ports []int - Tty bool // Attach standard streams to a tty, including stdin if it is not closed. - OpenStdin bool // Open stdin + Hostname string + User string + Memory int64 // Memory limit (in bytes) + MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap + Ports []int + Tty bool // Attach standard streams to a tty, including stdin if it is not closed. + OpenStdin bool // Open stdin } type NetworkSettings struct { diff --git a/container_test.go b/container_test.go index f79abe336..f9c835edf 100644 --- a/container_test.go +++ b/container_test.go @@ -24,7 +24,7 @@ func TestStart(t *testing.T) { []string{"-al"}, []string{testLayerPath}, &Config{ - Ram: 33554432, + Memory: 33554432, }, ) if err != nil { @@ -60,7 +60,7 @@ func TestRun(t *testing.T) { []string{"-al"}, []string{testLayerPath}, &Config{ - Ram: 33554432, + Memory: 33554432, }, ) if err != nil { @@ -589,11 +589,11 @@ func TestLXCConfig(t *testing.T) { if err != nil { t.Fatal(err) } - // Ram is allocated randomly for testing + // Memory is allocated randomly for testing rand.Seed(time.Now().UTC().UnixNano()) - ramMin := 33554432 - ramMax := 536870912 - ram := ramMin + rand.Intn(ramMax-ramMin) + memMin := 33554432 + memMax := 536870912 + mem := memMin + rand.Intn(memMax-memMin) container, err := docker.Create( "config_test", "/bin/true", @@ -601,7 +601,7 @@ func TestLXCConfig(t *testing.T) { []string{testLayerPath}, &Config{ Hostname: "foobar", - Ram: int64(ram), + Memory: int64(mem), }, ) if err != nil { @@ -611,9 +611,9 @@ func TestLXCConfig(t *testing.T) { container.generateLXCConfig() grepFile(t, container.lxcConfigPath, "lxc.utsname = foobar") grepFile(t, container.lxcConfigPath, - fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", ram)) + fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem)) grepFile(t, container.lxcConfigPath, - fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", ram*2)) + fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2)) } func BenchmarkRunSequencial(b *testing.B) { diff --git a/lxc_template.go b/lxc_template.go index 5a37624a3..5d92c9e6d 100755 --- a/lxc_template.go +++ b/lxc_template.go @@ -85,10 +85,10 @@ lxc.mount.entry = /etc/resolv.conf {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0 lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod net_raw setfcap setpcap sys_admin sys_boot sys_module sys_nice sys_pacct sys_rawio sys_resource sys_time sys_tty_config # limits -{{if .Config.Ram}} -lxc.cgroup.memory.limit_in_bytes = {{.Config.Ram}} -lxc.cgroup.memory.soft_limit_in_bytes = {{.Config.Ram}} -{{with $ramSwap := getRamSwap .Config}} +{{if .Config.Memory}} +lxc.cgroup.memory.limit_in_bytes = {{.Config.Memory}} +lxc.cgroup.memory.soft_limit_in_bytes = {{.Config.Memory}} +{{with $ramSwap := getMemorySwap .Config}} lxc.cgroup.memory.memsw.limit_in_bytes = {{$ramSwap}} {{end}} {{end}} @@ -96,19 +96,19 @@ lxc.cgroup.memory.memsw.limit_in_bytes = {{$ramSwap}} var LxcTemplateCompiled *template.Template -func getRamSwap(config *Config) int64 { - // By default, RamSwap is set to twice the size of RAM. - // If you want to omit RamSwap, set it to `-1'. - if config.RamSwap < 0 { +func getMemorySwap(config *Config) int64 { + // By default, MemorySwap is set to twice the size of RAM. + // If you want to omit MemorySwap, set it to `-1'. + if config.MemorySwap < 0 { return 0 } - return config.Ram * 2 + return config.Memory * 2 } func init() { var err error funcMap := template.FuncMap{ - "getRamSwap": getRamSwap, + "getMemorySwap": getMemorySwap, } LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate) if err != nil { From 3684b67572605de492abba041695bd80ef42bcb5 Mon Sep 17 00:00:00 2001 From: Sam Alba Date: Mon, 11 Mar 2013 19:51:24 -0700 Subject: [PATCH 5/6] Added -m to the run command in order to set a memory limit to a container --- server/server.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/server/server.go b/server/server.go index bc642cdbb..a490641d2 100644 --- a/server/server.go +++ b/server/server.go @@ -712,10 +712,18 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string return errors.New("No such container: " + cmd.Arg(0)) } -func (srv *Server) CreateContainer(img *image.Image, ports []int, user string, tty bool, openStdin bool, comment string, cmd string, args ...string) (*docker.Container, error) { +func (srv *Server) CreateContainer(img *image.Image, ports []int, user string, + tty bool, openStdin bool, memory int64, comment string, cmd string, args ...string) (*docker.Container, error) { id := future.RandomId()[:8] container, err := srv.containers.Create(id, cmd, args, img.Layers, - &docker.Config{Hostname: id, Ports: ports, User: user, Tty: tty, OpenStdin: openStdin}) + &docker.Config{ + Hostname: id, + Ports: ports, + User: user, + Tty: tty, + OpenStdin: openStdin, + Memory: memory, + }) if err != nil { return nil, err } @@ -799,6 +807,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached") fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty") fl_comment := cmd.String("c", "", "Comment") + fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)") var fl_ports ports cmd.Var(&fl_ports, "p", "Map a network port to the container") if err := cmd.Parse(args); err != nil { @@ -826,7 +835,8 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) return errors.New("No such image: " + name) } // Create new container - container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty, *fl_stdin, *fl_comment, cmdline[0], cmdline[1:]...) + container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty, + *fl_stdin, *fl_memory, *fl_comment, cmdline[0], cmdline[1:]...) if err != nil { return errors.New("Error creating container: " + err.Error()) } From f8fee421819e3c272e1c20890da7c90b4da971ad Mon Sep 17 00:00:00 2001 From: Sam Alba Date: Mon, 11 Mar 2013 19:55:14 -0700 Subject: [PATCH 6/6] Missed a rename --- lxc_template.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxc_template.go b/lxc_template.go index 5d92c9e6d..2a60e8b96 100755 --- a/lxc_template.go +++ b/lxc_template.go @@ -88,8 +88,8 @@ lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod net_raw se {{if .Config.Memory}} lxc.cgroup.memory.limit_in_bytes = {{.Config.Memory}} lxc.cgroup.memory.soft_limit_in_bytes = {{.Config.Memory}} -{{with $ramSwap := getMemorySwap .Config}} -lxc.cgroup.memory.memsw.limit_in_bytes = {{$ramSwap}} +{{with $memSwap := getMemorySwap .Config}} +lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}} {{end}} {{end}} `