mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-10 12:32:03 +00:00
apiserver: add component label to request metrics
This commit is contained in:
parent
ff6e028755
commit
7190b17e5a
@ -248,7 +248,7 @@ func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
if handler != nil {
|
||||
handler = metrics.InstrumentHandlerFunc(verb, requestInfo.APIGroup, requestInfo.APIVersion, resource, subresource, scope, handler)
|
||||
handler = metrics.InstrumentHandlerFunc(verb, requestInfo.APIGroup, requestInfo.APIVersion, resource, subresource, scope, metrics.APIServerComponent, handler)
|
||||
handler(w, req)
|
||||
return
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch
|
||||
return
|
||||
}
|
||||
requestInfo, _ := request.RequestInfoFrom(ctx)
|
||||
metrics.RecordLongRunning(req, requestInfo, func() {
|
||||
metrics.RecordLongRunning(req, requestInfo, metrics.APIServerComponent, func() {
|
||||
serveWatch(watcher, scope, req, w, timeout)
|
||||
})
|
||||
return
|
||||
|
@ -64,7 +64,7 @@ func WriteObject(statusCode int, gv schema.GroupVersion, s runtime.NegotiatedSer
|
||||
stream, ok := object.(rest.ResourceStreamer)
|
||||
if ok {
|
||||
requestInfo, _ := request.RequestInfoFrom(req.Context())
|
||||
metrics.RecordLongRunning(req, requestInfo, func() {
|
||||
metrics.RecordLongRunning(req, requestInfo, metrics.APIServerComponent, func() {
|
||||
StreamObject(statusCode, gv, s, stream, w, req)
|
||||
})
|
||||
return
|
||||
|
@ -144,7 +144,7 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi
|
||||
}
|
||||
}
|
||||
requestInfo, _ := request.RequestInfoFrom(ctx)
|
||||
metrics.RecordLongRunning(req, requestInfo, func() {
|
||||
metrics.RecordLongRunning(req, requestInfo, metrics.APIServerComponent, func() {
|
||||
handler, err := connecter.Connect(ctx, name, opts, &responder{scope: scope, req: req, w: w})
|
||||
if err != nil {
|
||||
scope.err(err, w, req)
|
||||
|
@ -573,9 +573,9 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
|
||||
if needOverride {
|
||||
// need change the reported verb
|
||||
handler = metrics.InstrumentRouteFunc(verbOverrider.OverrideMetricsVerb(action.Verb), group, version, resource, subresource, requestScope, handler)
|
||||
handler = metrics.InstrumentRouteFunc(verbOverrider.OverrideMetricsVerb(action.Verb), group, version, resource, subresource, requestScope, metrics.APIServerComponent, handler)
|
||||
} else {
|
||||
handler = metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, handler)
|
||||
handler = metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, handler)
|
||||
}
|
||||
|
||||
if a.enableAPIResponseCompression {
|
||||
@ -609,7 +609,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
if isSubresource {
|
||||
doc = "list " + subresource + " of objects of kind " + kind
|
||||
}
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, restfulListResource(lister, watcher, reqScope, false, a.minRequestTimeout))
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, restfulListResource(lister, watcher, reqScope, false, a.minRequestTimeout))
|
||||
if a.enableAPIResponseCompression {
|
||||
handler = genericfilters.RestfulWithCompression(handler)
|
||||
}
|
||||
@ -644,7 +644,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
if isSubresource {
|
||||
doc = "replace " + subresource + " of the specified " + kind
|
||||
}
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, restfulUpdateResource(updater, reqScope, admit))
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, restfulUpdateResource(updater, reqScope, admit))
|
||||
route := ws.PUT(action.Path).To(handler).
|
||||
Doc(doc).
|
||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||
@ -671,7 +671,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
string(types.MergePatchType),
|
||||
string(types.StrategicMergePatchType),
|
||||
}
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, restfulPatchResource(patcher, reqScope, admit, supportedTypes))
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, restfulPatchResource(patcher, reqScope, admit, supportedTypes))
|
||||
route := ws.PATCH(action.Path).To(handler).
|
||||
Doc(doc).
|
||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||
@ -693,7 +693,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
} else {
|
||||
handler = restfulCreateResource(creater, reqScope, admit)
|
||||
}
|
||||
handler = metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, handler)
|
||||
handler = metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, handler)
|
||||
article := getArticleForNoun(kind, " ")
|
||||
doc := "create" + article + kind
|
||||
if isSubresource {
|
||||
@ -722,7 +722,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
if isSubresource {
|
||||
doc = "delete " + subresource + " of" + article + kind
|
||||
}
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, restfulDeleteResource(gracefulDeleter, isGracefulDeleter, reqScope, admit))
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, restfulDeleteResource(gracefulDeleter, isGracefulDeleter, reqScope, admit))
|
||||
route := ws.DELETE(action.Path).To(handler).
|
||||
Doc(doc).
|
||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||
@ -745,7 +745,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
if isSubresource {
|
||||
doc = "delete collection of " + subresource + " of a " + kind
|
||||
}
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, restfulDeleteCollection(collectionDeleter, isCollectionDeleter, reqScope, admit))
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, restfulDeleteCollection(collectionDeleter, isCollectionDeleter, reqScope, admit))
|
||||
route := ws.DELETE(action.Path).To(handler).
|
||||
Doc(doc).
|
||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||
@ -765,7 +765,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
doc = "watch changes to " + subresource + " of an object of kind " + kind
|
||||
}
|
||||
doc += ". deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter."
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, restfulListResource(lister, watcher, reqScope, true, a.minRequestTimeout))
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, restfulListResource(lister, watcher, reqScope, true, a.minRequestTimeout))
|
||||
route := ws.GET(action.Path).To(handler).
|
||||
Doc(doc).
|
||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||
@ -785,7 +785,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
doc = "watch individual changes to a list of " + subresource + " of " + kind
|
||||
}
|
||||
doc += ". deprecated: use the 'watch' parameter with a list operation instead."
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, restfulListResource(lister, watcher, reqScope, true, a.minRequestTimeout))
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, restfulListResource(lister, watcher, reqScope, true, a.minRequestTimeout))
|
||||
route := ws.GET(action.Path).To(handler).
|
||||
Doc(doc).
|
||||
Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")).
|
||||
@ -808,7 +808,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
if isSubresource {
|
||||
doc = "connect " + method + " requests to " + subresource + " of " + kind
|
||||
}
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, restfulConnectResource(connecter, reqScope, admit, path, isSubresource))
|
||||
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, restfulConnectResource(connecter, reqScope, admit, path, isSubresource))
|
||||
route := ws.Method(method).Path(action.Path).
|
||||
To(handler).
|
||||
Doc(doc).
|
||||
|
@ -40,50 +40,54 @@ type resettableCollector interface {
|
||||
Reset()
|
||||
}
|
||||
|
||||
const (
|
||||
APIServerComponent string = "apiserver"
|
||||
)
|
||||
|
||||
var (
|
||||
// TODO(a-robinson): Add unit tests for the handling of these metrics once
|
||||
// the upstream library supports it.
|
||||
requestCounter = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "apiserver_request_count",
|
||||
Help: "Counter of apiserver requests broken out for each verb, group, version, resource, scope, client, and HTTP response contentType and code.",
|
||||
Help: "Counter of apiserver requests broken out for each verb, group, version, resource, scope, component, client, and HTTP response contentType and code.",
|
||||
},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope", "client", "contentType", "code"},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope", "component", "client", "contentType", "code"},
|
||||
)
|
||||
longRunningRequestGauge = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "apiserver_longrunning_gauge",
|
||||
Help: "Gauge of all active long-running apiserver requests broken out by verb, group, version, resource, and scope. Not all requests are tracked this way.",
|
||||
Help: "Gauge of all active long-running apiserver requests broken out by verb, group, version, resource, scope and component. Not all requests are tracked this way.",
|
||||
},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope"},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope", "component"},
|
||||
)
|
||||
requestLatencies = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "apiserver_request_latencies",
|
||||
Help: "Response latency distribution in microseconds for each verb, group, version, resource, subresource, and scope.",
|
||||
Help: "Response latency distribution in microseconds for each verb, group, version, resource, subresource, scope and component.",
|
||||
// Use buckets ranging from 125 ms to 8 seconds.
|
||||
Buckets: prometheus.ExponentialBuckets(125000, 2.0, 7),
|
||||
},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope"},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope", "component"},
|
||||
)
|
||||
requestLatenciesSummary = prometheus.NewSummaryVec(
|
||||
prometheus.SummaryOpts{
|
||||
Name: "apiserver_request_latencies_summary",
|
||||
Help: "Response latency summary in microseconds for each verb, group, version, resource, subresource, and scope.",
|
||||
Help: "Response latency summary in microseconds for each verb, group, version, resource, subresource, scope and component.",
|
||||
// Make the sliding window of 5h.
|
||||
// TODO: The value for this should be based on our SLI definition (medium term).
|
||||
MaxAge: 5 * time.Hour,
|
||||
},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope"},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope", "component"},
|
||||
)
|
||||
responseSizes = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "apiserver_response_sizes",
|
||||
Help: "Response size distribution in bytes for each verb, resource, subresource and scope (namespace/cluster).",
|
||||
Help: "Response size distribution in bytes for each group, version, verb, resource, subresource, scope and component.",
|
||||
// Use buckets ranging from 1000 bytes (1KB) to 10^9 bytes (1GB).
|
||||
Buckets: prometheus.ExponentialBuckets(1000, 10.0, 7),
|
||||
},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope"},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope", "component"},
|
||||
)
|
||||
// DroppedRequests is a number of requests dropped with 'Try again later' response"
|
||||
DroppedRequests = prometheus.NewCounterVec(
|
||||
@ -157,21 +161,21 @@ func UpdateInflightRequestMetrics(nonmutating, mutating int) {
|
||||
// Record records a single request to the standard metrics endpoints. For use by handlers that perform their own
|
||||
// processing. All API paths should use InstrumentRouteFunc implicitly. Use this instead of MonitorRequest if
|
||||
// you already have a RequestInfo object.
|
||||
func Record(req *http.Request, requestInfo *request.RequestInfo, contentType string, code int, responseSizeInBytes int, elapsed time.Duration) {
|
||||
func Record(req *http.Request, requestInfo *request.RequestInfo, component, contentType string, code int, responseSizeInBytes int, elapsed time.Duration) {
|
||||
if requestInfo == nil {
|
||||
requestInfo = &request.RequestInfo{Verb: req.Method, Path: req.URL.Path}
|
||||
}
|
||||
scope := CleanScope(requestInfo)
|
||||
if requestInfo.IsResourceRequest {
|
||||
MonitorRequest(req, strings.ToUpper(requestInfo.Verb), requestInfo.APIGroup, requestInfo.APIVersion, requestInfo.Resource, requestInfo.Subresource, scope, contentType, code, responseSizeInBytes, elapsed)
|
||||
MonitorRequest(req, strings.ToUpper(requestInfo.Verb), requestInfo.APIGroup, requestInfo.APIVersion, requestInfo.Resource, requestInfo.Subresource, scope, component, contentType, code, responseSizeInBytes, elapsed)
|
||||
} else {
|
||||
MonitorRequest(req, strings.ToUpper(requestInfo.Verb), "", "", "", requestInfo.Path, scope, contentType, code, responseSizeInBytes, elapsed)
|
||||
MonitorRequest(req, strings.ToUpper(requestInfo.Verb), "", "", "", requestInfo.Path, scope, component, contentType, code, responseSizeInBytes, elapsed)
|
||||
}
|
||||
}
|
||||
|
||||
// RecordLongRunning tracks the execution of a long running request against the API server. It provides an accurate count
|
||||
// of the total number of open long running requests. requestInfo may be nil if the caller is not in the normal request flow.
|
||||
func RecordLongRunning(req *http.Request, requestInfo *request.RequestInfo, fn func()) {
|
||||
func RecordLongRunning(req *http.Request, requestInfo *request.RequestInfo, component string, fn func()) {
|
||||
if requestInfo == nil {
|
||||
requestInfo = &request.RequestInfo{Verb: req.Method, Path: req.URL.Path}
|
||||
}
|
||||
@ -179,9 +183,9 @@ func RecordLongRunning(req *http.Request, requestInfo *request.RequestInfo, fn f
|
||||
scope := CleanScope(requestInfo)
|
||||
reportedVerb := cleanVerb(strings.ToUpper(requestInfo.Verb), req)
|
||||
if requestInfo.IsResourceRequest {
|
||||
g = longRunningRequestGauge.WithLabelValues(reportedVerb, requestInfo.APIGroup, requestInfo.APIVersion, requestInfo.Resource, requestInfo.Subresource, scope)
|
||||
g = longRunningRequestGauge.WithLabelValues(reportedVerb, requestInfo.APIGroup, requestInfo.APIVersion, requestInfo.Resource, requestInfo.Subresource, scope, component)
|
||||
} else {
|
||||
g = longRunningRequestGauge.WithLabelValues(reportedVerb, "", "", "", requestInfo.Path, scope)
|
||||
g = longRunningRequestGauge.WithLabelValues(reportedVerb, "", "", "", requestInfo.Path, scope, component)
|
||||
}
|
||||
g.Inc()
|
||||
defer g.Dec()
|
||||
@ -190,22 +194,22 @@ func RecordLongRunning(req *http.Request, requestInfo *request.RequestInfo, fn f
|
||||
|
||||
// MonitorRequest handles standard transformations for client and the reported verb and then invokes Monitor to record
|
||||
// a request. verb must be uppercase to be backwards compatible with existing monitoring tooling.
|
||||
func MonitorRequest(req *http.Request, verb, group, version, resource, subresource, scope, contentType string, httpCode, respSize int, elapsed time.Duration) {
|
||||
func MonitorRequest(req *http.Request, verb, group, version, resource, subresource, scope, component, contentType string, httpCode, respSize int, elapsed time.Duration) {
|
||||
reportedVerb := cleanVerb(verb, req)
|
||||
client := cleanUserAgent(utilnet.GetHTTPClient(req))
|
||||
elapsedMicroseconds := float64(elapsed / time.Microsecond)
|
||||
requestCounter.WithLabelValues(reportedVerb, group, version, resource, subresource, scope, client, contentType, codeToString(httpCode)).Inc()
|
||||
requestLatencies.WithLabelValues(reportedVerb, group, version, resource, subresource, scope).Observe(elapsedMicroseconds)
|
||||
requestLatenciesSummary.WithLabelValues(reportedVerb, group, version, resource, subresource, scope).Observe(elapsedMicroseconds)
|
||||
requestCounter.WithLabelValues(reportedVerb, group, version, resource, subresource, scope, component, client, contentType, codeToString(httpCode)).Inc()
|
||||
requestLatencies.WithLabelValues(reportedVerb, group, version, resource, subresource, scope, component).Observe(elapsedMicroseconds)
|
||||
requestLatenciesSummary.WithLabelValues(reportedVerb, group, version, resource, subresource, scope, component).Observe(elapsedMicroseconds)
|
||||
// We are only interested in response sizes of read requests.
|
||||
if verb == "GET" || verb == "LIST" {
|
||||
responseSizes.WithLabelValues(reportedVerb, group, version, resource, subresource, scope).Observe(float64(respSize))
|
||||
responseSizes.WithLabelValues(reportedVerb, group, version, resource, subresource, scope, component).Observe(float64(respSize))
|
||||
}
|
||||
}
|
||||
|
||||
// InstrumentRouteFunc works like Prometheus' InstrumentHandlerFunc but wraps
|
||||
// the go-restful RouteFunction instead of a HandlerFunc plus some Kubernetes endpoint specific information.
|
||||
func InstrumentRouteFunc(verb, group, version, resource, subresource, scope string, routeFunc restful.RouteFunction) restful.RouteFunction {
|
||||
func InstrumentRouteFunc(verb, group, version, resource, subresource, scope, component string, routeFunc restful.RouteFunction) restful.RouteFunction {
|
||||
return restful.RouteFunction(func(request *restful.Request, response *restful.Response) {
|
||||
now := time.Now()
|
||||
|
||||
@ -224,12 +228,12 @@ func InstrumentRouteFunc(verb, group, version, resource, subresource, scope stri
|
||||
|
||||
routeFunc(request, response)
|
||||
|
||||
MonitorRequest(request.Request, verb, group, version, resource, subresource, scope, delegate.Header().Get("Content-Type"), delegate.Status(), delegate.ContentLength(), time.Since(now))
|
||||
MonitorRequest(request.Request, verb, group, version, resource, subresource, scope, component, delegate.Header().Get("Content-Type"), delegate.Status(), delegate.ContentLength(), time.Since(now))
|
||||
})
|
||||
}
|
||||
|
||||
// InstrumentHandlerFunc works like Prometheus' InstrumentHandlerFunc but adds some Kubernetes endpoint specific information.
|
||||
func InstrumentHandlerFunc(verb, group, version, resource, subresource, scope string, handler http.HandlerFunc) http.HandlerFunc {
|
||||
func InstrumentHandlerFunc(verb, group, version, resource, subresource, scope, component string, handler http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
now := time.Now()
|
||||
|
||||
@ -246,7 +250,7 @@ func InstrumentHandlerFunc(verb, group, version, resource, subresource, scope st
|
||||
|
||||
handler(w, req)
|
||||
|
||||
MonitorRequest(req, verb, group, version, resource, subresource, scope, delegate.Header().Get("Content-Type"), delegate.Status(), delegate.ContentLength(), time.Since(now))
|
||||
MonitorRequest(req, verb, group, version, resource, subresource, scope, component, delegate.Header().Get("Content-Type"), delegate.Status(), delegate.ContentLength(), time.Since(now))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,7 @@ func WithMaxInFlightLimit(
|
||||
}
|
||||
}
|
||||
}
|
||||
metrics.Record(r, requestInfo, "", http.StatusTooManyRequests, 0, 0)
|
||||
metrics.Record(r, requestInfo, metrics.APIServerComponent, "", http.StatusTooManyRequests, 0, 0)
|
||||
tooManyRequests(r, w)
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ func WithTimeoutForNonLongRunningRequests(handler http.Handler, longRunning apir
|
||||
|
||||
postTimeoutFn := func() {
|
||||
cancel()
|
||||
metrics.Record(req, requestInfo, "", http.StatusGatewayTimeout, 0, 0)
|
||||
metrics.Record(req, requestInfo, metrics.APIServerComponent, "", http.StatusGatewayTimeout, 0, 0)
|
||||
}
|
||||
return req, time.After(timeout), postTimeoutFn, apierrors.NewTimeoutError(fmt.Sprintf("request did not complete within %s", timeout), 0)
|
||||
}
|
||||
|
@ -38,6 +38,8 @@ import (
|
||||
apiregistrationapi "k8s.io/kube-aggregator/pkg/apis/apiregistration"
|
||||
)
|
||||
|
||||
const AggregatorComponent metrics.Component = "aggregator"
|
||||
|
||||
// proxyHandler provides a http.Handler which will proxy traffic to locations
|
||||
// specified by items implementing Redirector.
|
||||
type proxyHandler struct {
|
||||
|
Loading…
Reference in New Issue
Block a user