...

Source file src/k8s.io/kubernetes/pkg/kubelet/metrics/collectors/cri_metrics.go

Documentation: k8s.io/kubernetes/pkg/kubelet/metrics/collectors

     1  /*
     2  Copyright 2022 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 collectors
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"time"
    23  
    24  	"k8s.io/component-base/metrics"
    25  	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
    26  	"k8s.io/klog/v2"
    27  )
    28  
    29  type criMetricsCollector struct {
    30  	metrics.BaseStableCollector
    31  	// The descriptors structure will be populated by one call to ListMetricDescriptors from the runtime.
    32  	// They will be saved in this map, where the key is the Name and the value is the Desc.
    33  	descriptors             map[string]*metrics.Desc
    34  	listPodSandboxMetricsFn func(context.Context) ([]*runtimeapi.PodSandboxMetrics, error)
    35  }
    36  
    37  // Check if criMetricsCollector implements necessary interface
    38  var _ metrics.StableCollector = &criMetricsCollector{}
    39  
    40  // NewCRIMetricsCollector implements the metrics.Collector interface
    41  func NewCRIMetricsCollector(ctx context.Context, listPodSandboxMetricsFn func(context.Context) ([]*runtimeapi.PodSandboxMetrics, error), listMetricDescriptorsFn func(context.Context) ([]*runtimeapi.MetricDescriptor, error)) metrics.StableCollector {
    42  	descs, err := listMetricDescriptorsFn(ctx)
    43  	if err != nil {
    44  		klog.ErrorS(err, "Error reading MetricDescriptors")
    45  		return &criMetricsCollector{
    46  			listPodSandboxMetricsFn: listPodSandboxMetricsFn,
    47  		}
    48  	}
    49  	c := &criMetricsCollector{
    50  		listPodSandboxMetricsFn: listPodSandboxMetricsFn,
    51  		descriptors:             make(map[string]*metrics.Desc, len(descs)),
    52  	}
    53  
    54  	for _, desc := range descs {
    55  		c.descriptors[desc.Name] = criDescToProm(desc)
    56  	}
    57  
    58  	return c
    59  }
    60  
    61  // Describe implements the metrics.DescribeWithStability interface.
    62  func (c *criMetricsCollector) DescribeWithStability(ch chan<- *metrics.Desc) {
    63  	for _, desc := range c.descriptors {
    64  		ch <- desc
    65  	}
    66  }
    67  
    68  // Collect implements the metrics.CollectWithStability interface.
    69  // TODO(haircommander): would it be better if these were processed async?
    70  func (c *criMetricsCollector) CollectWithStability(ch chan<- metrics.Metric) {
    71  	podMetrics, err := c.listPodSandboxMetricsFn(context.Background())
    72  	if err != nil {
    73  		klog.ErrorS(err, "Failed to get pod metrics")
    74  		return
    75  	}
    76  
    77  	for _, podMetric := range podMetrics {
    78  		for _, metric := range podMetric.GetMetrics() {
    79  			promMetric, err := c.criMetricToProm(metric)
    80  			if err == nil {
    81  				ch <- promMetric
    82  			}
    83  		}
    84  		for _, ctrMetric := range podMetric.GetContainerMetrics() {
    85  			for _, metric := range ctrMetric.GetMetrics() {
    86  				promMetric, err := c.criMetricToProm(metric)
    87  				if err == nil {
    88  					ch <- promMetric
    89  				}
    90  			}
    91  		}
    92  	}
    93  }
    94  
    95  func criDescToProm(m *runtimeapi.MetricDescriptor) *metrics.Desc {
    96  	// Labels in the translation are variableLabels, as opposed to constant labels.
    97  	// This is because the values of the labels will be different for each container.
    98  	return metrics.NewDesc(m.Name, m.Help, m.LabelKeys, nil, metrics.INTERNAL, "")
    99  }
   100  
   101  func (c *criMetricsCollector) criMetricToProm(m *runtimeapi.Metric) (metrics.Metric, error) {
   102  	desc, ok := c.descriptors[m.Name]
   103  	if !ok {
   104  		err := fmt.Errorf("error converting CRI Metric to prometheus format")
   105  		klog.V(5).ErrorS(err, "Descriptor not present in pre-populated list of descriptors", "name", m.Name)
   106  		return nil, err
   107  	}
   108  
   109  	typ := criTypeToProm[m.MetricType]
   110  
   111  	pm, err := metrics.NewConstMetric(desc, typ, float64(m.GetValue().Value), m.LabelValues...)
   112  	if err != nil {
   113  		klog.ErrorS(err, "Error getting CRI prometheus metric", "descriptor", desc.String())
   114  		return nil, err
   115  	}
   116  	// If Timestamp is 0, then the runtime did not cache the result.
   117  	// In this case, a cached result is a metric that was collected ahead of time,
   118  	// as opposed to on-demand.
   119  	// If the metric was requested as needed, then Timestamp==0.
   120  	if m.Timestamp == 0 {
   121  		return pm, nil
   122  	}
   123  	return metrics.NewLazyMetricWithTimestamp(time.Unix(0, m.Timestamp), pm), nil
   124  }
   125  
   126  var criTypeToProm = map[runtimeapi.MetricType]metrics.ValueType{
   127  	runtimeapi.MetricType_COUNTER: metrics.CounterValue,
   128  	runtimeapi.MetricType_GAUGE:   metrics.GaugeValue,
   129  }
   130  

View as plain text