mirror of https://github.com/k3s-io/k3s
Merge pull request #73659 from feiskyer/usage-nano-cores
Kubelet: add usageNanoCores from CRI stats providerpull/564/head
commit
ee44e24cd3
|
@ -22,6 +22,7 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
cadvisorfs "github.com/google/cadvisor/fs"
|
cadvisorfs "github.com/google/cadvisor/fs"
|
||||||
|
@ -38,6 +39,11 @@ import (
|
||||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// defaultCachePeriod is the default cache period for each cpuUsage.
|
||||||
|
defaultCachePeriod = 10 * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
// criStatsProvider implements the containerStatsProvider interface by getting
|
// criStatsProvider implements the containerStatsProvider interface by getting
|
||||||
// the container stats from CRI.
|
// the container stats from CRI.
|
||||||
type criStatsProvider struct {
|
type criStatsProvider struct {
|
||||||
|
@ -54,6 +60,10 @@ type criStatsProvider struct {
|
||||||
imageService internalapi.ImageManagerService
|
imageService internalapi.ImageManagerService
|
||||||
// logMetrics provides the metrics for container logs
|
// logMetrics provides the metrics for container logs
|
||||||
logMetricsService LogMetricsService
|
logMetricsService LogMetricsService
|
||||||
|
|
||||||
|
// cpuUsageCache caches the cpu usage for containers.
|
||||||
|
cpuUsageCache map[string]*runtimeapi.CpuUsage
|
||||||
|
mutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// newCRIStatsProvider returns a containerStatsProvider implementation that
|
// newCRIStatsProvider returns a containerStatsProvider implementation that
|
||||||
|
@ -71,6 +81,7 @@ func newCRIStatsProvider(
|
||||||
runtimeService: runtimeService,
|
runtimeService: runtimeService,
|
||||||
imageService: imageService,
|
imageService: imageService,
|
||||||
logMetricsService: logMetricsService,
|
logMetricsService: logMetricsService,
|
||||||
|
cpuUsageCache: make(map[string]*runtimeapi.CpuUsage),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,6 +176,8 @@ func (p *criStatsProvider) ListPodStats() ([]statsapi.PodStats, error) {
|
||||||
}
|
}
|
||||||
ps.Containers = append(ps.Containers, *cs)
|
ps.Containers = append(ps.Containers, *cs)
|
||||||
}
|
}
|
||||||
|
// cleanup outdated caches.
|
||||||
|
p.cleanupOutdatedCaches()
|
||||||
|
|
||||||
result := make([]statsapi.PodStats, 0, len(sandboxIDToPodStats))
|
result := make([]statsapi.PodStats, 0, len(sandboxIDToPodStats))
|
||||||
for _, s := range sandboxIDToPodStats {
|
for _, s := range sandboxIDToPodStats {
|
||||||
|
@ -247,6 +260,8 @@ func (p *criStatsProvider) ListPodCPUAndMemoryStats() ([]statsapi.PodStats, erro
|
||||||
}
|
}
|
||||||
ps.Containers = append(ps.Containers, *cs)
|
ps.Containers = append(ps.Containers, *cs)
|
||||||
}
|
}
|
||||||
|
// cleanup outdated caches.
|
||||||
|
p.cleanupOutdatedCaches()
|
||||||
|
|
||||||
result := make([]statsapi.PodStats, 0, len(sandboxIDToPodStats))
|
result := make([]statsapi.PodStats, 0, len(sandboxIDToPodStats))
|
||||||
for _, s := range sandboxIDToPodStats {
|
for _, s := range sandboxIDToPodStats {
|
||||||
|
@ -450,6 +465,11 @@ func (p *criStatsProvider) makeContainerStats(
|
||||||
if stats.Cpu.UsageCoreNanoSeconds != nil {
|
if stats.Cpu.UsageCoreNanoSeconds != nil {
|
||||||
result.CPU.UsageCoreNanoSeconds = &stats.Cpu.UsageCoreNanoSeconds.Value
|
result.CPU.UsageCoreNanoSeconds = &stats.Cpu.UsageCoreNanoSeconds.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usageNanoCores := p.getContainerUsageNanoCores(stats)
|
||||||
|
if usageNanoCores != nil {
|
||||||
|
result.CPU.UsageNanoCores = usageNanoCores
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if stats.Memory != nil {
|
if stats.Memory != nil {
|
||||||
result.Memory.Time = metav1.NewTime(time.Unix(0, stats.Memory.Timestamp))
|
result.Memory.Time = metav1.NewTime(time.Unix(0, stats.Memory.Timestamp))
|
||||||
|
@ -506,6 +526,11 @@ func (p *criStatsProvider) makeContainerCPUAndMemoryStats(
|
||||||
if stats.Cpu.UsageCoreNanoSeconds != nil {
|
if stats.Cpu.UsageCoreNanoSeconds != nil {
|
||||||
result.CPU.UsageCoreNanoSeconds = &stats.Cpu.UsageCoreNanoSeconds.Value
|
result.CPU.UsageCoreNanoSeconds = &stats.Cpu.UsageCoreNanoSeconds.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usageNanoCores := p.getContainerUsageNanoCores(stats)
|
||||||
|
if usageNanoCores != nil {
|
||||||
|
result.CPU.UsageNanoCores = usageNanoCores
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if stats.Memory != nil {
|
if stats.Memory != nil {
|
||||||
result.Memory.Time = metav1.NewTime(time.Unix(0, stats.Memory.Timestamp))
|
result.Memory.Time = metav1.NewTime(time.Unix(0, stats.Memory.Timestamp))
|
||||||
|
@ -516,6 +541,44 @@ func (p *criStatsProvider) makeContainerCPUAndMemoryStats(
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getContainerUsageNanoCores gets usageNanoCores based on cached usageCoreNanoSeconds.
|
||||||
|
func (p *criStatsProvider) getContainerUsageNanoCores(stats *runtimeapi.ContainerStats) *uint64 {
|
||||||
|
if stats == nil || stats.Cpu == nil || stats.Cpu.UsageCoreNanoSeconds == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p.mutex.Lock()
|
||||||
|
defer func() {
|
||||||
|
// Update cache with new value.
|
||||||
|
p.cpuUsageCache[stats.Attributes.Id] = stats.Cpu
|
||||||
|
p.mutex.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
cached, ok := p.cpuUsageCache[stats.Attributes.Id]
|
||||||
|
if !ok || cached.UsageCoreNanoSeconds == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
nanoSeconds := stats.Cpu.Timestamp - cached.Timestamp
|
||||||
|
usageNanoCores := (stats.Cpu.UsageCoreNanoSeconds.Value - cached.UsageCoreNanoSeconds.Value) * uint64(time.Second/time.Nanosecond) / uint64(nanoSeconds)
|
||||||
|
return &usageNanoCores
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *criStatsProvider) cleanupOutdatedCaches() {
|
||||||
|
p.mutex.Lock()
|
||||||
|
defer p.mutex.Unlock()
|
||||||
|
|
||||||
|
for k, v := range p.cpuUsageCache {
|
||||||
|
if v == nil {
|
||||||
|
delete(p.cpuUsageCache, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
if time.Since(time.Unix(0, v.Timestamp)) > defaultCachePeriod {
|
||||||
|
delete(p.cpuUsageCache, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// removeTerminatedContainer returns the specified container but with
|
// removeTerminatedContainer returns the specified container but with
|
||||||
// the stats of the terminated containers removed.
|
// the stats of the terminated containers removed.
|
||||||
func removeTerminatedContainer(containers []*runtimeapi.Container) []*runtimeapi.Container {
|
func removeTerminatedContainer(containers []*runtimeapi.Container) []*runtimeapi.Container {
|
||||||
|
|
|
@ -645,3 +645,110 @@ func makeFakeLogStats(seed int) *volume.Metrics {
|
||||||
m.InodesUsed = resource.NewQuantity(int64(seed+offsetInodeUsage), resource.BinarySI)
|
m.InodesUsed = resource.NewQuantity(int64(seed+offsetInodeUsage), resource.BinarySI)
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetContainerUsageNanoCores(t *testing.T) {
|
||||||
|
var value0 uint64
|
||||||
|
var value1 uint64 = 10000000000
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
cpuUsageCache map[string]*runtimeapi.CpuUsage
|
||||||
|
stats *runtimeapi.ContainerStats
|
||||||
|
expected *uint64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "should return nil if stats is nil",
|
||||||
|
cpuUsageCache: map[string]*runtimeapi.CpuUsage{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return nil if cpu stats is nil",
|
||||||
|
cpuUsageCache: map[string]*runtimeapi.CpuUsage{},
|
||||||
|
stats: &runtimeapi.ContainerStats{
|
||||||
|
Attributes: &runtimeapi.ContainerAttributes{
|
||||||
|
Id: "1",
|
||||||
|
},
|
||||||
|
Cpu: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return nil if usageCoreNanoSeconds is nil",
|
||||||
|
cpuUsageCache: map[string]*runtimeapi.CpuUsage{},
|
||||||
|
stats: &runtimeapi.ContainerStats{
|
||||||
|
Attributes: &runtimeapi.ContainerAttributes{
|
||||||
|
Id: "1",
|
||||||
|
},
|
||||||
|
Cpu: &runtimeapi.CpuUsage{
|
||||||
|
Timestamp: 1,
|
||||||
|
UsageCoreNanoSeconds: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return nil if cpu stats is not cached yet",
|
||||||
|
cpuUsageCache: map[string]*runtimeapi.CpuUsage{},
|
||||||
|
stats: &runtimeapi.ContainerStats{
|
||||||
|
Attributes: &runtimeapi.ContainerAttributes{
|
||||||
|
Id: "1",
|
||||||
|
},
|
||||||
|
Cpu: &runtimeapi.CpuUsage{
|
||||||
|
Timestamp: 1,
|
||||||
|
UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
|
||||||
|
Value: 10000000000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return zero value if cached cpu stats is equal to current value",
|
||||||
|
stats: &runtimeapi.ContainerStats{
|
||||||
|
Attributes: &runtimeapi.ContainerAttributes{
|
||||||
|
Id: "1",
|
||||||
|
},
|
||||||
|
Cpu: &runtimeapi.CpuUsage{
|
||||||
|
Timestamp: 1,
|
||||||
|
UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
|
||||||
|
Value: 10000000000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cpuUsageCache: map[string]*runtimeapi.CpuUsage{
|
||||||
|
"1": {
|
||||||
|
Timestamp: 0,
|
||||||
|
UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
|
||||||
|
Value: 10000000000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &value0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return correct value if cached cpu stats is not equal to current value",
|
||||||
|
stats: &runtimeapi.ContainerStats{
|
||||||
|
Attributes: &runtimeapi.ContainerAttributes{
|
||||||
|
Id: "1",
|
||||||
|
},
|
||||||
|
Cpu: &runtimeapi.CpuUsage{
|
||||||
|
Timestamp: int64(time.Second / time.Nanosecond),
|
||||||
|
UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
|
||||||
|
Value: 20000000000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cpuUsageCache: map[string]*runtimeapi.CpuUsage{
|
||||||
|
"1": {
|
||||||
|
Timestamp: 0,
|
||||||
|
UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
|
||||||
|
Value: 10000000000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &value1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
provider := &criStatsProvider{cpuUsageCache: test.cpuUsageCache}
|
||||||
|
real := provider.getContainerUsageNanoCores(test.stats)
|
||||||
|
assert.Equal(t, test.expected, real, test.desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue