cgroup: initial support for cgroups v2

add some initial support for cgroups v2.  Not all the stats
supported on cgroups v1 are supported, e.g. it is not possible to read
percpu usage.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
Giuseppe Scrivano 2019-07-01 15:15:18 +02:00
parent 4f0396f1cc
commit 60f064ee41
No known key found for this signature in database
GPG Key ID: E4730F97F60286ED
7 changed files with 71 additions and 17 deletions

View File

@ -19,6 +19,7 @@ import (
"io/ioutil"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"time"
@ -27,6 +28,7 @@ import (
info "github.com/google/cadvisor/info/v1"
"github.com/google/cadvisor/utils"
"github.com/karrick/godirwalk"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/pkg/errors"
"k8s.io/klog"
@ -47,6 +49,25 @@ func DebugInfo(watches map[string][]string) map[string][]string {
return out
}
// findFileInAncestorDir returns the path to the parent directory that contains the specified file.
// "" is returned if the lookup reaches the limit.
func findFileInAncestorDir(current, file, limit string) (string, error) {
for {
fpath := path.Join(current, file)
_, err := os.Stat(fpath)
if err == nil {
return current, nil
}
if !os.IsNotExist(err) {
return "", err
}
if current == limit {
return "", nil
}
current = filepath.Dir(current)
}
}
func GetSpec(cgroupPaths map[string]string, machineInfoFactory info.MachineInfoFactory, hasNetwork, hasFilesystem bool) (info.ContainerSpec, error) {
var spec info.ContainerSpec
@ -100,7 +121,12 @@ func GetSpec(cgroupPaths map[string]string, machineInfoFactory info.MachineInfoF
if ok {
if utils.FileExists(cpusetRoot) {
spec.HasCpu = true
mask := readString(cpusetRoot, "cpuset.cpus")
mask := ""
if cgroups.IsCgroup2UnifiedMode() {
mask = readString(cpusetRoot, "cpuset.cpus.effective")
} else {
mask = readString(cpusetRoot, "cpuset.cpus")
}
spec.Cpu.Mask = utils.FixCpuMask(mask, mi.NumCores)
}
}
@ -108,11 +134,24 @@ func GetSpec(cgroupPaths map[string]string, machineInfoFactory info.MachineInfoF
// Memory
memoryRoot, ok := cgroupPaths["memory"]
if ok {
if utils.FileExists(memoryRoot) {
spec.HasMemory = true
spec.Memory.Limit = readUInt64(memoryRoot, "memory.limit_in_bytes")
spec.Memory.SwapLimit = readUInt64(memoryRoot, "memory.memsw.limit_in_bytes")
spec.Memory.Reservation = readUInt64(memoryRoot, "memory.soft_limit_in_bytes")
if !cgroups.IsCgroup2UnifiedMode() {
if utils.FileExists(memoryRoot) {
spec.HasMemory = true
spec.Memory.Limit = readUInt64(memoryRoot, "memory.limit_in_bytes")
spec.Memory.SwapLimit = readUInt64(memoryRoot, "memory.memsw.limit_in_bytes")
spec.Memory.Reservation = readUInt64(memoryRoot, "memory.soft_limit_in_bytes")
}
} else {
memoryRoot, err := findFileInAncestorDir(memoryRoot, "memory.max", "/sys/fs/cgroup")
if err != nil {
return spec, err
}
if memoryRoot != "" {
spec.HasMemory = true
spec.Memory.Reservation = readUInt64(memoryRoot, "memory.high")
spec.Memory.Limit = readUInt64(memoryRoot, "memory.max")
spec.Memory.SwapLimit = readUInt64(memoryRoot, "memory.swap.max")
}
}
}
@ -128,7 +167,11 @@ func GetSpec(cgroupPaths map[string]string, machineInfoFactory info.MachineInfoF
spec.HasNetwork = hasNetwork
spec.HasFilesystem = hasFilesystem
if blkioRoot, ok := cgroupPaths["blkio"]; ok && utils.FileExists(blkioRoot) {
ioControllerName := "blkio"
if cgroups.IsCgroup2UnifiedMode() {
ioControllerName = "io"
}
if blkioRoot, ok := cgroupPaths[ioControllerName]; ok && utils.FileExists(blkioRoot) {
spec.HasDiskIo = true
}

View File

@ -47,6 +47,7 @@ func GetCgroupSubsystems(includedMetrics container.MetricSet) (CgroupSubsystems,
//currently we only support disable blkio subsystem
if !includedMetrics.Has(container.DiskIOMetrics) {
disableCgroups["blkio"] = struct{}{}
disableCgroups["io"] = struct{}{}
}
return getCgroupSubsystemsHelper(allCgroups, disableCgroups)
}
@ -109,6 +110,7 @@ var supportedSubsystems map[string]struct{} = map[string]struct{}{
"pids": {},
"cpuset": {},
"blkio": {},
"io": {},
"devices": {},
}

View File

@ -185,6 +185,9 @@ func (c *containerData) getCgroupPath(cgroups string) (string, error) {
if cgroups == "-" {
return "/", nil
}
if strings.HasPrefix(cgroups, "0::") {
return cgroups[3:], nil
}
matches := cgroupPathRegExp.FindSubmatch([]byte(cgroups))
if len(matches) != 2 {
klog.V(3).Infof("failed to get memory cgroup path from %q", cgroups)

View File

@ -918,13 +918,15 @@ func (m *manager) createContainerLocked(containerName string, watchSource watche
if err != nil {
return err
}
devicesCgroupPath, err := handler.GetCgroupPath("devices")
if err != nil {
klog.Warningf("Error getting devices cgroup path: %v", err)
} else {
cont.nvidiaCollector, err = m.nvidiaManager.GetCollector(devicesCgroupPath)
if !cgroups.IsCgroup2UnifiedMode() {
devicesCgroupPath, err := handler.GetCgroupPath("devices")
if err != nil {
klog.V(4).Infof("GPU metrics may be unavailable/incomplete for container %q: %v", cont.info.Name, err)
klog.Warningf("Error getting devices cgroup path: %v", err)
} else {
cont.nvidiaCollector, err = m.nvidiaManager.GetCollector(devicesCgroupPath)
if err != nil {
klog.V(4).Infof("GPU metrics may be unavailable/incomplete for container %q: %v", cont.info.Name, err)
}
}
}

View File

@ -264,6 +264,10 @@ function drawCpuPerCoreUsage(elementId, machineInfo, stats) {
var prev = stats.stats[i - 1];
var intervalNs = getInterval(cur.timestamp, prev.timestamp);
if (cur.cpu.usage.per_cpu_usage == undefined) {
return;
}
var elements = [];
elements.push(cur.timestamp);
for (var j = 0; j < machineInfo.num_cores; j++) {

File diff suppressed because one or more lines are too long

View File

@ -139,7 +139,7 @@ func validateCpuCfsBandwidth(available_cgroups map[string]int) string {
if !ok {
return "\tCpu cfs bandwidth status unknown: cpu cgroup not enabled.\n"
}
mnt, err := cgroups.FindCgroupMountpoint("cpu")
mnt, err := cgroups.FindCgroupMountpoint("/", "cpu")
if err != nil {
return "\tCpu cfs bandwidth status unknown: cpu cgroup not mounted.\n"
}
@ -156,7 +156,7 @@ func validateMemoryAccounting(available_cgroups map[string]int) string {
if !ok {
return "\tHierarchical memory accounting status unknown: memory cgroup not enabled.\n"
}
mnt, err := cgroups.FindCgroupMountpoint("memory")
mnt, err := cgroups.FindCgroupMountpoint("/", "memory")
if err != nil {
return "\tHierarchical memory accounting status unknown: memory cgroup not mounted.\n"
}
@ -216,7 +216,7 @@ func validateDockerInfo() (string, string) {
func validateCgroupMounts() (string, string) {
const recommendedMount = "/sys/fs/cgroup"
desc := fmt.Sprintf("\tAny cgroup mount point that is detectible and accessible is supported. %s is recommended as a standard location.\n", recommendedMount)
mnt, err := cgroups.FindCgroupMountpoint("cpu")
mnt, err := cgroups.FindCgroupMountpoint("/", "cpu")
if err != nil {
out := "Could not locate cgroup mount point.\n"
out += desc