kubelet: Implement support for Windows podAndContainerStatsFromCRI

Part of kubernetes/enhancements#2371

Follow up to the initial work introducing CRI API fields for Windows
metrics collection #110754
Windows equivalent work for adding support for Windows
podAndContainerStatsFromCRI #103095, which will allow users to get
Windows pod and container stats only from CRI.

Signed-off-by: mansikulkarni96 <mankulka@redhat.com>
This commit is contained in:
mansikulkarni96 2023-03-29 00:56:07 -04:00
parent 14952cef5b
commit 7926656038
No known key found for this signature in database
GPG Key ID: 1C99C5F5A5B9C210

View File

@ -22,10 +22,12 @@ package stats
import ( import (
"time" "time"
"k8s.io/klog/v2"
"github.com/Microsoft/hcsshim" "github.com/Microsoft/hcsshim"
cadvisorapiv2 "github.com/google/cadvisor/info/v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
"k8s.io/klog/v2"
statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1" statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
) )
@ -79,6 +81,101 @@ func (p *criStatsProvider) listContainerNetworkStats() (map[string]*statsapi.Net
return networkStats, nil return networkStats, nil
} }
func (p *criStatsProvider) addCRIPodContainerStats(criSandboxStat *runtimeapi.PodSandboxStats,
ps *statsapi.PodStats, fsIDtoInfo map[runtimeapi.FilesystemIdentifier]*cadvisorapiv2.FsInfo,
containerMap map[string]*runtimeapi.Container,
podSandbox *runtimeapi.PodSandbox,
rootFsInfo *cadvisorapiv2.FsInfo,
updateCPUNanoCoreUsage bool) {
for _, criContainerStat := range criSandboxStat.Windows.Containers {
container, found := containerMap[criContainerStat.Attributes.Id]
if !found {
continue
}
// Fill available stats for full set of required pod stats
cs := p.makeWinContainerStats(criContainerStat, container, rootFsInfo, fsIDtoInfo, podSandbox.GetMetadata())
ps.Containers = append(ps.Containers, *cs)
}
}
func (p *criStatsProvider) makeWinContainerStats(
stats *runtimeapi.WindowsContainerStats,
container *runtimeapi.Container,
rootFsInfo *cadvisorapiv2.FsInfo,
fsIDtoInfo map[runtimeapi.FilesystemIdentifier]*cadvisorapiv2.FsInfo,
meta *runtimeapi.PodSandboxMetadata) *statsapi.ContainerStats {
result := &statsapi.ContainerStats{
Name: stats.Attributes.Metadata.Name,
// The StartTime in the summary API is the container creation time.
StartTime: metav1.NewTime(time.Unix(0, container.CreatedAt)),
CPU: &statsapi.CPUStats{},
Memory: &statsapi.MemoryStats{},
Rootfs: &statsapi.FsStats{},
// UserDefinedMetrics is not supported by CRI.
}
if stats.Cpu != nil {
result.CPU.Time = metav1.NewTime(time.Unix(0, stats.Cpu.Timestamp))
if stats.Cpu.UsageCoreNanoSeconds != nil {
result.CPU.UsageCoreNanoSeconds = &stats.Cpu.UsageCoreNanoSeconds.Value
}
if stats.Cpu.UsageNanoCores != nil {
result.CPU.UsageNanoCores = &stats.Cpu.UsageNanoCores.Value
}
} else {
result.CPU.Time = metav1.NewTime(time.Unix(0, time.Now().UnixNano()))
result.CPU.UsageCoreNanoSeconds = uint64Ptr(0)
result.CPU.UsageNanoCores = uint64Ptr(0)
}
if stats.Memory != nil {
result.Memory.Time = metav1.NewTime(time.Unix(0, stats.Memory.Timestamp))
if stats.Memory.WorkingSetBytes != nil {
result.Memory.WorkingSetBytes = &stats.Memory.WorkingSetBytes.Value
}
if stats.Memory.AvailableBytes != nil {
result.Memory.AvailableBytes = &stats.Memory.AvailableBytes.Value
}
if stats.Memory.PageFaults != nil {
result.Memory.AvailableBytes = &stats.Memory.PageFaults.Value
}
} else {
result.Memory.Time = metav1.NewTime(time.Unix(0, time.Now().UnixNano()))
result.Memory.WorkingSetBytes = uint64Ptr(0)
result.Memory.AvailableBytes = uint64Ptr(0)
result.Memory.PageFaults = uint64Ptr(0)
}
if stats.WritableLayer != nil {
result.Rootfs.Time = metav1.NewTime(time.Unix(0, stats.WritableLayer.Timestamp))
if stats.WritableLayer.UsedBytes != nil {
result.Rootfs.UsedBytes = &stats.WritableLayer.UsedBytes.Value
}
}
fsID := stats.GetWritableLayer().GetFsId()
if fsID != nil {
imageFsInfo, found := fsIDtoInfo[*fsID]
if !found {
imageFsInfo = p.getFsInfo(fsID)
fsIDtoInfo[*fsID] = imageFsInfo
}
if imageFsInfo != nil {
// The image filesystem id is unknown to the local node or there's
// an error on retrieving the stats. In these cases, we omit those stats
// and return the best-effort partial result. See
// https://github.com/kubernetes/heapster/issues/1793.
result.Rootfs.AvailableBytes = &imageFsInfo.Available
result.Rootfs.CapacityBytes = &imageFsInfo.Capacity
}
}
// NOTE: This doesn't support the old pod log path, `/var/log/pods/UID`. For containers
// using old log path, empty log stats are returned. This is fine, because we don't
// officially support in-place upgrade anyway.
var err error
result.Logs, err = p.hostStatsProvider.getPodContainerLogStats(meta.GetNamespace(), meta.GetName(), types.UID(meta.GetUid()), container.GetMetadata().GetName(), rootFsInfo)
if err != nil {
klog.ErrorS(err, "Unable to fetch container log stats", "containerName", container.GetMetadata().GetName())
}
return result
}
// hcsStatsToNetworkStats converts hcsshim.Statistics.Network to statsapi.NetworkStats // hcsStatsToNetworkStats converts hcsshim.Statistics.Network to statsapi.NetworkStats
func hcsStatsToNetworkStats(timestamp time.Time, hcsStats *hcsshim.HNSEndpointStats, endpointName string) *statsapi.NetworkStats { func hcsStatsToNetworkStats(timestamp time.Time, hcsStats *hcsshim.HNSEndpointStats, endpointName string) *statsapi.NetworkStats {
result := &statsapi.NetworkStats{ result := &statsapi.NetworkStats{
@ -104,6 +201,64 @@ func hcsStatToInterfaceStat(hcsStats *hcsshim.HNSEndpointStats, endpointName str
return iStat return iStat
} }
func addCRIPodCPUStats(ps *statsapi.PodStats, criPodStat *runtimeapi.PodSandboxStats) {
if criPodStat == nil || criPodStat.Windows == nil || criPodStat.Windows.Cpu == nil {
return
}
criCPU := criPodStat.Windows.Cpu
ps.CPU = &statsapi.CPUStats{
Time: metav1.NewTime(time.Unix(0, criCPU.Timestamp)),
UsageNanoCores: valueOfUInt64Value(criCPU.UsageNanoCores),
UsageCoreNanoSeconds: valueOfUInt64Value(criCPU.UsageCoreNanoSeconds),
}
}
func addCRIPodMemoryStats(ps *statsapi.PodStats, criPodStat *runtimeapi.PodSandboxStats) {
if criPodStat == nil || criPodStat.Windows == nil || criPodStat.Windows.Memory == nil {
return
}
criMemory := criPodStat.Windows.Memory
ps.Memory = &statsapi.MemoryStats{
Time: metav1.NewTime(time.Unix(0, criMemory.Timestamp)),
AvailableBytes: valueOfUInt64Value(criMemory.AvailableBytes),
WorkingSetBytes: valueOfUInt64Value(criMemory.WorkingSetBytes),
PageFaults: valueOfUInt64Value(criMemory.PageFaults),
}
}
func addCRIPodProcessStats(ps *statsapi.PodStats, criPodStat *runtimeapi.PodSandboxStats) {
if criPodStat == nil || criPodStat.Windows == nil || criPodStat.Windows.Process == nil {
return
}
ps.ProcessStats = &statsapi.ProcessStats{
ProcessCount: valueOfUInt64Value(criPodStat.Windows.Process.ProcessCount),
}
}
func addCRIPodNetworkStats(ps *statsapi.PodStats, criPodStat *runtimeapi.PodSandboxStats) {
if criPodStat == nil || criPodStat.Windows == nil || criPodStat.Windows.Network == nil {
return
}
criNetwork := criPodStat.Windows.Network
iStats := statsapi.NetworkStats{
Time: metav1.NewTime(time.Unix(0, criNetwork.Timestamp)),
InterfaceStats: criInterfaceToWinSummary(criNetwork.DefaultInterface),
Interfaces: make([]statsapi.InterfaceStats, 0, len(criNetwork.Interfaces)),
}
for _, iface := range criNetwork.Interfaces {
iStats.Interfaces = append(iStats.Interfaces, criInterfaceToWinSummary(iface))
}
ps.Network = &iStats
}
func criInterfaceToWinSummary(criIface *runtimeapi.WindowsNetworkInterfaceUsage) statsapi.InterfaceStats {
return statsapi.InterfaceStats{
Name: criIface.Name,
RxBytes: valueOfUInt64Value(criIface.RxBytes),
TxBytes: valueOfUInt64Value(criIface.TxBytes),
}
}
// newNetworkStatsProvider uses the real windows hcsshim if not provided otherwise if the interface is provided // newNetworkStatsProvider uses the real windows hcsshim if not provided otherwise if the interface is provided
// by the cristatsprovider in testing scenarios it uses that one // by the cristatsprovider in testing scenarios it uses that one
func newNetworkStatsProvider(p *criStatsProvider) windowsNetworkStatsProvider { func newNetworkStatsProvider(p *criStatsProvider) windowsNetworkStatsProvider {