From 593bd35aaad9c7732ca34dfb1205a5c87273f17a Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Fri, 28 Jun 2019 15:34:24 +0300 Subject: [PATCH] lib/memory: an attempt to read proper memory limit inside LXC container Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/84 --- lib/memory/memory_linux.go | 48 +++++++++++++++++++++++++++------ lib/memory/memory_linux_test.go | 19 +++++++++++++ 2 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 lib/memory/memory_linux_test.go diff --git a/lib/memory/memory_linux.go b/lib/memory/memory_linux.go index 4dc3510fb..5e75eb33d 100644 --- a/lib/memory/memory_linux.go +++ b/lib/memory/memory_linux.go @@ -2,6 +2,7 @@ package memory import ( "io/ioutil" + "os/exec" "strconv" "syscall" @@ -19,17 +20,48 @@ func sysTotalMemory() int { // See https://stackoverflow.com/questions/42187085/check-mem-limit-within-a-docker-container . data, err := ioutil.ReadFile("/sys/fs/cgroup/memory/memory.limit_in_bytes") if err != nil { - return totalMem + // Try determining the amount of memory inside lxc container. + mem, err := readLXCMemoryLimit(totalMem) + if err != nil { + return totalMem + } + return mem } - for len(data) > 0 && data[len(data)-1] == '\n' { - data = data[:len(data)-1] - } - mem, err := strconv.Atoi(string(data)) + mem, err := readPositiveInt(data, totalMem) if err != nil { return totalMem } - if mem > totalMem { - mem = totalMem - } return mem } + +func readLXCMemoryLimit(totalMem int) (int, error) { + // Read memory limit according to https://unix.stackexchange.com/questions/242718/how-to-find-out-how-much-memory-lxc-container-is-allowed-to-consume + // This should properly determine the limit inside lxc container. + // See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/84 + cmd := exec.Command("/bin/sh", "-c", + `cat $(mount | grep cgroup | grep memory | cut -d" " -f3)$(cat /proc/self/cgroup | grep memory | cut -d: -f3)/memory.limit_in_bytes`) + data, err := cmd.Output() + if err != nil { + return 0, err + } + return readPositiveInt(data, totalMem) +} + +func readPositiveInt(data []byte, maxN int) (int, error) { + for len(data) > 0 && data[len(data)-1] == '\n' { + data = data[:len(data)-1] + } + n, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return 0, err + } + if int64(n) < 0 || int64(int(n)) != int64(n) { + // Int overflow. + return maxN, nil + } + ni := int(n) + if ni > maxN { + return maxN, nil + } + return ni, nil +} diff --git a/lib/memory/memory_linux_test.go b/lib/memory/memory_linux_test.go new file mode 100644 index 000000000..b74bb9858 --- /dev/null +++ b/lib/memory/memory_linux_test.go @@ -0,0 +1,19 @@ +package memory + +import ( + "testing" +) + +func TestReadLXCMemoryLimit(t *testing.T) { + const maxMem = 1<<31 - 1 + n, err := readLXCMemoryLimit(maxMem) + if err != nil { + t.Fatalf("unexpected error in readLXCMemoryLimit: %s", err) + } + if n < 0 { + t.Fatalf("n must be positive; got %d", n) + } + if n > maxMem { + t.Fatalf("n must be smaller than maxMem=%d; got %d", maxMem, n) + } +}