...

Source file src/k8s.io/apiextensions-apiserver/pkg/apiserver/conversion/metrics.go

Documentation: k8s.io/apiextensions-apiserver/pkg/apiserver/conversion

     1  /*
     2  Copyright 2019 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 conversion
    18  
    19  import (
    20  	"context"
    21  	"strconv"
    22  	"sync"
    23  	"time"
    24  
    25  	"k8s.io/apimachinery/pkg/runtime"
    26  	"k8s.io/apimachinery/pkg/runtime/schema"
    27  	"k8s.io/component-base/metrics"
    28  	"k8s.io/component-base/metrics/legacyregistry"
    29  )
    30  
    31  var (
    32  	latencyBuckets = metrics.ExponentialBuckets(0.001, 2, 15)
    33  )
    34  
    35  // converterMetricFactory holds metrics for all CRD converters
    36  type converterMetricFactory struct {
    37  	// A map from a converter name to it's metric. Allows the converterMetric to be created
    38  	// again with the same metric for a specific converter (e.g. 'webhook').
    39  	durations   map[string]*metrics.HistogramVec
    40  	factoryLock sync.Mutex
    41  }
    42  
    43  func newConverterMetricFactory() *converterMetricFactory {
    44  	return &converterMetricFactory{durations: map[string]*metrics.HistogramVec{}, factoryLock: sync.Mutex{}}
    45  }
    46  
    47  var _ crConverterInterface = &converterMetric{}
    48  
    49  type converterMetric struct {
    50  	delegate  crConverterInterface
    51  	latencies *metrics.HistogramVec
    52  	crdName   string
    53  }
    54  
    55  func (c *converterMetricFactory) addMetrics(crdName string, converter crConverterInterface) (crConverterInterface, error) {
    56  	c.factoryLock.Lock()
    57  	defer c.factoryLock.Unlock()
    58  	metric, exists := c.durations["webhook"]
    59  	if !exists {
    60  		metric = metrics.NewHistogramVec(
    61  			&metrics.HistogramOpts{
    62  				Name:           "apiserver_crd_conversion_webhook_duration_seconds",
    63  				Help:           "CRD webhook conversion duration in seconds",
    64  				Buckets:        latencyBuckets,
    65  				StabilityLevel: metrics.ALPHA,
    66  			},
    67  			[]string{"crd_name", "from_version", "to_version", "succeeded"})
    68  		err := legacyregistry.Register(metric)
    69  		if err != nil {
    70  			return nil, err
    71  		}
    72  		c.durations["webhook"] = metric
    73  	}
    74  	return &converterMetric{latencies: metric, delegate: converter, crdName: crdName}, nil
    75  }
    76  
    77  func (m *converterMetric) Convert(in runtime.Object, targetGV schema.GroupVersion) (runtime.Object, error) {
    78  	start := time.Now()
    79  	obj, err := m.delegate.Convert(in, targetGV)
    80  	fromVersion := in.GetObjectKind().GroupVersionKind().Version
    81  	toVersion := targetGV.Version
    82  
    83  	// only record this observation if the version is different
    84  	if fromVersion != toVersion {
    85  		m.latencies.WithLabelValues(
    86  			m.crdName, fromVersion, toVersion, strconv.FormatBool(err == nil)).Observe(time.Since(start).Seconds())
    87  	}
    88  	return obj, err
    89  }
    90  
    91  type ConversionWebhookErrorType string
    92  
    93  const (
    94  	ConversionWebhookCallFailure                   ConversionWebhookErrorType = "conversion_webhook_call_failure"
    95  	ConversionWebhookMalformedResponseFailure      ConversionWebhookErrorType = "conversion_webhook_malformed_response_failure"
    96  	ConversionWebhookPartialResponseFailure        ConversionWebhookErrorType = "conversion_webhook_partial_response_failure"
    97  	ConversionWebhookInvalidConvertedObjectFailure ConversionWebhookErrorType = "conversion_webhook_invalid_converted_object_failure"
    98  	ConversionWebhookNoObjectsReturnedFailure      ConversionWebhookErrorType = "conversion_webhook_no_objects_returned_failure"
    99  )
   100  
   101  var (
   102  	Metrics   = newConversionWebhookMetrics()
   103  	namespace = "apiserver"
   104  )
   105  
   106  // ConversionWebhookMetrics instruments webhook conversion with prometheus metrics.
   107  type ConversionWebhookMetrics struct {
   108  	conversionWebhookRequest *metrics.CounterVec
   109  	conversionWebhookLatency *metrics.HistogramVec
   110  }
   111  
   112  func newConversionWebhookMetrics() *ConversionWebhookMetrics {
   113  	conversionWebhookRequest := metrics.NewCounterVec(
   114  		&metrics.CounterOpts{
   115  			Name:           "conversion_webhook_request_total",
   116  			Namespace:      namespace,
   117  			Help:           "Counter for conversion webhook requests with success/failure and failure error type",
   118  			StabilityLevel: metrics.ALPHA,
   119  		},
   120  		[]string{"result", "failure_type"})
   121  
   122  	conversionWebhookLatency := metrics.NewHistogramVec(
   123  		&metrics.HistogramOpts{
   124  			Name:      "conversion_webhook_duration_seconds",
   125  			Namespace: namespace,
   126  			Help:      "Conversion webhook request latency",
   127  			// Various buckets from 5 ms to 60 seconds
   128  			Buckets:        []float64{0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 30, 45, 60},
   129  			StabilityLevel: metrics.ALPHA,
   130  		},
   131  		[]string{"result", "failure_type"},
   132  	)
   133  
   134  	legacyregistry.MustRegister(conversionWebhookRequest)
   135  	legacyregistry.MustRegister(conversionWebhookLatency)
   136  
   137  	return &ConversionWebhookMetrics{conversionWebhookRequest: conversionWebhookRequest, conversionWebhookLatency: conversionWebhookLatency}
   138  }
   139  
   140  // Observe successful request
   141  func (m *ConversionWebhookMetrics) ObserveConversionWebhookSuccess(ctx context.Context, elapsed time.Duration) {
   142  	result := "success"
   143  	m.conversionWebhookRequest.WithContext(ctx).WithLabelValues(result, "").Inc()
   144  	m.observe(ctx, elapsed, result, "")
   145  }
   146  
   147  // Observe failure with failure type
   148  func (m *ConversionWebhookMetrics) ObserveConversionWebhookFailure(ctx context.Context, elapsed time.Duration, errorType ConversionWebhookErrorType) {
   149  	result := "failure"
   150  	m.conversionWebhookRequest.WithContext(ctx).WithLabelValues(result, string(errorType)).Inc()
   151  	m.observe(ctx, elapsed, result, errorType)
   152  }
   153  
   154  // Observe latency
   155  func (m *ConversionWebhookMetrics) observe(ctx context.Context, elapsed time.Duration, result string, errorType ConversionWebhookErrorType) {
   156  	elapsedSeconds := elapsed.Seconds()
   157  	m.conversionWebhookLatency.WithContext(ctx).WithLabelValues(result, string(errorType)).Observe(elapsedSeconds)
   158  }
   159  

View as plain text