Merge pull request #1249 from theatrus/add-container-stats

Add methods for listing and fetching container stats
This commit is contained in:
Mrunal Patel 2018-02-15 13:29:15 -08:00 committed by GitHub
commit 8f5e37a83c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 111 additions and 42 deletions

View file

@ -15,8 +15,8 @@ import (
type ContainerStats struct {
Container string
CPU float64
cpuNano uint64
systemNano uint64
CPUNano uint64
SystemNano int64
MemUsage uint64
MemLimit uint64
MemPerc float64
@ -29,8 +29,8 @@ 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
previousCPU := previousStats.CPUNano
previousSystem := previousStats.SystemNano
libcontainerStats, err := c.LibcontainerStats(ctr)
if err != nil {
return nil, err
@ -38,6 +38,8 @@ func (c *ContainerServer) GetContainerStats(ctr *oci.Container, previousStats *C
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)
@ -84,11 +86,11 @@ func getContainerNetIO(stats *libcontainer.Stats) (received uint64, transmitted
return
}
func calculateCPUPercent(stats *libcontainer.Stats, previousCPU, previousSystem uint64) float64 {
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()) - previousSystem)
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

View file

@ -28,6 +28,42 @@ func filterContainer(c *pb.Container, filter *pb.ContainerFilter) bool {
return true
}
// filterContainerList applies a protobuf-defined filter to retrieve only intended containers. Not matching
// the filter is not considered an error but will return an empty response.
func (s *Server) filterContainerList(filter *pb.ContainerFilter, origCtrList []*oci.Container) []*oci.Container {
// Filter using container id and pod id first.
if filter.Id != "" {
id, err := s.CtrIDIndex().Get(filter.Id)
if err != nil {
// If we don't find a container ID with a filter, it should not
// be considered an error. Log a warning and return an empty struct
logrus.Warn("unable to find container ID %s", filter.Id)
return []*oci.Container{}
}
c := s.ContainerServer.GetContainer(id)
if c != nil {
switch {
case filter.PodSandboxId == "":
return []*oci.Container{c}
case c.Sandbox() == filter.PodSandboxId:
return []*oci.Container{c}
default:
return []*oci.Container{}
}
}
} else {
if filter.PodSandboxId != "" {
pod := s.ContainerServer.GetSandbox(filter.PodSandboxId)
if pod == nil {
return []*oci.Container{}
}
return pod.Containers().List()
}
}
logrus.Debug("no filters were applied, returning full container list")
return origCtrList
}
// ListContainers lists all containers by filters.
func (s *Server) ListContainers(ctx context.Context, req *pb.ListContainersRequest) (resp *pb.ListContainersResponse, err error) {
const operation = "list_containers"
@ -45,39 +81,7 @@ func (s *Server) ListContainers(ctx context.Context, req *pb.ListContainersReque
}
if filter != nil {
// Filter using container id and pod id first.
if filter.Id != "" {
id, err := s.CtrIDIndex().Get(filter.Id)
if err != nil {
// If we don't find a container ID with a filter, it should not
// be considered an error. Log a warning and return an empty struct
logrus.Warn("unable to find container ID %s", filter.Id)
return &pb.ListContainersResponse{}, nil
}
c := s.ContainerServer.GetContainer(id)
if c != nil {
if filter.PodSandboxId != "" {
if c.Sandbox() == filter.PodSandboxId {
ctrList = []*oci.Container{c}
} else {
ctrList = []*oci.Container{}
}
} else {
ctrList = []*oci.Container{c}
}
}
} else {
if filter.PodSandboxId != "" {
pod := s.ContainerServer.GetSandbox(filter.PodSandboxId)
if pod == nil {
ctrList = []*oci.Container{}
} else {
ctrList = pod.Containers().List()
}
}
}
ctrList = s.filterContainerList(filter, ctrList)
}
for _, ctr := range ctrList {

View file

@ -4,10 +4,32 @@ import (
"fmt"
"time"
"github.com/kubernetes-incubator/cri-o/lib"
"github.com/kubernetes-incubator/cri-o/oci"
"golang.org/x/net/context"
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
)
func buildContainerStats(stats *lib.ContainerStats, container *oci.Container) *pb.ContainerStats {
return &pb.ContainerStats{
Attributes: &pb.ContainerAttributes{
Id: container.ID(),
Metadata: container.Metadata(),
Labels: container.Labels(),
Annotations: container.Annotations(),
},
Cpu: &pb.CpuUsage{
Timestamp: stats.SystemNano,
UsageCoreNanoSeconds: &pb.UInt64Value{Value: stats.CPUNano},
},
Memory: &pb.MemoryUsage{
Timestamp: stats.SystemNano,
WorkingSetBytes: &pb.UInt64Value{Value: stats.MemUsage},
},
WritableLayer: nil,
}
}
// ContainerStats returns stats of the container. If the container does not
// exist, the call returns an error.
func (s *Server) ContainerStats(ctx context.Context, req *pb.ContainerStatsRequest) (resp *pb.ContainerStatsResponse, err error) {
@ -16,5 +38,16 @@ func (s *Server) ContainerStats(ctx context.Context, req *pb.ContainerStatsReque
recordOperation(operation, time.Now())
recordError(operation, err)
}()
return nil, fmt.Errorf("not implemented")
container := s.GetContainer(req.ContainerId)
if container == nil {
return nil, fmt.Errorf("invalid container")
}
stats, err := s.GetContainerStats(container, &lib.ContainerStats{})
if err != nil {
return nil, err
}
return &pb.ContainerStatsResponse{Stats: buildContainerStats(stats, container)}, nil
}

View file

@ -1,9 +1,10 @@
package server
import (
"fmt"
"time"
"github.com/kubernetes-incubator/cri-o/lib"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
)
@ -15,5 +16,34 @@ func (s *Server) ListContainerStats(ctx context.Context, req *pb.ListContainerSt
recordOperation(operation, time.Now())
recordError(operation, err)
}()
return nil, fmt.Errorf("not implemented")
ctrList, err := s.ContainerServer.ListContainers()
if err != nil {
return nil, err
}
filter := req.GetFilter()
if filter != nil {
cFilter := &pb.ContainerFilter{
Id: req.Filter.Id,
PodSandboxId: req.Filter.PodSandboxId,
LabelSelector: req.Filter.LabelSelector,
}
ctrList = s.filterContainerList(cFilter, ctrList)
}
var allStats []*pb.ContainerStats
for _, container := range ctrList {
stats, err := s.GetContainerStats(container, &lib.ContainerStats{})
if err != nil {
logrus.Warn("unable to get stats for container %s", container.ID())
continue
}
response := buildContainerStats(stats, container)
allStats = append(allStats, response)
}
return &pb.ListContainerStatsResponse{
Stats: allStats,
}, nil
}