This reduces latency of calling ListPodSandboxStats() by avoiding calling shim API Task(). Signed-off-by: Eric Lin <linxiulei@gmail.com>
		
			
				
	
	
		
			160 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			160 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
   Copyright The containerd Authors.
 | 
						|
 | 
						|
   Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
   you may not use this file except in compliance with the License.
 | 
						|
   You may obtain a copy of the License at
 | 
						|
 | 
						|
       http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
   Unless required by applicable law or agreed to in writing, software
 | 
						|
   distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
   See the License for the specific language governing permissions and
 | 
						|
   limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package server
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/containerd/cgroups/v3"
 | 
						|
	"github.com/containerd/cgroups/v3/cgroup1"
 | 
						|
	cgroupsv2 "github.com/containerd/cgroups/v3/cgroup2"
 | 
						|
	sandboxstore "github.com/containerd/containerd/v2/internal/cri/store/sandbox"
 | 
						|
	"github.com/containerd/errdefs"
 | 
						|
	"github.com/containerd/log"
 | 
						|
	"github.com/containernetworking/plugins/pkg/ns"
 | 
						|
	"github.com/vishvananda/netlink"
 | 
						|
	runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
 | 
						|
)
 | 
						|
 | 
						|
func (c *criService) podSandboxStats(
 | 
						|
	ctx context.Context,
 | 
						|
	sandbox sandboxstore.Sandbox) (*runtime.PodSandboxStats, error) {
 | 
						|
	meta := sandbox.Metadata
 | 
						|
 | 
						|
	if sandbox.Status.Get().State != sandboxstore.StateReady {
 | 
						|
		return nil, fmt.Errorf("failed to get pod sandbox stats since sandbox container %q is not in ready state: %w", meta.ID, errdefs.ErrUnavailable)
 | 
						|
	}
 | 
						|
 | 
						|
	stats, err := metricsForSandbox(sandbox)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("failed getting metrics for sandbox %s: %w", sandbox.ID, err)
 | 
						|
	}
 | 
						|
 | 
						|
	podSandboxStats := &runtime.PodSandboxStats{
 | 
						|
		Linux: &runtime.LinuxPodSandboxStats{},
 | 
						|
		Attributes: &runtime.PodSandboxAttributes{
 | 
						|
			Id:          meta.ID,
 | 
						|
			Metadata:    meta.Config.GetMetadata(),
 | 
						|
			Labels:      meta.Config.GetLabels(),
 | 
						|
			Annotations: meta.Config.GetAnnotations(),
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	if stats != nil {
 | 
						|
		timestamp := time.Now()
 | 
						|
 | 
						|
		cpuStats, err := c.cpuContainerStats(meta.ID, true /* isSandbox */, stats, timestamp)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to obtain cpu stats: %w", err)
 | 
						|
		}
 | 
						|
		podSandboxStats.Linux.Cpu = cpuStats
 | 
						|
 | 
						|
		memoryStats, err := c.memoryContainerStats(meta.ID, stats, timestamp)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to obtain memory stats: %w", err)
 | 
						|
		}
 | 
						|
		podSandboxStats.Linux.Memory = memoryStats
 | 
						|
 | 
						|
		if sandbox.NetNSPath != "" {
 | 
						|
			rxBytes, rxErrors, txBytes, txErrors := getContainerNetIO(ctx, sandbox.NetNSPath)
 | 
						|
			podSandboxStats.Linux.Network = &runtime.NetworkUsage{
 | 
						|
				DefaultInterface: &runtime.NetworkInterfaceUsage{
 | 
						|
					Name:     defaultIfName,
 | 
						|
					RxBytes:  &runtime.UInt64Value{Value: rxBytes},
 | 
						|
					RxErrors: &runtime.UInt64Value{Value: rxErrors},
 | 
						|
					TxBytes:  &runtime.UInt64Value{Value: txBytes},
 | 
						|
					TxErrors: &runtime.UInt64Value{Value: txErrors},
 | 
						|
				},
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		listContainerStatsRequest := &runtime.ListContainerStatsRequest{Filter: &runtime.ContainerStatsFilter{PodSandboxId: meta.ID}}
 | 
						|
		css, err := c.listContainerStats(ctx, listContainerStatsRequest)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to obtain container stats during podSandboxStats call: %w", err)
 | 
						|
		}
 | 
						|
		var pidCount uint64
 | 
						|
		for _, cs := range css {
 | 
						|
			pidCount += cs.pids
 | 
						|
			podSandboxStats.Linux.Containers = append(podSandboxStats.Linux.Containers, cs.stats)
 | 
						|
		}
 | 
						|
		podSandboxStats.Linux.Process = &runtime.ProcessUsage{
 | 
						|
			Timestamp:    timestamp.UnixNano(),
 | 
						|
			ProcessCount: &runtime.UInt64Value{Value: pidCount},
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return podSandboxStats, nil
 | 
						|
}
 | 
						|
 | 
						|
// https://github.com/cri-o/cri-o/blob/74a5cf8dffd305b311eb1c7f43a4781738c388c1/internal/oci/stats.go#L32
 | 
						|
func getContainerNetIO(ctx context.Context, netNsPath string) (rxBytes, rxErrors, txBytes, txErrors uint64) {
 | 
						|
	ns.WithNetNSPath(netNsPath, func(_ ns.NetNS) error {
 | 
						|
		link, err := netlink.LinkByName(defaultIfName)
 | 
						|
		if err != nil {
 | 
						|
			log.G(ctx).WithError(err).Errorf("unable to retrieve network namespace stats for netNsPath: %v, interface: %v", netNsPath, defaultIfName)
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		attrs := link.Attrs()
 | 
						|
		if attrs != nil && attrs.Statistics != nil {
 | 
						|
			rxBytes = attrs.Statistics.RxBytes
 | 
						|
			rxErrors = attrs.Statistics.RxErrors
 | 
						|
			txBytes = attrs.Statistics.TxBytes
 | 
						|
			txErrors = attrs.Statistics.TxErrors
 | 
						|
		}
 | 
						|
		return nil
 | 
						|
	})
 | 
						|
 | 
						|
	return rxBytes, rxErrors, txBytes, txErrors
 | 
						|
}
 | 
						|
 | 
						|
func metricsForSandbox(sandbox sandboxstore.Sandbox) (interface{}, error) {
 | 
						|
	cgroupPath := sandbox.Config.GetLinux().GetCgroupParent()
 | 
						|
 | 
						|
	if cgroupPath == "" {
 | 
						|
		return nil, fmt.Errorf("failed to get cgroup metrics for sandbox %v because cgroupPath is empty", sandbox.ID)
 | 
						|
	}
 | 
						|
 | 
						|
	var statsx interface{}
 | 
						|
	if cgroups.Mode() == cgroups.Unified {
 | 
						|
		cg, err := cgroupsv2.Load(cgroupPath)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to load sandbox cgroup: %v: %w", cgroupPath, err)
 | 
						|
		}
 | 
						|
		stats, err := cg.Stat()
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to get stats for cgroup: %v: %w", cgroupPath, err)
 | 
						|
		}
 | 
						|
		statsx = stats
 | 
						|
 | 
						|
	} else {
 | 
						|
		control, err := cgroup1.Load(cgroup1.StaticPath(cgroupPath))
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to load sandbox cgroup %v: %w", cgroupPath, err)
 | 
						|
		}
 | 
						|
		stats, err := control.Stat(cgroup1.IgnoreNotExist)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("failed to get stats for cgroup %v: %w", cgroupPath, err)
 | 
						|
		}
 | 
						|
		statsx = stats
 | 
						|
	}
 | 
						|
 | 
						|
	return statsx, nil
 | 
						|
}
 |