mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
parent
c2f2e5c0a0
commit
b9bf3cbe3e
15 changed files with 240 additions and 22 deletions
|
@ -3,6 +3,7 @@ package cgroup
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -40,8 +41,20 @@ func updateGOMAXPROCSToCPUQuota() {
|
||||||
runtime.GOMAXPROCS(gomaxprocs)
|
runtime.GOMAXPROCS(gomaxprocs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCPUStat(sysPath, cgroupPath, statName string) (int64, error) {
|
||||||
|
n, err := readInt64(path.Join(sysPath, statName))
|
||||||
|
if err == nil {
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
subPath, err := grepFirstMatch(cgroupPath, "cpu,", 2, ":")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return readInt64(path.Join(sysPath, subPath, statName))
|
||||||
|
}
|
||||||
|
|
||||||
func getCPUQuota() float64 {
|
func getCPUQuota() float64 {
|
||||||
quotaUS, err := readInt64("/sys/fs/cgroup/cpu/cpu.cfs_quota_us", "cat /sys/fs/cgroup/cpu$(cat /proc/self/cgroup | grep cpu, | cut -d: -f3)/cpu.cfs_quota_us")
|
quotaUS, err := getCPUStat("/sys/fs/cgroup/cpu", "/proc/self/cgroup", "cpu.cfs_quota_us")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -50,7 +63,7 @@ func getCPUQuota() float64 {
|
||||||
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/685#issuecomment-674423728
|
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/685#issuecomment-674423728
|
||||||
return getOnlineCPUCount()
|
return getOnlineCPUCount()
|
||||||
}
|
}
|
||||||
periodUS, err := readInt64("/sys/fs/cgroup/cpu/cpu.cfs_period_us", "cat /sys/fs/cgroup/cpu$(cat /proc/self/cgroup | grep cpu, | cut -d: -f3)/cpu.cfs_period_us")
|
periodUS, err := getCPUStat("/sys/fs/cgroup/cpu", "/proc/self/cgroup", "cpu.cfs_period_us")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,3 +22,35 @@ func TestCountCPUs(t *testing.T) {
|
||||||
f("0-3", 4)
|
f("0-3", 4)
|
||||||
f("0-6", 7)
|
f("0-6", 7)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetCPUStatQuota(t *testing.T) {
|
||||||
|
f := func(sysPath, cgroupPath string, want int64, wantErr bool) {
|
||||||
|
t.Helper()
|
||||||
|
got, err := getCPUStat(sysPath, cgroupPath, "cpu.cfs_quota_us")
|
||||||
|
if (err != nil && !wantErr) || (err == nil && wantErr) {
|
||||||
|
t.Fatalf("unxpected error value: %v, want err: %v", err, wantErr)
|
||||||
|
}
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("unxpected result, got: %d, want %d", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f("testdata/", "testdata/self/cgroup", -1, false)
|
||||||
|
f("testdata/cgroup", "testdata/self/cgroup", 10, false)
|
||||||
|
f("testdata/", "testdata/missing_folder", 0, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCPUStatPeriod(t *testing.T) {
|
||||||
|
f := func(sysPath, cgroupPath string, want int64, wantErr bool) {
|
||||||
|
t.Helper()
|
||||||
|
got, err := getCPUStat(sysPath, cgroupPath, "cpu.cfs_period_us")
|
||||||
|
if (err != nil && !wantErr) || (err == nil && wantErr) {
|
||||||
|
t.Fatalf("unxpected error value: %v, want err: %v", err, wantErr)
|
||||||
|
}
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("unxpected result, got: %d, want %d", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f("testdata/", "testdata/self/cgroup", 100000, false)
|
||||||
|
f("testdata/cgroup", "testdata/self/cgroup", 500000, false)
|
||||||
|
f("testdata/", "testdata/missing_folder", 0, true)
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
package cgroup
|
package cgroup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
// GetMemoryLimit returns cgroup memory limit
|
// GetMemoryLimit returns cgroup memory limit
|
||||||
func GetMemoryLimit() int64 {
|
func GetMemoryLimit() int64 {
|
||||||
// Try determining the amount of memory inside docker container.
|
// Try determining the amount of memory inside docker container.
|
||||||
|
@ -8,24 +13,60 @@ func GetMemoryLimit() int64 {
|
||||||
// Read memory limit according to https://unix.stackexchange.com/questions/242718/how-to-find-out-how-much-memory-lxc-container-is-allowed-to-consume
|
// 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.
|
// This should properly determine the limit inside lxc container.
|
||||||
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/84
|
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/84
|
||||||
n, err := readInt64("/sys/fs/cgroup/memory/memory.limit_in_bytes", "cat /sys/fs/cgroup/memory$(cat /proc/self/cgroup | grep memory | cut -d: -f3)/memory.limit_in_bytes")
|
n, err := getMemLimit("/sys/fs/cgroup/", "/proc/self/cgroup")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMemLimit(sysPath, cgroupPath string) (int64, error) {
|
||||||
|
n, err := readInt64(path.Join(sysPath, "memory.limit_in_bytes"))
|
||||||
|
if err == nil {
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
subPath, err := grepFirstMatch(cgroupPath, "memory", 2, ":")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return readInt64(path.Join(sysPath, subPath, "memory.limit_in_bytes"))
|
||||||
|
}
|
||||||
|
|
||||||
// GetHierarchicalMemoryLimit returns hierarchical memory limit
|
// GetHierarchicalMemoryLimit returns hierarchical memory limit
|
||||||
|
// https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
|
||||||
func GetHierarchicalMemoryLimit() int64 {
|
func GetHierarchicalMemoryLimit() int64 {
|
||||||
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/699
|
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/699
|
||||||
n, err := readInt64FromCommand("cat /sys/fs/cgroup/memory/memory.stat | grep hierarchical_memory_limit | cut -d' ' -f 2")
|
n, err := getHierarchicalMemoryLimit("/sys/fs/cgroup/memory", "/proc/self/cgroup")
|
||||||
if err == nil {
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
n, err = readInt64FromCommand(
|
|
||||||
"cat /sys/fs/cgroup/memory$(cat /proc/self/cgroup | grep memory | cut -d: -f3)/memory.stat | grep hierarchical_memory_limit | cut -d' ' -f 2")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getHierarchicalMemoryLimit(sysPath, cgroupPath string) (int64, error) {
|
||||||
|
n, err := getMemStatDirect(sysPath)
|
||||||
|
if err == nil {
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
return getMemStatSubPath(sysPath, cgroupPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMemStatDirect(sysPath string) (int64, error) {
|
||||||
|
memStat, err := grepFirstMatch(path.Join(sysPath, "memory.stat"), "hierarchical_memory_limit", 1, " ")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return strconv.ParseInt(memStat, 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMemStatSubPath(sysPath, cgroupPath string) (int64, error) {
|
||||||
|
cgrps, err := grepFirstMatch(cgroupPath, "memory", 2, ":")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
memStat, err := grepFirstMatch(path.Join(sysPath, cgrps, "memory.stat"), "hierarchical_memory_limit", 1, " ")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return strconv.ParseInt(memStat, 10, 64)
|
||||||
|
}
|
||||||
|
|
35
lib/cgroup/mem_test.go
Normal file
35
lib/cgroup/mem_test.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package cgroup
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestGetMemLimit(t *testing.T) {
|
||||||
|
f := func(sysPath, cgroupPath string, want int64, wantErr bool) {
|
||||||
|
t.Helper()
|
||||||
|
got, err := getMemLimit(sysPath, cgroupPath)
|
||||||
|
if (err != nil && !wantErr) || (err == nil && wantErr) {
|
||||||
|
t.Fatalf("unxpected error: %v, wantErr: %v", err, wantErr)
|
||||||
|
}
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("unxpected result, got: %d, want %d", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f("testdata/", "testdata/self/cgroup", 9223372036854771712, false)
|
||||||
|
f("testdata/cgroup", "testdata/self/cgroup", 523372036854771712, false)
|
||||||
|
f("testdata/", "testdata/none_existing_folder", 0, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMemHierarchical(t *testing.T) {
|
||||||
|
f := func(sysPath, cgroupPath string, want int64, wantErr bool) {
|
||||||
|
t.Helper()
|
||||||
|
got, err := getHierarchicalMemoryLimit(sysPath, cgroupPath)
|
||||||
|
if (err != nil && !wantErr) || (err == nil && wantErr) {
|
||||||
|
t.Fatalf("unxpected error: %v, wantErr: %v", err, wantErr)
|
||||||
|
}
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("unxpected result, got: %d, want %d", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f("testdata/", "testdata/self/cgroup", 16, false)
|
||||||
|
f("testdata/cgroup", "testdata/self/cgroup", 120, false)
|
||||||
|
f("testdata/", "testdata/none_existing_folder", 0, true)
|
||||||
|
}
|
1
lib/cgroup/testdata/cgroup/cpu.cfs_period_us
vendored
Normal file
1
lib/cgroup/testdata/cgroup/cpu.cfs_period_us
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
500000
|
1
lib/cgroup/testdata/cgroup/cpu.cfs_quota_us
vendored
Normal file
1
lib/cgroup/testdata/cgroup/cpu.cfs_quota_us
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
10
|
1
lib/cgroup/testdata/cgroup/memory.limit_in_bytes
vendored
Normal file
1
lib/cgroup/testdata/cgroup/memory.limit_in_bytes
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
523372036854771712
|
31
lib/cgroup/testdata/cgroup/memory.stat
vendored
Normal file
31
lib/cgroup/testdata/cgroup/memory.stat
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
rss 2
|
||||||
|
rss_huge 3
|
||||||
|
mapped_file 4
|
||||||
|
dirty 5
|
||||||
|
writeback 6
|
||||||
|
pgpgin 7
|
||||||
|
pgpgout 8
|
||||||
|
pgfault 9
|
||||||
|
pgmajfault 10
|
||||||
|
inactive_anon 11
|
||||||
|
active_anon 12
|
||||||
|
inactive_file 13
|
||||||
|
active_file 14
|
||||||
|
unevictable 15
|
||||||
|
hierarchical_memory_limit 120
|
||||||
|
hierarchical_memsw_limit 17
|
||||||
|
total_cache 18
|
||||||
|
total_rss 19
|
||||||
|
total_rss_huge 20
|
||||||
|
total_mapped_file 21
|
||||||
|
total_dirty 22
|
||||||
|
total_writeback 23
|
||||||
|
total_pgpgin 24
|
||||||
|
total_pgpgout 25
|
||||||
|
total_pgfault 26
|
||||||
|
total_pgmajfault 27
|
||||||
|
total_inactive_anon 28
|
||||||
|
total_active_anon 29
|
||||||
|
total_inactive_file 30
|
||||||
|
total_active_file 31
|
||||||
|
total_unevictable 32
|
|
@ -0,0 +1 @@
|
||||||
|
100000
|
|
@ -0,0 +1 @@
|
||||||
|
-1
|
|
@ -0,0 +1 @@
|
||||||
|
9223372036854771712
|
|
@ -0,0 +1,31 @@
|
||||||
|
rss 2
|
||||||
|
rss_huge 3
|
||||||
|
mapped_file 4
|
||||||
|
dirty 5
|
||||||
|
writeback 6
|
||||||
|
pgpgin 7
|
||||||
|
pgpgout 8
|
||||||
|
pgfault 9
|
||||||
|
pgmajfault 10
|
||||||
|
inactive_anon 11
|
||||||
|
active_anon 12
|
||||||
|
inactive_file 13
|
||||||
|
active_file 14
|
||||||
|
unevictable 15
|
||||||
|
hierarchical_memory_limit 16
|
||||||
|
hierarchical_memsw_limit 17
|
||||||
|
total_cache 18
|
||||||
|
total_rss 19
|
||||||
|
total_rss_huge 20
|
||||||
|
total_mapped_file 21
|
||||||
|
total_dirty 22
|
||||||
|
total_writeback 23
|
||||||
|
total_pgpgin 24
|
||||||
|
total_pgpgout 25
|
||||||
|
total_pgfault 26
|
||||||
|
total_pgmajfault 27
|
||||||
|
total_inactive_anon 28
|
||||||
|
total_active_anon 29
|
||||||
|
total_inactive_file 30
|
||||||
|
total_active_file 31
|
||||||
|
total_unevictable 32
|
1
lib/cgroup/testdata/memory.stat
vendored
Normal file
1
lib/cgroup/testdata/memory.stat
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
9223372036854771712
|
13
lib/cgroup/testdata/self/cgroup
vendored
Normal file
13
lib/cgroup/testdata/self/cgroup
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
12:perf_event:/docker/74c9abf42b88b9a35b1b56061b08303e56fd1707fe5c5b4df93324dedb36b5db
|
||||||
|
11:rdma:/
|
||||||
|
10:pids:/docker/74c9abf42b88b9a35b1b56061b08303e56fd1707fe5c5b4df93324dedb36b5db
|
||||||
|
9:freezer:/docker/74c9abf42b88b9a35b1b56061b08303e56fd1707fe5c5b4df93324dedb36b5db
|
||||||
|
8:memory:/docker/74c9abf42b88b9a35b1b56061b08303e56fd1707fe5c5b4df93324dedb36b5db
|
||||||
|
7:devices:/docker/74c9abf42b88b9a35b1b56061b08303e56fd1707fe5c5b4df93324dedb36b5db
|
||||||
|
6:cpuset:/docker/74c9abf42b88b9a35b1b56061b08303e56fd1707fe5c5b4df93324dedb36b5db
|
||||||
|
5:hugetlb:/docker/74c9abf42b88b9a35b1b56061b08303e56fd1707fe5c5b4df93324dedb36b5db
|
||||||
|
4:net_cls,net_prio:/docker/74c9abf42b88b9a35b1b56061b08303e56fd1707fe5c5b4df93324dedb36b5db
|
||||||
|
3:blkio:/docker/74c9abf42b88b9a35b1b56061b08303e56fd1707fe5c5b4df93324dedb36b5db
|
||||||
|
2:cpu,cpuacct:/docker/74c9abf42b88b9a35b1b56061b08303e56fd1707fe5c5b4df93324dedb36b5db
|
||||||
|
1:name=systemd:/docker/74c9abf42b88b9a35b1b56061b08303e56fd1707fe5c5b4df93324dedb36b5db
|
||||||
|
0::/system.slice/containerd.service
|
|
@ -1,27 +1,42 @@
|
||||||
package cgroup
|
package cgroup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os/exec"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func readInt64(path, altCommand string) (int64, error) {
|
// grepFirstMatch search match line at file and returns item from it by index with given delimiter.
|
||||||
|
func grepFirstMatch(sourcePath string, match string, index int, delimiter string) (string, error) {
|
||||||
|
f, err := os.Open(sourcePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer func() { _ = f.Close() }()
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
for scanner.Scan() {
|
||||||
|
text := scanner.Text()
|
||||||
|
if !strings.Contains(text, match) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
split := strings.Split(text, delimiter)
|
||||||
|
if len(split) < index {
|
||||||
|
return "", fmt.Errorf("needed index line: %d, wasn't found at line: %q at file: %q", index, text, sourcePath)
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(split[index]), nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("stat: %q, wasn't found at file: %q", match, sourcePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readInt64(path string) (int64, error) {
|
||||||
data, err := ioutil.ReadFile(path)
|
data, err := ioutil.ReadFile(path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
data = bytes.TrimSpace(data)
|
data = bytes.TrimSpace(data)
|
||||||
return strconv.ParseInt(string(data), 10, 64)
|
return strconv.ParseInt(string(data), 10, 64)
|
||||||
}
|
}
|
||||||
return readInt64FromCommand(altCommand)
|
return 0, err
|
||||||
}
|
|
||||||
|
|
||||||
func readInt64FromCommand(command string) (int64, error) {
|
|
||||||
cmd := exec.Command("/bin/sh", "-c", command)
|
|
||||||
data, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
data = bytes.TrimSpace(data)
|
|
||||||
return strconv.ParseInt(string(data), 10, 64)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue