k8s.io/kubernetes@v1.29.3/pkg/kubelet/metrics/collectors/cri_metrics.go (about) 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 }