From 1dd7d52e566ee06ac9c8511c500710fb57ae87cf Mon Sep 17 00:00:00 2001 From: Soulou Date: Wed, 28 Oct 2015 22:23:51 +0100 Subject: [PATCH] Better memory stats support --- CHANGELOG.md | 3 +- README.md | 15 +++++++-- client/client.go | 3 ++ mem/mem.go | 65 ++++++++++++++++++++++++++++++++---- server/acadock_monitoring.go | 17 +++++++--- 5 files changed, 88 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5990618e..82e82dc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # CHANGELOG -## To be released +## v0.2.0 * Can run in a container * Stats of net interface of containers +* More memory stats ## v0.1.2 diff --git a/README.md b/README.md index d261fe84..0ed412a2 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,23 @@ docker run -v /sys/fs/cgroup:/host/cgroup:ro -e CGROUP_DIR=/host/cgroup API --- -* Memory consumption (in bytes) +* Memory consumption `GET /containers/:id/mem` Return 200 OK - Content-Type: text/plain + Content-Type: application/json + +```json +{ + "mem_usage": 123, + "mem_limit": 5000, + "max_mem_usage": 500, + "max_swap_mem_usage": 200, + "swap_mem_usage": 145, + "swap_mem_limit": 1000 +} +``` * CPU usage (percentage) diff --git a/client/client.go b/client/client.go index 51dacfea..6cc27c9d 100644 --- a/client/client.go +++ b/client/client.go @@ -6,9 +6,12 @@ import ( "net/url" "strconv" + "github.com/Scalingo/acadock-monitoring/mem" "gopkg.in/errgo.v1" ) +type MemoryUsage mem.Usage + type Client struct { Endpoint string } diff --git a/mem/mem.go b/mem/mem.go index 08e23df5..dcf46347 100644 --- a/mem/mem.go +++ b/mem/mem.go @@ -4,27 +4,78 @@ import ( "log" "os" "strconv" + "sync" "github.com/Scalingo/acadock-monitoring/config" "github.com/Scalingo/acadock-monitoring/docker" ) const ( - LXC_MEM_USAGE_FILE = "memory.usage_in_bytes" + LXC_MEM_USAGE_FILE = "memory.usage_in_bytes" + LXC_SWAP_MEM_USAGE_FILE = "memory.memsw.usage_in_bytes" + LXC_MEM_LIMIT_FILE = "memory.limit_in_bytes" + LXC_SWAP_MEM_LIMIT_FILE = "memory.memsw.limit_in_bytes" + LXC_MAX_MEM_FILE = "memory.max_usage_in_bytes" + LXC_MAX_SWAP_MEM_FILE = "memory.memsw.max_usage_in_bytes" ) -func GetUsage(id string) (int64, error) { +type Usage struct { + MemoryUsage int64 `json:"memory_usage"` + SwapMemoryUsage int64 `json:"swap_memory_usage"` + MemoryLimit int64 `json:"memory_limit"` + SwapMemoryLimit int64 `json:"swap_memory_limit"` + MaxMemoryUsage int64 `json:"max_memory_usage"` + MaxSwapMemoryUsage int64 `json:"max_swap_memory_usage"` +} + +func GetUsage(id string) (Usage, error) { + usage := Usage{} id, err := docker.ExpandId(id) if err != nil { log.Println("Error when expanding id:", err) - return 0, err + return usage, err + } + + errors := make(chan error) + wg := &sync.WaitGroup{} + wg.Add(6) + go readMemoryCgroupInt64Async(id, LXC_MAX_MEM_FILE, &usage.MaxMemoryUsage, errors, wg) + go readMemoryCgroupInt64Async(id, LXC_MAX_SWAP_MEM_FILE, &usage.MaxSwapMemoryUsage, errors, wg) + go readMemoryCgroupInt64Async(id, LXC_MEM_LIMIT_FILE, &usage.MemoryLimit, errors, wg) + go readMemoryCgroupInt64Async(id, LXC_MEM_USAGE_FILE, &usage.MemoryUsage, errors, wg) + go readMemoryCgroupInt64Async(id, LXC_SWAP_MEM_LIMIT_FILE, &usage.SwapMemoryLimit, errors, wg) + go readMemoryCgroupInt64Async(id, LXC_SWAP_MEM_USAGE_FILE, &usage.SwapMemoryUsage, errors, wg) + + go func() { + wg.Wait() + close(errors) + }() + + for err := range errors { + if err != nil { + return usage, err + } } - path := config.CgroupPath("memory", id) + "/" + LXC_MEM_USAGE_FILE + return usage, nil +} + +func readMemoryCgroupInt64Async(id, file string, ret *int64, errors chan error, wg *sync.WaitGroup) { + defer wg.Done() + val, err := readMemoryCgroupInt64(id, file) + if err != nil { + errors <- err + return + } + *ret = val +} + +func readMemoryCgroupInt64(id, file string) (int64, error) { + path := config.CgroupPath("memory", id) + "/" + file f, err := os.Open(path) if err != nil { log.Println("Error while opening:", err) - return 0, err + return -1, err } defer f.Close() @@ -32,14 +83,14 @@ func GetUsage(id string) (int64, error) { n, err := f.Read(buffer) if err != nil { log.Println("Error while reading ", path, ":", err) - return 0, err + return -1, err } buffer = buffer[:n-1] val, err := strconv.ParseInt(string(buffer), 10, 64) if err != nil { log.Println("Error while parsing ", string(buffer), " : ", err) - return 0, err + return -1, err } return val, nil diff --git a/server/acadock_monitoring.go b/server/acadock_monitoring.go index 2354b83c..11ca89f2 100644 --- a/server/acadock_monitoring.go +++ b/server/acadock_monitoring.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "flag" + "fmt" "log" "strconv" @@ -15,15 +16,21 @@ import ( "github.com/codegangsta/martini" ) -func containerMemUsageHandler(params martini.Params) (int, string) { +func containerMemUsageHandler(params martini.Params, res http.ResponseWriter) { id := params["id"] - containerMemory, err := mem.GetUsage(id) + containerMemoryUsage, err := mem.GetUsage(id) if err != nil { - return 500, err.Error() + res.WriteHeader(500) + res.Header().Set("Content-Type", "text/plain") + fmt.Fprintf(res, err.Error()) + return } - containerMemoryStr := strconv.FormatInt(containerMemory, 10) - return 200, containerMemoryStr + + res.WriteHeader(200) + res.Header().Set("Content-Type", "application/json") + + json.NewEncoder(res).Encode(&containerMemoryUsage) } func containerCpuUsageHandler(params martini.Params) (int, string) {