adds cgroupsv2 support (#1283)

* adds cgroupv2 limits support
https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1269

* small fix

* changes Atoi to ParseUint
This commit is contained in:
Nikolay 2021-05-13 09:02:13 +03:00 committed by GitHub
parent a22a17dc66
commit b50024812e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 74 additions and 6 deletions

View file

@ -1,6 +1,7 @@
package cgroup package cgroup
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"runtime" "runtime"
@ -41,20 +42,28 @@ func updateGOMAXPROCSToCPUQuota() {
} }
func getCPUQuota() float64 { func getCPUQuota() float64 {
quotaUS, err := getCPUStat("cpu.cfs_quota_us") cpuQuota, err := getCPUStatGeneric()
if err != nil { if err != nil {
return 0 return 0
} }
if quotaUS <= 0 {
if cpuQuota <= 0 {
// The quota isn't set. This may be the case in multilevel containers. // The quota isn't set. This may be the case in multilevel containers.
// 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 := getCPUStat("cpu.cfs_period_us") return cpuQuota
if err != nil { }
return 0
func getCPUStatGeneric() (float64, error) {
quotaUS, err := getCPUStat("cpu.cfs_quota_us")
if err == nil {
periodUS, err := getCPUStat("cpu.cfs_period_us")
if err == nil {
return float64(quotaUS) / float64(periodUS), nil
}
} }
return float64(quotaUS) / float64(periodUS) return getCPUStatV2("/sys/fs/cgroup", "/proc/self/cgroup")
} }
func getCPUStat(statName string) (int64, error) { func getCPUStat(statName string) (int64, error) {
@ -74,6 +83,35 @@ func getOnlineCPUCount() float64 {
return n return n
} }
func getCPUStatV2(sysPrefix, cgroupPath string) (float64, error) {
data, err := getFileContents("cpu.max", sysPrefix, cgroupPath, "")
if err != nil {
return 0, err
}
return parseCPUMax(data)
}
// https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#cpu
func parseCPUMax(data string) (float64, error) {
data = strings.TrimRight(data, "\r\n")
bounds := strings.Split(data, " ")
if len(bounds) != 2 {
return 0, fmt.Errorf("unexpected count: %d, want quota and period, got: %s", len(bounds), data)
}
if bounds[0] == "max" {
return -1, nil
}
quota, err := strconv.ParseUint(bounds[0], 10, 64)
if err != nil {
return 0, err
}
period, err := strconv.ParseUint(bounds[1], 10, 64)
if err != nil {
return 0, err
}
return float64(quota) / float64(period), nil
}
func countCPUs(data string) int { func countCPUs(data string) int {
data = strings.TrimSpace(data) data = strings.TrimSpace(data)
n := 0 n := 0

View file

@ -22,3 +22,17 @@ func TestCountCPUs(t *testing.T) {
f("0-3", 4) f("0-3", 4)
f("0-6", 7) f("0-6", 7)
} }
func TestGetCPUStatV2(t *testing.T) {
f := func(sysPrefix, cgroupPath string, expectedCPU float64) {
t.Helper()
got, err := getCPUStatV2(sysPrefix, cgroupPath)
if err != nil {
t.Fatalf("unexpected error: %s, sysPrefix: %s, cgroupPath: %s", err, sysPrefix, cgroupPath)
}
if got != expectedCPU {
t.Fatalf("unexpected result from getCPUStatV2(%s, %s), got %f, want %f", sysPrefix, cgroupPath, got, expectedCPU)
}
}
f("testdata/cgroup", "testdata/self/cgroupv2", 2)
}

View file

@ -13,12 +13,22 @@ func GetMemoryLimit() int64 {
// 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 := getMemStat("memory.limit_in_bytes") n, err := getMemStat("memory.limit_in_bytes")
if err == nil {
return n
}
// https: //www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files
n, err = getMemStatV2()
if err != nil { if err != nil {
return 0 return 0
} }
return n return n
} }
func getMemStatV2() (int64, error) {
return getStatGeneric("memory.max", "/sys/fs/cgroup", "/proc/self/cgroup", "")
}
func getMemStat(statName string) (int64, error) { func getMemStat(statName string) (int64, error) {
return getStatGeneric(statName, "/sys/fs/cgroup/memory", "/proc/self/cgroup", "memory") return getStatGeneric(statName, "/sys/fs/cgroup/memory", "/proc/self/cgroup", "memory")
} }

1
lib/cgroup/testdata/cgroup/cpu.max vendored Normal file
View file

@ -0,0 +1 @@
200000 100000

1
lib/cgroup/testdata/cgroup/memory.max vendored Normal file
View file

@ -0,0 +1 @@
523372036854771712

1
lib/cgroup/testdata/self/cgroupv2 vendored Normal file
View file

@ -0,0 +1 @@
0::/

View file

@ -13,6 +13,7 @@ func getStatGeneric(statName, sysfsPrefix, cgroupPath, cgroupGrepLine string) (i
if err != nil { if err != nil {
return 0, err return 0, err
} }
data = strings.TrimRight(data, "\r\n")
n, err := strconv.ParseInt(data, 10, 64) n, err := strconv.ParseInt(data, 10, 64)
if err != nil { if err != nil {
return 0, err return 0, err

View file

@ -21,6 +21,7 @@ func TestGetStatGenericSuccess(t *testing.T) {
f("cpu.cfs_period_us", "testdata/cgroup", "testdata/self/cgroup", "cpu,", 500000) f("cpu.cfs_period_us", "testdata/cgroup", "testdata/self/cgroup", "cpu,", 500000)
f("memory.limit_in_bytes", "testdata/", "testdata/self/cgroup", "memory", 9223372036854771712) f("memory.limit_in_bytes", "testdata/", "testdata/self/cgroup", "memory", 9223372036854771712)
f("memory.limit_in_bytes", "testdata/cgroup", "testdata/self/cgroup", "memory", 523372036854771712) f("memory.limit_in_bytes", "testdata/cgroup", "testdata/self/cgroup", "memory", 523372036854771712)
f("memory.max", "testdata/cgroup", "testdata/self/cgroupv2", "", 523372036854771712)
} }
func TestGetStatGenericFailure(t *testing.T) { func TestGetStatGenericFailure(t *testing.T) {
@ -37,4 +38,5 @@ func TestGetStatGenericFailure(t *testing.T) {
f("cpu.cfs_quota_us", "testdata/", "testdata/missing_folder", "cpu,") f("cpu.cfs_quota_us", "testdata/", "testdata/missing_folder", "cpu,")
f("cpu.cfs_period_us", "testdata/", "testdata/missing_folder", "cpu,") f("cpu.cfs_period_us", "testdata/", "testdata/missing_folder", "cpu,")
f("memory.limit_in_bytes", "testdata/", "testdata/none_existing_folder", "memory") f("memory.limit_in_bytes", "testdata/", "testdata/none_existing_folder", "memory")
f("memory.max", "testdata/", "testdata/none_existing_folder", "")
} }