k8s.io/kubernetes@v1.29.3/pkg/kubelet/stats/cadvisor_stats_provider.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package stats
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"path/filepath"
    23  	"sort"
    24  	"strings"
    25  
    26  	cadvisorapiv2 "github.com/google/cadvisor/info/v2"
    27  	"k8s.io/klog/v2"
    28  
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/types"
    31  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    32  	statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
    33  	kubetypes "k8s.io/kubelet/pkg/types"
    34  	"k8s.io/kubernetes/pkg/features"
    35  	"k8s.io/kubernetes/pkg/kubelet/cadvisor"
    36  	"k8s.io/kubernetes/pkg/kubelet/cm"
    37  	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
    38  	"k8s.io/kubernetes/pkg/kubelet/leaky"
    39  	"k8s.io/kubernetes/pkg/kubelet/server/stats"
    40  	"k8s.io/kubernetes/pkg/kubelet/status"
    41  )
    42  
    43  // cadvisorStatsProvider implements the containerStatsProvider interface by
    44  // getting the container stats from cAdvisor. This is needed by
    45  // integrations which do not provide stats from CRI. See
    46  // `pkg/kubelet/cadvisor/util.go#UsingLegacyCadvisorStats` for the logic for
    47  // determining which integrations do not provide stats from CRI.
    48  type cadvisorStatsProvider struct {
    49  	// cadvisor is used to get the stats of the cgroup for the containers that
    50  	// are managed by pods.
    51  	cadvisor cadvisor.Interface
    52  	// resourceAnalyzer is used to get the volume stats of the pods.
    53  	resourceAnalyzer stats.ResourceAnalyzer
    54  	// imageService is used to get the stats of the image filesystem.
    55  	imageService kubecontainer.ImageService
    56  	// statusProvider is used to get pod metadata
    57  	statusProvider status.PodStatusProvider
    58  	// hostStatsProvider is used to get pod host stat usage.
    59  	hostStatsProvider HostStatsProvider
    60  }
    61  
    62  // newCadvisorStatsProvider returns a containerStatsProvider that provides
    63  // container stats from cAdvisor.
    64  func newCadvisorStatsProvider(
    65  	cadvisor cadvisor.Interface,
    66  	resourceAnalyzer stats.ResourceAnalyzer,
    67  	imageService kubecontainer.ImageService,
    68  	statusProvider status.PodStatusProvider,
    69  	hostStatsProvider HostStatsProvider,
    70  ) containerStatsProvider {
    71  	return &cadvisorStatsProvider{
    72  		cadvisor:          cadvisor,
    73  		resourceAnalyzer:  resourceAnalyzer,
    74  		imageService:      imageService,
    75  		statusProvider:    statusProvider,
    76  		hostStatsProvider: hostStatsProvider,
    77  	}
    78  }
    79  
    80  // ListPodStats returns the stats of all the pod-managed containers.
    81  func (p *cadvisorStatsProvider) ListPodStats(_ context.Context) ([]statsapi.PodStats, error) {
    82  	// Gets node root filesystem information and image filesystem stats, which
    83  	// will be used to populate the available and capacity bytes/inodes in
    84  	// container stats.
    85  	rootFsInfo, err := p.cadvisor.RootFsInfo()
    86  	if err != nil {
    87  		return nil, fmt.Errorf("failed to get rootFs info: %v", err)
    88  	}
    89  	imageFsInfo, err := p.cadvisor.ImagesFsInfo()
    90  	if err != nil {
    91  		return nil, fmt.Errorf("failed to get imageFs info: %v", err)
    92  	}
    93  	infos, err := getCadvisorContainerInfo(p.cadvisor)
    94  	if err != nil {
    95  		return nil, fmt.Errorf("failed to get container info from cadvisor: %v", err)
    96  	}
    97  
    98  	filteredInfos, allInfos := filterTerminatedContainerInfoAndAssembleByPodCgroupKey(infos)
    99  	// Map each container to a pod and update the PodStats with container data.
   100  	podToStats := map[statsapi.PodReference]*statsapi.PodStats{}
   101  	for key, cinfo := range filteredInfos {
   102  		// On systemd using devicemapper each mount into the container has an
   103  		// associated cgroup. We ignore them to ensure we do not get duplicate
   104  		// entries in our summary. For details on .mount units:
   105  		// http://man7.org/linux/man-pages/man5/systemd.mount.5.html
   106  		if strings.HasSuffix(key, ".mount") {
   107  			continue
   108  		}
   109  		// Build the Pod key if this container is managed by a Pod
   110  		if !isPodManagedContainer(&cinfo) {
   111  			continue
   112  		}
   113  		ref := buildPodRef(cinfo.Spec.Labels)
   114  
   115  		// Lookup the PodStats for the pod using the PodRef. If none exists,
   116  		// initialize a new entry.
   117  		podStats, found := podToStats[ref]
   118  		if !found {
   119  			podStats = &statsapi.PodStats{PodRef: ref}
   120  			podToStats[ref] = podStats
   121  		}
   122  
   123  		// Update the PodStats entry with the stats from the container by
   124  		// adding it to podStats.Containers.
   125  		containerName := kubetypes.GetContainerName(cinfo.Spec.Labels)
   126  		if containerName == leaky.PodInfraContainerName {
   127  			// Special case for infrastructure container which is hidden from
   128  			// the user and has network stats.
   129  			podStats.Network = cadvisorInfoToNetworkStats(&cinfo)
   130  		} else {
   131  			containerStat := cadvisorInfoToContainerStats(containerName, &cinfo, &rootFsInfo, &imageFsInfo)
   132  			// NOTE: This doesn't support the old pod log path, `/var/log/pods/UID`. For containers
   133  			// using old log path, they will be populated by cadvisorInfoToContainerStats.
   134  			podUID := types.UID(podStats.PodRef.UID)
   135  			logs, err := p.hostStatsProvider.getPodContainerLogStats(podStats.PodRef.Namespace, podStats.PodRef.Name, podUID, containerName, &rootFsInfo)
   136  			if err != nil {
   137  				klog.ErrorS(err, "Unable to fetch container log stats", "containerName", containerName)
   138  			} else {
   139  				containerStat.Logs = logs
   140  			}
   141  			podStats.Containers = append(podStats.Containers, *containerStat)
   142  		}
   143  	}
   144  
   145  	// Add each PodStats to the result.
   146  	result := make([]statsapi.PodStats, 0, len(podToStats))
   147  	for _, podStats := range podToStats {
   148  		makePodStorageStats(podStats, &rootFsInfo, p.resourceAnalyzer, p.hostStatsProvider, false)
   149  
   150  		podUID := types.UID(podStats.PodRef.UID)
   151  		// Lookup the pod-level cgroup's CPU and memory stats
   152  		podInfo := getCadvisorPodInfoFromPodUID(podUID, allInfos)
   153  		if podInfo != nil {
   154  			cpu, memory := cadvisorInfoToCPUandMemoryStats(podInfo)
   155  			podStats.CPU = cpu
   156  			podStats.Memory = memory
   157  			podStats.Swap = cadvisorInfoToSwapStats(podInfo)
   158  			podStats.ProcessStats = cadvisorInfoToProcessStats(podInfo)
   159  		}
   160  
   161  		status, found := p.statusProvider.GetPodStatus(podUID)
   162  		if found && status.StartTime != nil && !status.StartTime.IsZero() {
   163  			podStats.StartTime = *status.StartTime
   164  			// only append stats if we were able to get the start time of the pod
   165  			result = append(result, *podStats)
   166  		}
   167  	}
   168  
   169  	return result, nil
   170  }
   171  
   172  // ListPodStatsAndUpdateCPUNanoCoreUsage updates the cpu nano core usage for
   173  // the containers and returns the stats for all the pod-managed containers.
   174  // For cadvisor, cpu nano core usages are pre-computed and cached, so this
   175  // function simply calls ListPodStats.
   176  func (p *cadvisorStatsProvider) ListPodStatsAndUpdateCPUNanoCoreUsage(ctx context.Context) ([]statsapi.PodStats, error) {
   177  	return p.ListPodStats(ctx)
   178  }
   179  
   180  // ListPodCPUAndMemoryStats returns the cpu and memory stats of all the pod-managed containers.
   181  func (p *cadvisorStatsProvider) ListPodCPUAndMemoryStats(_ context.Context) ([]statsapi.PodStats, error) {
   182  	infos, err := getCadvisorContainerInfo(p.cadvisor)
   183  	if err != nil {
   184  		return nil, fmt.Errorf("failed to get container info from cadvisor: %v", err)
   185  	}
   186  	filteredInfos, allInfos := filterTerminatedContainerInfoAndAssembleByPodCgroupKey(infos)
   187  	// Map each container to a pod and update the PodStats with container data.
   188  	podToStats := map[statsapi.PodReference]*statsapi.PodStats{}
   189  	for key, cinfo := range filteredInfos {
   190  		// On systemd using devicemapper each mount into the container has an
   191  		// associated cgroup. We ignore them to ensure we do not get duplicate
   192  		// entries in our summary. For details on .mount units:
   193  		// http://man7.org/linux/man-pages/man5/systemd.mount.5.html
   194  		if strings.HasSuffix(key, ".mount") {
   195  			continue
   196  		}
   197  		// Build the Pod key if this container is managed by a Pod
   198  		if !isPodManagedContainer(&cinfo) {
   199  			continue
   200  		}
   201  		ref := buildPodRef(cinfo.Spec.Labels)
   202  
   203  		// Lookup the PodStats for the pod using the PodRef. If none exists,
   204  		// initialize a new entry.
   205  		podStats, found := podToStats[ref]
   206  		if !found {
   207  			podStats = &statsapi.PodStats{PodRef: ref}
   208  			podToStats[ref] = podStats
   209  		}
   210  
   211  		// Update the PodStats entry with the stats from the container by
   212  		// adding it to podStats.Containers.
   213  		containerName := kubetypes.GetContainerName(cinfo.Spec.Labels)
   214  		if containerName == leaky.PodInfraContainerName {
   215  			// Special case for infrastructure container which is hidden from
   216  			// the user and has network stats.
   217  			podStats.StartTime = metav1.NewTime(cinfo.Spec.CreationTime)
   218  		} else {
   219  			podStats.Containers = append(podStats.Containers, *cadvisorInfoToContainerCPUAndMemoryStats(containerName, &cinfo))
   220  		}
   221  	}
   222  
   223  	// Add each PodStats to the result.
   224  	result := make([]statsapi.PodStats, 0, len(podToStats))
   225  	for _, podStats := range podToStats {
   226  		podUID := types.UID(podStats.PodRef.UID)
   227  		// Lookup the pod-level cgroup's CPU and memory stats
   228  		podInfo := getCadvisorPodInfoFromPodUID(podUID, allInfos)
   229  		if podInfo != nil {
   230  			cpu, memory := cadvisorInfoToCPUandMemoryStats(podInfo)
   231  			podStats.CPU = cpu
   232  			podStats.Memory = memory
   233  			podStats.Swap = cadvisorInfoToSwapStats(podInfo)
   234  		}
   235  		result = append(result, *podStats)
   236  	}
   237  
   238  	return result, nil
   239  }
   240  
   241  // ImageFsStats returns the stats of the filesystem for storing images.
   242  func (p *cadvisorStatsProvider) ImageFsStats(ctx context.Context) (imageFsRet *statsapi.FsStats, containerFsRet *statsapi.FsStats, errCall error) {
   243  	imageFsInfo, err := p.cadvisor.ImagesFsInfo()
   244  	if err != nil {
   245  		return nil, nil, fmt.Errorf("failed to get imageFs info: %v", err)
   246  	}
   247  
   248  	if !utilfeature.DefaultFeatureGate.Enabled(features.KubeletSeparateDiskGC) {
   249  		imageStats, err := p.imageService.ImageStats(ctx)
   250  		if err != nil || imageStats == nil {
   251  			return nil, nil, fmt.Errorf("failed to get image stats: %v", err)
   252  		}
   253  
   254  		var imageFsInodesUsed *uint64
   255  		if imageFsInfo.Inodes != nil && imageFsInfo.InodesFree != nil {
   256  			imageFsIU := *imageFsInfo.Inodes - *imageFsInfo.InodesFree
   257  			imageFsInodesUsed = &imageFsIU
   258  		}
   259  
   260  		imageFs := &statsapi.FsStats{
   261  			Time:           metav1.NewTime(imageFsInfo.Timestamp),
   262  			AvailableBytes: &imageFsInfo.Available,
   263  			CapacityBytes:  &imageFsInfo.Capacity,
   264  			UsedBytes:      &imageStats.TotalStorageBytes,
   265  			InodesFree:     imageFsInfo.InodesFree,
   266  			Inodes:         imageFsInfo.Inodes,
   267  			InodesUsed:     imageFsInodesUsed,
   268  		}
   269  		return imageFs, imageFs, nil
   270  	}
   271  	containerFsInfo, err := p.cadvisor.ContainerFsInfo()
   272  	if err != nil {
   273  		return nil, nil, fmt.Errorf("failed to get container fs info: %v", err)
   274  	}
   275  	imageStats, err := p.imageService.ImageFsInfo(ctx)
   276  	if err != nil || imageStats == nil {
   277  		return nil, nil, fmt.Errorf("failed to get image stats: %v", err)
   278  	}
   279  	splitFileSystem := false
   280  	if imageStats.ImageFilesystems[0].FsId.Mountpoint != imageStats.ContainerFilesystems[0].FsId.Mountpoint {
   281  		klog.InfoS("Detect Split Filesystem", "ImageFilesystems", imageStats.ImageFilesystems[0], "ContainerFilesystems", imageStats.ContainerFilesystems[0])
   282  		splitFileSystem = true
   283  	}
   284  	imageFs := imageStats.ImageFilesystems[0]
   285  	var imageFsInodesUsed *uint64
   286  	if imageFsInfo.Inodes != nil && imageFsInfo.InodesFree != nil {
   287  		imageFsIU := *imageFsInfo.Inodes - *imageFsInfo.InodesFree
   288  		imageFsInodesUsed = &imageFsIU
   289  	}
   290  
   291  	fsStats := &statsapi.FsStats{
   292  		Time:           metav1.NewTime(imageFsInfo.Timestamp),
   293  		AvailableBytes: &imageFsInfo.Available,
   294  		CapacityBytes:  &imageFsInfo.Capacity,
   295  		UsedBytes:      &imageFs.UsedBytes.Value,
   296  		InodesFree:     imageFsInfo.InodesFree,
   297  		Inodes:         imageFsInfo.Inodes,
   298  		InodesUsed:     imageFsInodesUsed,
   299  	}
   300  	if !splitFileSystem {
   301  		return fsStats, fsStats, nil
   302  	}
   303  
   304  	containerFs := imageStats.ContainerFilesystems[0]
   305  	var containerFsInodesUsed *uint64
   306  	if containerFsInfo.Inodes != nil && containerFsInfo.InodesFree != nil {
   307  		containerFsIU := *containerFsInfo.Inodes - *containerFsInfo.InodesFree
   308  		containerFsInodesUsed = &containerFsIU
   309  	}
   310  
   311  	fsContainerStats := &statsapi.FsStats{
   312  		Time:           metav1.NewTime(containerFsInfo.Timestamp),
   313  		AvailableBytes: &containerFsInfo.Available,
   314  		CapacityBytes:  &containerFsInfo.Capacity,
   315  		UsedBytes:      &containerFs.UsedBytes.Value,
   316  		InodesFree:     containerFsInfo.InodesFree,
   317  		Inodes:         containerFsInfo.Inodes,
   318  		InodesUsed:     containerFsInodesUsed,
   319  	}
   320  
   321  	return fsStats, fsContainerStats, nil
   322  }
   323  
   324  // ImageFsDevice returns name of the device where the image filesystem locates,
   325  // e.g. /dev/sda1.
   326  func (p *cadvisorStatsProvider) ImageFsDevice(_ context.Context) (string, error) {
   327  	imageFsInfo, err := p.cadvisor.ImagesFsInfo()
   328  	if err != nil {
   329  		return "", err
   330  	}
   331  	return imageFsInfo.Device, nil
   332  }
   333  
   334  // buildPodRef returns a PodReference that identifies the Pod managing cinfo
   335  func buildPodRef(containerLabels map[string]string) statsapi.PodReference {
   336  	podName := kubetypes.GetPodName(containerLabels)
   337  	podNamespace := kubetypes.GetPodNamespace(containerLabels)
   338  	podUID := kubetypes.GetPodUID(containerLabels)
   339  	return statsapi.PodReference{Name: podName, Namespace: podNamespace, UID: podUID}
   340  }
   341  
   342  // isPodManagedContainer returns true if the cinfo container is managed by a Pod
   343  func isPodManagedContainer(cinfo *cadvisorapiv2.ContainerInfo) bool {
   344  	podName := kubetypes.GetPodName(cinfo.Spec.Labels)
   345  	podNamespace := kubetypes.GetPodNamespace(cinfo.Spec.Labels)
   346  	managed := podName != "" && podNamespace != ""
   347  	if !managed && podName != podNamespace {
   348  		klog.InfoS(
   349  			"Expect container to have either both podName and podNamespace labels, or neither",
   350  			"podNameLabel", podName, "podNamespaceLabel", podNamespace)
   351  	}
   352  	return managed
   353  }
   354  
   355  // getCadvisorPodInfoFromPodUID returns a pod cgroup information by matching the podUID with its CgroupName identifier base name
   356  func getCadvisorPodInfoFromPodUID(podUID types.UID, infos map[string]cadvisorapiv2.ContainerInfo) *cadvisorapiv2.ContainerInfo {
   357  	if info, found := infos[cm.GetPodCgroupNameSuffix(podUID)]; found {
   358  		return &info
   359  	}
   360  	return nil
   361  }
   362  
   363  // filterTerminatedContainerInfoAndAssembleByPodCgroupKey returns the specified containerInfo but with
   364  // the stats of the terminated containers removed and all containerInfos assembled by pod cgroup key.
   365  // the first return map is container cgroup name <-> ContainerInfo and
   366  // the second return map is pod cgroup key <-> ContainerInfo.
   367  // A ContainerInfo is considered to be of a terminated container if it has an
   368  // older CreationTime and zero CPU instantaneous and memory RSS usage.
   369  func filterTerminatedContainerInfoAndAssembleByPodCgroupKey(containerInfo map[string]cadvisorapiv2.ContainerInfo) (map[string]cadvisorapiv2.ContainerInfo, map[string]cadvisorapiv2.ContainerInfo) {
   370  	cinfoMap := make(map[containerID][]containerInfoWithCgroup)
   371  	cinfosByPodCgroupKey := make(map[string]cadvisorapiv2.ContainerInfo)
   372  	for key, cinfo := range containerInfo {
   373  		var podCgroupKey string
   374  		if cm.IsSystemdStyleName(key) {
   375  			// Convert to internal cgroup name and take the last component only.
   376  			internalCgroupName := cm.ParseSystemdToCgroupName(key)
   377  			podCgroupKey = internalCgroupName[len(internalCgroupName)-1]
   378  		} else {
   379  			// Take last component only.
   380  			podCgroupKey = filepath.Base(key)
   381  		}
   382  		cinfosByPodCgroupKey[podCgroupKey] = cinfo
   383  		if !isPodManagedContainer(&cinfo) {
   384  			continue
   385  		}
   386  		cinfoID := containerID{
   387  			podRef:        buildPodRef(cinfo.Spec.Labels),
   388  			containerName: kubetypes.GetContainerName(cinfo.Spec.Labels),
   389  		}
   390  		cinfoMap[cinfoID] = append(cinfoMap[cinfoID], containerInfoWithCgroup{
   391  			cinfo:  cinfo,
   392  			cgroup: key,
   393  		})
   394  	}
   395  	result := make(map[string]cadvisorapiv2.ContainerInfo)
   396  	for _, refs := range cinfoMap {
   397  		if len(refs) == 1 {
   398  			// ContainerInfo with no CPU/memory/network usage for uncleaned cgroups of
   399  			// already terminated containers, which should not be shown in the results.
   400  			if !isContainerTerminated(&refs[0].cinfo) {
   401  				result[refs[0].cgroup] = refs[0].cinfo
   402  			}
   403  			continue
   404  		}
   405  		sort.Sort(ByCreationTime(refs))
   406  		for i := len(refs) - 1; i >= 0; i-- {
   407  			if hasMemoryAndCPUInstUsage(&refs[i].cinfo) {
   408  				result[refs[i].cgroup] = refs[i].cinfo
   409  				break
   410  			}
   411  		}
   412  	}
   413  	return result, cinfosByPodCgroupKey
   414  }
   415  
   416  // ByCreationTime implements sort.Interface for []containerInfoWithCgroup based
   417  // on the cinfo.Spec.CreationTime field.
   418  type ByCreationTime []containerInfoWithCgroup
   419  
   420  func (a ByCreationTime) Len() int      { return len(a) }
   421  func (a ByCreationTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   422  func (a ByCreationTime) Less(i, j int) bool {
   423  	if a[i].cinfo.Spec.CreationTime.Equal(a[j].cinfo.Spec.CreationTime) {
   424  		// There shouldn't be two containers with the same name and/or the same
   425  		// creation time. However, to make the logic here robust, we break the
   426  		// tie by moving the one without CPU instantaneous or memory RSS usage
   427  		// to the beginning.
   428  		return hasMemoryAndCPUInstUsage(&a[j].cinfo)
   429  	}
   430  	return a[i].cinfo.Spec.CreationTime.Before(a[j].cinfo.Spec.CreationTime)
   431  }
   432  
   433  // containerID is the identity of a container in a pod.
   434  type containerID struct {
   435  	podRef        statsapi.PodReference
   436  	containerName string
   437  }
   438  
   439  // containerInfoWithCgroup contains the ContainerInfo and its cgroup name.
   440  type containerInfoWithCgroup struct {
   441  	cinfo  cadvisorapiv2.ContainerInfo
   442  	cgroup string
   443  }
   444  
   445  // hasMemoryAndCPUInstUsage returns true if the specified container info has
   446  // both non-zero CPU instantaneous usage and non-zero memory RSS usage, and
   447  // false otherwise.
   448  func hasMemoryAndCPUInstUsage(info *cadvisorapiv2.ContainerInfo) bool {
   449  	if !info.Spec.HasCpu || !info.Spec.HasMemory {
   450  		return false
   451  	}
   452  	cstat, found := latestContainerStats(info)
   453  	if !found {
   454  		return false
   455  	}
   456  	if cstat.CpuInst == nil {
   457  		return false
   458  	}
   459  	return cstat.CpuInst.Usage.Total != 0 && cstat.Memory.RSS != 0
   460  }
   461  
   462  // isContainerTerminated returns true if the specified container meet one of the following conditions
   463  // 1. info.spec both cpu memory and network are false conditions
   464  // 2. info.Stats both network and cpu or memory are nil
   465  // 3. both zero CPU instantaneous usage zero memory RSS usage and zero network usage,
   466  // and false otherwise.
   467  func isContainerTerminated(info *cadvisorapiv2.ContainerInfo) bool {
   468  	if !info.Spec.HasCpu && !info.Spec.HasMemory && !info.Spec.HasNetwork {
   469  		return true
   470  	}
   471  	cstat, found := latestContainerStats(info)
   472  	if !found {
   473  		return true
   474  	}
   475  	if cstat.Network != nil {
   476  		iStats := cadvisorInfoToNetworkStats(info)
   477  		if iStats != nil {
   478  			for _, iStat := range iStats.Interfaces {
   479  				if *iStat.RxErrors != 0 || *iStat.TxErrors != 0 || *iStat.RxBytes != 0 || *iStat.TxBytes != 0 {
   480  					return false
   481  				}
   482  			}
   483  		}
   484  	}
   485  	if cstat.CpuInst == nil || cstat.Memory == nil {
   486  		return true
   487  	}
   488  	return cstat.CpuInst.Usage.Total == 0 && cstat.Memory.RSS == 0
   489  }
   490  
   491  func getCadvisorContainerInfo(ca cadvisor.Interface) (map[string]cadvisorapiv2.ContainerInfo, error) {
   492  	infos, err := ca.ContainerInfoV2("/", cadvisorapiv2.RequestOptions{
   493  		IdType:    cadvisorapiv2.TypeName,
   494  		Count:     2, // 2 samples are needed to compute "instantaneous" CPU
   495  		Recursive: true,
   496  	})
   497  	if err != nil {
   498  		if _, ok := infos["/"]; ok {
   499  			// If the failure is partial, log it and return a best-effort
   500  			// response.
   501  			klog.ErrorS(err, "Partial failure issuing cadvisor.ContainerInfoV2")
   502  		} else {
   503  			return nil, fmt.Errorf("failed to get root cgroup stats: %v", err)
   504  		}
   505  	}
   506  	return infos, nil
   507  }