mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
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:
parent
a22a17dc66
commit
b50024812e
8 changed files with 74 additions and 6 deletions
|
@ -1,6 +1,7 @@
|
|||
package cgroup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
|
@ -41,20 +42,28 @@ func updateGOMAXPROCSToCPUQuota() {
|
|||
}
|
||||
|
||||
func getCPUQuota() float64 {
|
||||
quotaUS, err := getCPUStat("cpu.cfs_quota_us")
|
||||
cpuQuota, err := getCPUStatGeneric()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
if quotaUS <= 0 {
|
||||
|
||||
if cpuQuota <= 0 {
|
||||
// The quota isn't set. This may be the case in multilevel containers.
|
||||
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/685#issuecomment-674423728
|
||||
return getOnlineCPUCount()
|
||||
}
|
||||
periodUS, err := getCPUStat("cpu.cfs_period_us")
|
||||
if err != nil {
|
||||
return 0
|
||||
return cpuQuota
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -74,6 +83,35 @@ func getOnlineCPUCount() float64 {
|
|||
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 {
|
||||
data = strings.TrimSpace(data)
|
||||
n := 0
|
||||
|
|
|
@ -22,3 +22,17 @@ func TestCountCPUs(t *testing.T) {
|
|||
f("0-3", 4)
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -13,12 +13,22 @@ func GetMemoryLimit() int64 {
|
|||
// This should properly determine the limit inside lxc container.
|
||||
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/84
|
||||
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 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func getMemStatV2() (int64, error) {
|
||||
return getStatGeneric("memory.max", "/sys/fs/cgroup", "/proc/self/cgroup", "")
|
||||
}
|
||||
|
||||
func getMemStat(statName string) (int64, error) {
|
||||
return getStatGeneric(statName, "/sys/fs/cgroup/memory", "/proc/self/cgroup", "memory")
|
||||
}
|
||||
|
|
1
lib/cgroup/testdata/cgroup/cpu.max
vendored
Normal file
1
lib/cgroup/testdata/cgroup/cpu.max
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
200000 100000
|
1
lib/cgroup/testdata/cgroup/memory.max
vendored
Normal file
1
lib/cgroup/testdata/cgroup/memory.max
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
523372036854771712
|
1
lib/cgroup/testdata/self/cgroupv2
vendored
Normal file
1
lib/cgroup/testdata/self/cgroupv2
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
0::/
|
|
@ -13,6 +13,7 @@ func getStatGeneric(statName, sysfsPrefix, cgroupPath, cgroupGrepLine string) (i
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
data = strings.TrimRight(data, "\r\n")
|
||||
n, err := strconv.ParseInt(data, 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
|
|
@ -21,6 +21,7 @@ func TestGetStatGenericSuccess(t *testing.T) {
|
|||
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/cgroup", "testdata/self/cgroup", "memory", 523372036854771712)
|
||||
f("memory.max", "testdata/cgroup", "testdata/self/cgroupv2", "", 523372036854771712)
|
||||
}
|
||||
|
||||
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_period_us", "testdata/", "testdata/missing_folder", "cpu,")
|
||||
f("memory.limit_in_bytes", "testdata/", "testdata/none_existing_folder", "memory")
|
||||
f("memory.max", "testdata/", "testdata/none_existing_folder", "")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue