2019-01-12 04:58:27 +00:00
|
|
|
/*
|
|
|
|
Copyright 2017 The Kubernetes 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 metrics
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
|
|
|
|
"k8s.io/apiserver/pkg/admission"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
namespace = "apiserver"
|
|
|
|
subsystem = "admission"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// Use buckets ranging from 25 ms to ~2.5 seconds.
|
|
|
|
latencyBuckets = prometheus.ExponentialBuckets(25000, 2.5, 5)
|
|
|
|
latencySummaryMaxAge = 5 * time.Hour
|
|
|
|
|
|
|
|
// Metrics provides access to all admission metrics.
|
|
|
|
Metrics = newAdmissionMetrics()
|
|
|
|
)
|
|
|
|
|
|
|
|
// ObserverFunc is a func that emits metrics.
|
|
|
|
type ObserverFunc func(elapsed time.Duration, rejected bool, attr admission.Attributes, stepType string, extraLabels ...string)
|
|
|
|
|
|
|
|
const (
|
|
|
|
stepValidate = "validate"
|
|
|
|
stepAdmit = "admit"
|
|
|
|
)
|
|
|
|
|
|
|
|
// WithControllerMetrics is a decorator for named admission handlers.
|
|
|
|
func WithControllerMetrics(i admission.Interface, name string) admission.Interface {
|
|
|
|
return WithMetrics(i, Metrics.ObserveAdmissionController, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithStepMetrics is a decorator for a whole admission phase, i.e. admit or validation.admission step.
|
|
|
|
func WithStepMetrics(i admission.Interface) admission.Interface {
|
|
|
|
return WithMetrics(i, Metrics.ObserveAdmissionStep)
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithMetrics is a decorator for admission handlers with a generic observer func.
|
|
|
|
func WithMetrics(i admission.Interface, observer ObserverFunc, extraLabels ...string) admission.Interface {
|
|
|
|
return &pluginHandlerWithMetrics{
|
|
|
|
Interface: i,
|
|
|
|
observer: observer,
|
|
|
|
extraLabels: extraLabels,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// pluginHandlerWithMetrics decorates a admission handler with metrics.
|
|
|
|
type pluginHandlerWithMetrics struct {
|
|
|
|
admission.Interface
|
|
|
|
observer ObserverFunc
|
|
|
|
extraLabels []string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Admit performs a mutating admission control check and emit metrics.
|
2019-04-07 17:07:55 +00:00
|
|
|
func (p pluginHandlerWithMetrics) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
|
2019-01-12 04:58:27 +00:00
|
|
|
mutatingHandler, ok := p.Interface.(admission.MutationInterface)
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
start := time.Now()
|
2019-04-07 17:07:55 +00:00
|
|
|
err := mutatingHandler.Admit(a, o)
|
2019-01-12 04:58:27 +00:00
|
|
|
p.observer(time.Since(start), err != nil, a, stepAdmit, p.extraLabels...)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate performs a non-mutating admission control check and emits metrics.
|
2019-04-07 17:07:55 +00:00
|
|
|
func (p pluginHandlerWithMetrics) Validate(a admission.Attributes, o admission.ObjectInterfaces) error {
|
2019-01-12 04:58:27 +00:00
|
|
|
validatingHandler, ok := p.Interface.(admission.ValidationInterface)
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
start := time.Now()
|
2019-04-07 17:07:55 +00:00
|
|
|
err := validatingHandler.Validate(a, o)
|
2019-01-12 04:58:27 +00:00
|
|
|
p.observer(time.Since(start), err != nil, a, stepValidate, p.extraLabels...)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// AdmissionMetrics instruments admission with prometheus metrics.
|
|
|
|
type AdmissionMetrics struct {
|
|
|
|
step *metricSet
|
|
|
|
controller *metricSet
|
|
|
|
webhook *metricSet
|
|
|
|
}
|
|
|
|
|
|
|
|
// newAdmissionMetrics create a new AdmissionMetrics, configured with default metric names.
|
|
|
|
func newAdmissionMetrics() *AdmissionMetrics {
|
|
|
|
// Admission metrics for a step of the admission flow. The entire admission flow is broken down into a series of steps
|
|
|
|
// Each step is identified by a distinct type label value.
|
|
|
|
step := newMetricSet("step",
|
|
|
|
[]string{"type", "operation", "rejected"},
|
|
|
|
"Admission sub-step %s, broken out for each operation and API resource and step type (validate or admit).", true)
|
|
|
|
|
|
|
|
// Built-in admission controller metrics. Each admission controller is identified by name.
|
|
|
|
controller := newMetricSet("controller",
|
|
|
|
[]string{"name", "type", "operation", "rejected"},
|
|
|
|
"Admission controller %s, identified by name and broken out for each operation and API resource and type (validate or admit).", false)
|
|
|
|
|
|
|
|
// Admission webhook metrics. Each webhook is identified by name.
|
|
|
|
webhook := newMetricSet("webhook",
|
|
|
|
[]string{"name", "type", "operation", "rejected"},
|
|
|
|
"Admission webhook %s, identified by name and broken out for each operation and API resource and type (validate or admit).", false)
|
|
|
|
|
|
|
|
step.mustRegister()
|
|
|
|
controller.mustRegister()
|
|
|
|
webhook.mustRegister()
|
|
|
|
return &AdmissionMetrics{step: step, controller: controller, webhook: webhook}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *AdmissionMetrics) reset() {
|
|
|
|
m.step.reset()
|
|
|
|
m.controller.reset()
|
|
|
|
m.webhook.reset()
|
|
|
|
}
|
|
|
|
|
|
|
|
// ObserveAdmissionStep records admission related metrics for a admission step, identified by step type.
|
|
|
|
func (m *AdmissionMetrics) ObserveAdmissionStep(elapsed time.Duration, rejected bool, attr admission.Attributes, stepType string, extraLabels ...string) {
|
|
|
|
m.step.observe(elapsed, append(extraLabels, stepType, string(attr.GetOperation()), strconv.FormatBool(rejected))...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ObserveAdmissionController records admission related metrics for a built-in admission controller, identified by it's plugin handler name.
|
|
|
|
func (m *AdmissionMetrics) ObserveAdmissionController(elapsed time.Duration, rejected bool, attr admission.Attributes, stepType string, extraLabels ...string) {
|
|
|
|
m.controller.observe(elapsed, append(extraLabels, stepType, string(attr.GetOperation()), strconv.FormatBool(rejected))...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ObserveWebhook records admission related metrics for a admission webhook.
|
|
|
|
func (m *AdmissionMetrics) ObserveWebhook(elapsed time.Duration, rejected bool, attr admission.Attributes, stepType string, extraLabels ...string) {
|
|
|
|
m.webhook.observe(elapsed, append(extraLabels, stepType, string(attr.GetOperation()), strconv.FormatBool(rejected))...)
|
|
|
|
}
|
|
|
|
|
|
|
|
type metricSet struct {
|
2019-04-07 17:07:55 +00:00
|
|
|
latencies *prometheus.HistogramVec
|
|
|
|
deprecatedLatencies *prometheus.HistogramVec
|
|
|
|
latenciesSummary *prometheus.SummaryVec
|
|
|
|
deprecatedLatenciesSummary *prometheus.SummaryVec
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func newMetricSet(name string, labels []string, helpTemplate string, hasSummary bool) *metricSet {
|
2019-04-07 17:07:55 +00:00
|
|
|
var summary, deprecatedSummary *prometheus.SummaryVec
|
2019-01-12 04:58:27 +00:00
|
|
|
if hasSummary {
|
|
|
|
summary = prometheus.NewSummaryVec(
|
|
|
|
prometheus.SummaryOpts{
|
|
|
|
Namespace: namespace,
|
|
|
|
Subsystem: subsystem,
|
2019-04-07 17:07:55 +00:00
|
|
|
Name: fmt.Sprintf("%s_admission_duration_seconds_summary", name),
|
|
|
|
Help: fmt.Sprintf(helpTemplate, "latency summary in seconds"),
|
|
|
|
MaxAge: latencySummaryMaxAge,
|
|
|
|
},
|
|
|
|
labels,
|
|
|
|
)
|
|
|
|
deprecatedSummary = prometheus.NewSummaryVec(
|
|
|
|
prometheus.SummaryOpts{
|
|
|
|
Namespace: namespace,
|
|
|
|
Subsystem: subsystem,
|
|
|
|
Name: fmt.Sprintf("%s_admission_latencies_milliseconds_summary", name),
|
|
|
|
Help: fmt.Sprintf("(Deprecated) "+helpTemplate, "latency summary in milliseconds"),
|
2019-01-12 04:58:27 +00:00
|
|
|
MaxAge: latencySummaryMaxAge,
|
|
|
|
},
|
|
|
|
labels,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &metricSet{
|
|
|
|
latencies: prometheus.NewHistogramVec(
|
|
|
|
prometheus.HistogramOpts{
|
|
|
|
Namespace: namespace,
|
|
|
|
Subsystem: subsystem,
|
2019-04-07 17:07:55 +00:00
|
|
|
Name: fmt.Sprintf("%s_admission_duration_seconds", name),
|
|
|
|
Help: fmt.Sprintf(helpTemplate, "latency histogram in seconds"),
|
|
|
|
Buckets: latencyBuckets,
|
|
|
|
},
|
|
|
|
labels,
|
|
|
|
),
|
|
|
|
deprecatedLatencies: prometheus.NewHistogramVec(
|
|
|
|
prometheus.HistogramOpts{
|
|
|
|
Namespace: namespace,
|
|
|
|
Subsystem: subsystem,
|
|
|
|
Name: fmt.Sprintf("%s_admission_latencies_milliseconds", name),
|
|
|
|
Help: fmt.Sprintf("(Deprecated) "+helpTemplate, "latency histogram in milliseconds"),
|
2019-01-12 04:58:27 +00:00
|
|
|
Buckets: latencyBuckets,
|
|
|
|
},
|
|
|
|
labels,
|
|
|
|
),
|
|
|
|
|
2019-04-07 17:07:55 +00:00
|
|
|
latenciesSummary: summary,
|
|
|
|
deprecatedLatenciesSummary: deprecatedSummary,
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MustRegister registers all the prometheus metrics in the metricSet.
|
|
|
|
func (m *metricSet) mustRegister() {
|
|
|
|
prometheus.MustRegister(m.latencies)
|
2019-04-07 17:07:55 +00:00
|
|
|
prometheus.MustRegister(m.deprecatedLatencies)
|
2019-01-12 04:58:27 +00:00
|
|
|
if m.latenciesSummary != nil {
|
|
|
|
prometheus.MustRegister(m.latenciesSummary)
|
|
|
|
}
|
2019-04-07 17:07:55 +00:00
|
|
|
if m.deprecatedLatenciesSummary != nil {
|
|
|
|
prometheus.MustRegister(m.deprecatedLatenciesSummary)
|
|
|
|
}
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Reset resets all the prometheus metrics in the metricSet.
|
|
|
|
func (m *metricSet) reset() {
|
|
|
|
m.latencies.Reset()
|
2019-04-07 17:07:55 +00:00
|
|
|
m.deprecatedLatencies.Reset()
|
2019-01-12 04:58:27 +00:00
|
|
|
if m.latenciesSummary != nil {
|
|
|
|
m.latenciesSummary.Reset()
|
|
|
|
}
|
2019-04-07 17:07:55 +00:00
|
|
|
if m.deprecatedLatenciesSummary != nil {
|
|
|
|
m.deprecatedLatenciesSummary.Reset()
|
|
|
|
}
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Observe records an observed admission event to all metrics in the metricSet.
|
|
|
|
func (m *metricSet) observe(elapsed time.Duration, labels ...string) {
|
2019-04-07 17:07:55 +00:00
|
|
|
elapsedSeconds := elapsed.Seconds()
|
2019-01-12 04:58:27 +00:00
|
|
|
elapsedMicroseconds := float64(elapsed / time.Microsecond)
|
2019-04-07 17:07:55 +00:00
|
|
|
m.latencies.WithLabelValues(labels...).Observe(elapsedSeconds)
|
|
|
|
m.deprecatedLatencies.WithLabelValues(labels...).Observe(elapsedMicroseconds)
|
2019-01-12 04:58:27 +00:00
|
|
|
if m.latenciesSummary != nil {
|
2019-04-07 17:07:55 +00:00
|
|
|
m.latenciesSummary.WithLabelValues(labels...).Observe(elapsedSeconds)
|
|
|
|
}
|
|
|
|
if m.deprecatedLatenciesSummary != nil {
|
|
|
|
m.deprecatedLatenciesSummary.WithLabelValues(labels...).Observe(elapsedMicroseconds)
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
}
|