Merge pull request #1249 from theatrus/add-container-stats
Add methods for listing and fetching container stats
This commit is contained in:
commit
8f5e37a83c
4 changed files with 111 additions and 42 deletions
14
lib/stats.go
14
lib/stats.go
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue