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
|
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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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
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 {
|
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
|
||||||
|
|
|
@ -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", "")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue