Merge pull request #12403 from giuseppe/improve-cgroup-detection

libpod: improve heuristic to detect cgroup
This commit is contained in:
OpenShift Merge Robot 2021-11-25 11:59:09 +01:00 committed by GitHub
commit 12f73d5f88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 59 additions and 9 deletions

View File

@ -6,9 +6,11 @@ import (
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
"strings"
"time" "time"
types040 "github.com/containernetworking/cni/pkg/types/040" types040 "github.com/containernetworking/cni/pkg/types/040"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/secrets" "github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/manifest" "github.com/containers/image/v5/manifest"
"github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/define"
@ -963,6 +965,29 @@ func (c *Container) cGroupPath() (string, error) {
return "", errors.Errorf("could not find any cgroup in %q", procPath) return "", errors.Errorf("could not find any cgroup in %q", procPath)
} }
cgroupManager := c.CgroupManager()
switch {
case c.config.CgroupsMode == cgroupSplit:
name := fmt.Sprintf("/libpod-payload-%s/", c.ID())
if index := strings.LastIndex(cgroupPath, name); index >= 0 {
return cgroupPath[:index+len(name)-1], nil
}
case cgroupManager == config.CgroupfsCgroupsManager:
name := fmt.Sprintf("/libpod-%s/", c.ID())
if index := strings.LastIndex(cgroupPath, name); index >= 0 {
return cgroupPath[:index+len(name)-1], nil
}
case cgroupManager == config.SystemdCgroupsManager:
// When running under systemd, try to detect the scope that was requested
// to be created. It improves the heuristic since we report the first
// cgroup that was created instead of the cgroup where PID 1 might have
// moved to.
name := fmt.Sprintf("/libpod-%s.scope/", c.ID())
if index := strings.LastIndex(cgroupPath, name); index >= 0 {
return cgroupPath[:index+len(name)-1], nil
}
}
return cgroupPath, nil return cgroupPath, nil
} }

View File

@ -97,6 +97,16 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
return nil, err return nil, err
} }
cgroupPath, err := c.cGroupPath()
if err != nil {
// Handle the case where the container is not running or has no cgroup.
if errors.Is(err, define.ErrNoCgroups) || errors.Is(err, define.ErrCtrStopped) {
cgroupPath = ""
} else {
return nil, err
}
}
data := &define.InspectContainerData{ data := &define.InspectContainerData{
ID: config.ID, ID: config.ID,
Created: config.CreatedTime, Created: config.CreatedTime,
@ -116,6 +126,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
StartedAt: runtimeInfo.StartedTime, StartedAt: runtimeInfo.StartedTime,
FinishedAt: runtimeInfo.FinishedTime, FinishedAt: runtimeInfo.FinishedTime,
Checkpointed: runtimeInfo.Checkpointed, Checkpointed: runtimeInfo.Checkpointed,
CgroupPath: cgroupPath,
}, },
Image: config.RootfsImageID, Image: config.RootfsImageID,
ImageName: config.RootfsImageName, ImageName: config.RootfsImageName,

View File

@ -2618,7 +2618,7 @@ func (c *Container) getOCICgroupPath() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return filepath.Join(selfCgroup, "container"), nil return filepath.Join(selfCgroup, fmt.Sprintf("libpod-payload-%s", c.ID())), nil
case cgroupManager == config.SystemdCgroupsManager: case cgroupManager == config.SystemdCgroupsManager:
// When the OCI runtime is set to use Systemd as a cgroup manager, it // When the OCI runtime is set to use Systemd as a cgroup manager, it
// expects cgroups to be passed as follows: // expects cgroups to be passed as follows:

View File

@ -204,6 +204,7 @@ type InspectContainerState struct {
FinishedAt time.Time `json:"FinishedAt"` FinishedAt time.Time `json:"FinishedAt"`
Health HealthCheckResults `json:"Health,omitempty"` Health HealthCheckResults `json:"Health,omitempty"`
Checkpointed bool `json:"Checkpointed,omitempty"` Checkpointed bool `json:"Checkpointed,omitempty"`
CgroupPath string `json:"CgroupPath,omitempty"`
} }
// Healthcheck returns the HealthCheckResults. This is used for old podman compat // Healthcheck returns the HealthCheckResults. This is used for old podman compat

View File

@ -3,6 +3,7 @@
package libpod package libpod
import ( import (
"math"
"strings" "strings"
"syscall" "syscall"
"time" "time"
@ -68,7 +69,7 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de
stats.AvgCPU = calculateAvgCPU(stats.CPU, previousStats.AvgCPU, previousStats.DataPoints) stats.AvgCPU = calculateAvgCPU(stats.CPU, previousStats.AvgCPU, previousStats.DataPoints)
stats.DataPoints = previousStats.DataPoints + 1 stats.DataPoints = previousStats.DataPoints + 1
stats.MemUsage = cgroupStats.Memory.Usage.Usage stats.MemUsage = cgroupStats.Memory.Usage.Usage
stats.MemLimit = getMemLimit(cgroupStats.Memory.Usage.Limit) stats.MemLimit = c.getMemLimit()
stats.MemPerc = (float64(stats.MemUsage) / float64(stats.MemLimit)) * 100 stats.MemPerc = (float64(stats.MemUsage) / float64(stats.MemLimit)) * 100
stats.PIDs = 0 stats.PIDs = 0
if conState == define.ContainerStateRunning || conState == define.ContainerStatePaused { if conState == define.ContainerStateRunning || conState == define.ContainerStatePaused {
@ -91,22 +92,29 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de
return stats, nil return stats, nil
} }
// getMemory limit returns the memory limit for a given cgroup // getMemory limit returns the memory limit for a container
// If the configured memory limit is larger than the total memory on the sys, the func (c *Container) getMemLimit() uint64 {
// physical system memory size is returned memLimit := uint64(math.MaxUint64)
func getMemLimit(cgroupLimit uint64) uint64 {
if c.config.Spec.Linux != nil && c.config.Spec.Linux.Resources != nil &&
c.config.Spec.Linux.Resources.Memory != nil && c.config.Spec.Linux.Resources.Memory.Limit != nil {
memLimit = uint64(*c.config.Spec.Linux.Resources.Memory.Limit)
}
si := &syscall.Sysinfo_t{} si := &syscall.Sysinfo_t{}
err := syscall.Sysinfo(si) err := syscall.Sysinfo(si)
if err != nil { if err != nil {
return cgroupLimit return memLimit
} }
//nolint:unconvert //nolint:unconvert
physicalLimit := uint64(si.Totalram) physicalLimit := uint64(si.Totalram)
if cgroupLimit > physicalLimit {
if memLimit <= 0 || memLimit > physicalLimit {
return physicalLimit return physicalLimit
} }
return cgroupLimit
return memLimit
} }
// calculateCPUPercent calculates the cpu usage using the latest measurement in stats. // calculateCPUPercent calculates the cpu usage using the latest measurement in stats.

View File

@ -109,6 +109,11 @@ WantedBy=multi-user.target
stats := podmanTest.Podman([]string{"stats", "--no-stream", ctrName}) stats := podmanTest.Podman([]string{"stats", "--no-stream", ctrName})
stats.WaitWithDefaultTimeout() stats.WaitWithDefaultTimeout()
Expect(stats).Should(Exit(0)) Expect(stats).Should(Exit(0))
cgroupPath := podmanTest.Podman([]string{"inspect", "--format='{{.State.CgroupPath}}'", ctrName})
cgroupPath.WaitWithDefaultTimeout()
Expect(cgroupPath).Should(Exit(0))
Expect(result.OutputToString()).To(Not(ContainSubstring("init.scope")))
}) })
It("podman create container with systemd entrypoint triggers systemd mode", func() { It("podman create container with systemd entrypoint triggers systemd mode", func() {