Merge pull request from giuseppe/api-fix-cpu-stats

api: fix the CPU stats reported
This commit is contained in:
OpenShift Merge Robot
2020-02-13 15:42:53 +01:00
committed by GitHub
5 changed files with 75 additions and 24 deletions

@ -66,7 +66,9 @@ func (c *Container) GetContainerStats(previousStats *ContainerStats) (*Container
} }
stats.BlockInput, stats.BlockOutput = calculateBlockIO(cgroupStats) stats.BlockInput, stats.BlockOutput = calculateBlockIO(cgroupStats)
stats.CPUNano = cgroupStats.CPU.Usage.Total stats.CPUNano = cgroupStats.CPU.Usage.Total
stats.CPUSystemNano = cgroupStats.CPU.Usage.Kernel
stats.SystemNano = now stats.SystemNano = now
stats.PerCPU = cgroupStats.CPU.Usage.PerCPU
// Handle case where the container is not in a network namespace // Handle case where the container is not in a network namespace
if netStats != nil { if netStats != nil {
stats.NetInput = netStats.TxBytes stats.NetInput = netStats.TxBytes

@ -2,17 +2,19 @@ package libpod
// ContainerStats contains the statistics information for a running container // ContainerStats contains the statistics information for a running container
type ContainerStats struct { type ContainerStats struct {
ContainerID string ContainerID string
Name string Name string
CPU float64 PerCPU []uint64
CPUNano uint64 CPU float64
SystemNano uint64 CPUNano uint64
MemUsage uint64 CPUSystemNano uint64
MemLimit uint64 SystemNano uint64
MemPerc float64 MemUsage uint64
NetInput uint64 MemLimit uint64
NetOutput uint64 MemPerc float64
BlockInput uint64 NetInput uint64
BlockOutput uint64 NetOutput uint64
PIDs uint64 BlockInput uint64
BlockOutput uint64
PIDs uint64
} }

@ -61,14 +61,15 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) {
var preCPUStats docker.CPUStats var preCPUStats docker.CPUStats
if query.Stream { if query.Stream {
preRead = time.Now() preRead = time.Now()
systemUsage, _ := cgroups.GetSystemCPUUsage()
preCPUStats = docker.CPUStats{ preCPUStats = docker.CPUStats{
CPUUsage: docker.CPUUsage{ CPUUsage: docker.CPUUsage{
TotalUsage: stats.CPUNano, TotalUsage: stats.CPUNano,
PercpuUsage: []uint64{uint64(stats.CPU)}, PercpuUsage: stats.PerCPU,
UsageInKernelmode: 0, UsageInKernelmode: stats.CPUSystemNano,
UsageInUsermode: 0, UsageInUsermode: stats.CPUNano - stats.CPUSystemNano,
}, },
SystemUsage: 0, SystemUsage: systemUsage,
OnlineCPUs: 0, OnlineCPUs: 0,
ThrottlingData: docker.ThrottlingData{}, ThrottlingData: docker.ThrottlingData{},
} }
@ -122,6 +123,8 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) {
InstanceID: "", InstanceID: "",
} }
systemUsage, _ := cgroups.GetSystemCPUUsage()
s := handlers.Stats{StatsJSON: docker.StatsJSON{ s := handlers.Stats{StatsJSON: docker.StatsJSON{
Stats: docker.Stats{ Stats: docker.Stats{
Read: time.Now(), Read: time.Now(),
@ -143,11 +146,11 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) {
CPUStats: docker.CPUStats{ CPUStats: docker.CPUStats{
CPUUsage: docker.CPUUsage{ CPUUsage: docker.CPUUsage{
TotalUsage: cgroupStat.CPU.Usage.Total, TotalUsage: cgroupStat.CPU.Usage.Total,
PercpuUsage: []uint64{uint64(stats.CPU)}, PercpuUsage: cgroupStat.CPU.Usage.PerCPU,
UsageInKernelmode: cgroupStat.CPU.Usage.Kernel, UsageInKernelmode: cgroupStat.CPU.Usage.Kernel,
UsageInUsermode: cgroupStat.CPU.Usage.Total - cgroupStat.CPU.Usage.Kernel, UsageInUsermode: cgroupStat.CPU.Usage.Total - cgroupStat.CPU.Usage.Kernel,
}, },
SystemUsage: 0, SystemUsage: systemUsage,
OnlineCPUs: uint32(len(cgroupStat.CPU.Usage.PerCPU)), OnlineCPUs: uint32(len(cgroupStat.CPU.Usage.PerCPU)),
ThrottlingData: docker.ThrottlingData{ ThrottlingData: docker.ThrottlingData{
Periods: 0, Periods: 0,

@ -536,15 +536,14 @@ func (c *CgroupControl) Stat() (*Metrics, error) {
return &m, nil return &m, nil
} }
func readCgroup2MapFile(ctr *CgroupControl, name string) (map[string][]string, error) { func readCgroup2MapPath(path string) (map[string][]string, error) {
ret := map[string][]string{} ret := map[string][]string{}
p := filepath.Join(cgroupRoot, ctr.path, name) f, err := os.Open(path)
f, err := os.Open(p)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return ret, nil return ret, nil
} }
return nil, errors.Wrapf(err, "open file %s", p) return nil, errors.Wrapf(err, "open file %s", path)
} }
defer f.Close() defer f.Close()
scanner := bufio.NewScanner(f) scanner := bufio.NewScanner(f)
@ -557,7 +556,13 @@ func readCgroup2MapFile(ctr *CgroupControl, name string) (map[string][]string, e
ret[parts[0]] = parts[1:] ret[parts[0]] = parts[1:]
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
return nil, errors.Wrapf(err, "parsing file %s", p) return nil, errors.Wrapf(err, "parsing file %s", path)
} }
return ret, nil return ret, nil
} }
func readCgroup2MapFile(ctr *CgroupControl, name string) (map[string][]string, error) {
p := filepath.Join(cgroupRoot, ctr.path, name)
return readCgroup2MapPath(p)
}

@ -121,3 +121,42 @@ func (c *cpuHandler) Stat(ctr *CgroupControl, m *Metrics) error {
m.CPU = CPUMetrics{Usage: usage} m.CPU = CPUMetrics{Usage: usage}
return nil return nil
} }
// GetSystemCPUUsage returns the system usage for all the cgroups
func GetSystemCPUUsage() (uint64, error) {
cgroupv2, err := IsCgroup2UnifiedMode()
if err != nil {
return 0, err
}
if !cgroupv2 {
p := filepath.Join(cgroupRoot, CPUAcct, "cpuacct.usage")
return readFileAsUint64(p)
}
files, err := ioutil.ReadDir(cgroupRoot)
if err != nil {
return 0, errors.Wrapf(err, "read directory %q", cgroupRoot)
}
var total uint64
for _, file := range files {
if !file.IsDir() {
continue
}
p := filepath.Join(cgroupRoot, file.Name(), "cpu.stat")
values, err := readCgroup2MapPath(p)
if err != nil {
return 0, err
}
if val, found := values["usage_usec"]; found {
v, err := strconv.ParseUint(cleanString(val[0]), 10, 0)
if err != nil {
return 0, err
}
total += v * 1000
}
}
return total, nil
}