diff --git a/lib/container_server.go b/lib/container_server.go index 3e8e5298..5a140e79 100644 --- a/lib/container_server.go +++ b/lib/container_server.go @@ -17,7 +17,6 @@ import ( "github.com/kubernetes-incubator/cri-o/pkg/annotations" "github.com/kubernetes-incubator/cri-o/pkg/registrar" "github.com/kubernetes-incubator/cri-o/pkg/storage" - "github.com/opencontainers/runc/libcontainer" rspec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" @@ -771,20 +770,3 @@ func (c *ContainerServer) RemoveSandbox(id string) { func (c *ContainerServer) ListSandboxes() []*sandbox.Sandbox { return c.state.sandboxes.List() } - -// LibcontainerStats gets the stats for the container with the given id from runc/libcontainer -func (c *ContainerServer) LibcontainerStats(ctr *oci.Container) (*libcontainer.Stats, error) { - // TODO: make this not hardcoded - // was: c.runtime.Path(ociContainer) but that returns /usr/bin/runc - how do we get /run/runc? - // runroot is /var/run/runc - // Hardcoding probably breaks ClearContainers compatibility - factory, err := loadFactory("/run/runc") - if err != nil { - return nil, err - } - container, err := factory.Load(ctr.ID()) - if err != nil { - return nil, err - } - return container.Stats() -} diff --git a/lib/container_server_linux.go b/lib/container_server_linux.go index ffc03d5f..0ffcfc5b 100644 --- a/lib/container_server_linux.go +++ b/lib/container_server_linux.go @@ -3,11 +3,33 @@ package lib import ( + "path/filepath" + "time" + "github.com/kubernetes-incubator/cri-o/lib/sandbox" + "github.com/kubernetes-incubator/cri-o/oci" + "github.com/opencontainers/runc/libcontainer" selinux "github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux/label" ) +// libcontainerStats gets the stats for the container with the given id from runc/libcontainer +func (c *ContainerServer) libcontainerStats(ctr *oci.Container) (*libcontainer.Stats, error) { + // TODO: make this not hardcoded + // was: c.runtime.Path(ociContainer) but that returns /usr/bin/runc - how do we get /run/runc? + // runroot is /var/run/runc + // Hardcoding probably breaks ClearContainers compatibility + factory, err := loadFactory("/run/runc") + if err != nil { + return nil, err + } + container, err := factory.Load(ctr.ID()) + if err != nil { + return nil, err + } + return container.Stats() +} + func (c *ContainerServer) addSandboxPlatform(sb *sandbox.Sandbox) { c.state.processLevels[selinux.NewContext(sb.ProcessLabel())["level"]]++ } @@ -24,3 +46,35 @@ func (c *ContainerServer) removeSandboxPlatform(sb *sandbox.Sandbox) { } } } + +func loadFactory(root string) (libcontainer.Factory, error) { + abs, err := filepath.Abs(root) + if err != nil { + return nil, err + } + cgroupManager := libcontainer.Cgroupfs + return libcontainer.New(abs, cgroupManager, libcontainer.CriuPath("")) +} + +func (c *ContainerServer) getContainerStats(ctr *oci.Container, previousStats *ContainerStats) (*ContainerStats, error) { + previousCPU := previousStats.CPUNano + previousSystem := previousStats.SystemNano + libcontainerStats, err := c.libcontainerStats(ctr) + if err != nil { + return nil, err + } + cgroupStats := libcontainerStats.CgroupStats + stats := new(ContainerStats) + stats.Container = ctr.ID() + stats.CPUNano = cgroupStats.CpuStats.CpuUsage.TotalUsage + stats.SystemNano = time.Now().UnixNano() + stats.CPU = calculateCPUPercent(libcontainerStats, previousCPU, previousSystem) + stats.MemUsage = cgroupStats.MemoryStats.Usage.Usage + stats.MemLimit = getMemLimit(cgroupStats.MemoryStats.Usage.Limit) + stats.MemPerc = float64(stats.MemUsage) / float64(stats.MemLimit) + stats.PIDs = cgroupStats.PidsStats.Current + stats.BlockInput, stats.BlockOutput = calculateBlockIO(libcontainerStats) + stats.NetInput, stats.NetOutput = getContainerNetIO(libcontainerStats) + + return stats, nil +} diff --git a/lib/container_server_unsupported.go b/lib/container_server_unsupported.go index 370de43a..edb40fa2 100644 --- a/lib/container_server_unsupported.go +++ b/lib/container_server_unsupported.go @@ -2,7 +2,11 @@ package lib -import "github.com/kubernetes-incubator/cri-o/lib/sandbox" +import ( + "github.com/kubernetes-incubator/cri-o/lib/sandbox" + "github.com/kubernetes-incubator/cri-o/oci" + "github.com/pkg/errors" +) func (c *ContainerServer) addSandboxPlatform(sb *sandbox.Sandbox) { // nothin' doin' @@ -11,3 +15,8 @@ func (c *ContainerServer) addSandboxPlatform(sb *sandbox.Sandbox) { func (c *ContainerServer) removeSandboxPlatform(sb *sandbox.Sandbox) { // nothin' doin' } + +func (c *ContainerServer) getContainerStats(ctr *oci.Container, previousStats *ContainerStats) (*ContainerStats, error) { + // nothin' doin' + return nil, errors.New("container stats not supported") +} diff --git a/lib/stats.go b/lib/stats.go index 58fac59d..a816f43c 100644 --- a/lib/stats.go +++ b/lib/stats.go @@ -1,14 +1,7 @@ package lib import ( - "path/filepath" - "syscall" - "time" - - "strings" - "github.com/kubernetes-incubator/cri-o/oci" - "github.com/opencontainers/runc/libcontainer" ) // ContainerStats contains the statistics information for a running container @@ -29,85 +22,5 @@ type ContainerStats struct { // GetContainerStats gets the running stats for a given container func (c *ContainerServer) GetContainerStats(ctr *oci.Container, previousStats *ContainerStats) (*ContainerStats, error) { - previousCPU := previousStats.CPUNano - previousSystem := previousStats.SystemNano - libcontainerStats, err := c.LibcontainerStats(ctr) - if err != nil { - return nil, err - } - cgroupStats := libcontainerStats.CgroupStats - stats := new(ContainerStats) - stats.Container = ctr.ID() - stats.CPUNano = cgroupStats.CpuStats.CpuUsage.TotalUsage - stats.SystemNano = time.Now().UnixNano() - stats.CPU = calculateCPUPercent(libcontainerStats, previousCPU, previousSystem) - stats.MemUsage = cgroupStats.MemoryStats.Usage.Usage - stats.MemLimit = getMemLimit(cgroupStats.MemoryStats.Usage.Limit) - stats.MemPerc = float64(stats.MemUsage) / float64(stats.MemLimit) - stats.PIDs = cgroupStats.PidsStats.Current - stats.BlockInput, stats.BlockOutput = calculateBlockIO(libcontainerStats) - stats.NetInput, stats.NetOutput = getContainerNetIO(libcontainerStats) - - return stats, nil -} - -func loadFactory(root string) (libcontainer.Factory, error) { - abs, err := filepath.Abs(root) - if err != nil { - return nil, err - } - cgroupManager := libcontainer.Cgroupfs - return libcontainer.New(abs, cgroupManager, libcontainer.CriuPath("")) -} - -// getMemory limit returns the memory limit for a given cgroup -// If the configured memory limit is larger than the total memory on the sys, the -// physical system memory size is returned -func getMemLimit(cgroupLimit uint64) uint64 { - si := &syscall.Sysinfo_t{} - err := syscall.Sysinfo(si) - if err != nil { - return cgroupLimit - } - - physicalLimit := uint64(si.Totalram) - if cgroupLimit > physicalLimit { - return physicalLimit - } - return cgroupLimit -} - -// Returns the total number of bytes transmitted and received for the given container stats -func getContainerNetIO(stats *libcontainer.Stats) (received uint64, transmitted uint64) { - for _, iface := range stats.Interfaces { - received += iface.RxBytes - transmitted += iface.TxBytes - } - return -} - -func calculateCPUPercent(stats *libcontainer.Stats, previousCPU uint64, previousSystem int64) float64 { - var ( - cpuPercent = 0.0 - cpuDelta = float64(stats.CgroupStats.CpuStats.CpuUsage.TotalUsage - previousCPU) - systemDelta = float64(uint64(time.Now().UnixNano()) - uint64(previousSystem)) - ) - if systemDelta > 0.0 && cpuDelta > 0.0 { - // gets a ratio of container cpu usage total, multiplies it by the number of cores (4 cores running - // at 100% utilization should be 400% utilization), and multiplies that by 100 to get a percentage - cpuPercent = (cpuDelta / systemDelta) * float64(len(stats.CgroupStats.CpuStats.CpuUsage.PercpuUsage)) * 100 - } - return cpuPercent -} - -func calculateBlockIO(stats *libcontainer.Stats) (read uint64, write uint64) { - for _, blkIOEntry := range stats.CgroupStats.BlkioStats.IoServiceBytesRecursive { - switch strings.ToLower(blkIOEntry.Op) { - case "read": - read += blkIOEntry.Value - case "write": - write += blkIOEntry.Value - } - } - return + return c.getContainerStats(ctr, previousStats) } diff --git a/lib/stats_linux.go b/lib/stats_linux.go new file mode 100644 index 00000000..fc52e35f --- /dev/null +++ b/lib/stats_linux.go @@ -0,0 +1,61 @@ +package lib + +import ( + "strings" + "syscall" + "time" + + "github.com/opencontainers/runc/libcontainer" +) + +// Returns the total number of bytes transmitted and received for the given container stats +func getContainerNetIO(stats *libcontainer.Stats) (received uint64, transmitted uint64) { + for _, iface := range stats.Interfaces { + received += iface.RxBytes + transmitted += iface.TxBytes + } + return +} + +func calculateCPUPercent(stats *libcontainer.Stats, previousCPU uint64, previousSystem int64) float64 { + var ( + cpuPercent = 0.0 + cpuDelta = float64(stats.CgroupStats.CpuStats.CpuUsage.TotalUsage - previousCPU) + systemDelta = float64(uint64(time.Now().UnixNano()) - uint64(previousSystem)) + ) + if systemDelta > 0.0 && cpuDelta > 0.0 { + // gets a ratio of container cpu usage total, multiplies it by the number of cores (4 cores running + // at 100% utilization should be 400% utilization), and multiplies that by 100 to get a percentage + cpuPercent = (cpuDelta / systemDelta) * float64(len(stats.CgroupStats.CpuStats.CpuUsage.PercpuUsage)) * 100 + } + return cpuPercent +} + +func calculateBlockIO(stats *libcontainer.Stats) (read uint64, write uint64) { + for _, blkIOEntry := range stats.CgroupStats.BlkioStats.IoServiceBytesRecursive { + switch strings.ToLower(blkIOEntry.Op) { + case "read": + read += blkIOEntry.Value + case "write": + write += blkIOEntry.Value + } + } + return +} + +// getMemory limit returns the memory limit for a given cgroup +// If the configured memory limit is larger than the total memory on the sys, the +// physical system memory size is returned +func getMemLimit(cgroupLimit uint64) uint64 { + si := &syscall.Sysinfo_t{} + err := syscall.Sysinfo(si) + if err != nil { + return cgroupLimit + } + + physicalLimit := uint64(si.Totalram) + if cgroupLimit > physicalLimit { + return physicalLimit + } + return cgroupLimit +}