mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +00:00
aggregator: add metrics for request errors
This commit is contained in:
parent
7190b17e5a
commit
fce6eb0903
@ -55,6 +55,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/endpoints/metrics:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/server:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/server:go_default_library",
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||||
"k8s.io/apimachinery/pkg/util/proxy"
|
"k8s.io/apimachinery/pkg/util/proxy"
|
||||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||||
|
endpointmetrics "k8s.io/apiserver/pkg/endpoints/metrics"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
genericfeatures "k8s.io/apiserver/pkg/features"
|
genericfeatures "k8s.io/apiserver/pkg/features"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
@ -38,7 +39,7 @@ import (
|
|||||||
apiregistrationapi "k8s.io/kube-aggregator/pkg/apis/apiregistration"
|
apiregistrationapi "k8s.io/kube-aggregator/pkg/apis/apiregistration"
|
||||||
)
|
)
|
||||||
|
|
||||||
const AggregatorComponent metrics.Component = "aggregator"
|
const aggregatorComponent string = "aggregator"
|
||||||
|
|
||||||
// proxyHandler provides a http.Handler which will proxy traffic to locations
|
// proxyHandler provides a http.Handler which will proxy traffic to locations
|
||||||
// specified by items implementing Redirector.
|
// specified by items implementing Redirector.
|
||||||
@ -62,6 +63,8 @@ type proxyHandlingInfo struct {
|
|||||||
// local indicates that this APIService is locally satisfied
|
// local indicates that this APIService is locally satisfied
|
||||||
local bool
|
local bool
|
||||||
|
|
||||||
|
// name is the name of the APIService
|
||||||
|
name string
|
||||||
// restConfig holds the information for building a roundtripper
|
// restConfig holds the information for building a roundtripper
|
||||||
restConfig *restclient.Config
|
restConfig *restclient.Config
|
||||||
// transportBuildingError is an error produced while building the transport. If this
|
// transportBuildingError is an error produced while building the transport. If this
|
||||||
@ -77,6 +80,19 @@ type proxyHandlingInfo struct {
|
|||||||
serviceAvailable bool
|
serviceAvailable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func proxyError(w http.ResponseWriter, req *http.Request, error string, code int) {
|
||||||
|
http.Error(w, error, code)
|
||||||
|
|
||||||
|
ctx := req.Context()
|
||||||
|
info, ok := genericapirequest.RequestInfoFrom(ctx)
|
||||||
|
if !ok {
|
||||||
|
klog.Warning("no RequestInfo found in the context")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: record long-running request differently? The long-running check func does not necessarily match the one of the aggregated apiserver
|
||||||
|
endpointmetrics.Record(req, info, aggregatorComponent, "", code, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
value := r.handlingInfo.Load()
|
value := r.handlingInfo.Load()
|
||||||
if value == nil {
|
if value == nil {
|
||||||
@ -94,18 +110,18 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !handlingInfo.serviceAvailable {
|
if !handlingInfo.serviceAvailable {
|
||||||
http.Error(w, "service unavailable", http.StatusServiceUnavailable)
|
proxyError(w, req, "service unavailable", http.StatusServiceUnavailable)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if handlingInfo.transportBuildingError != nil {
|
if handlingInfo.transportBuildingError != nil {
|
||||||
http.Error(w, handlingInfo.transportBuildingError.Error(), http.StatusInternalServerError)
|
proxyError(w, req, handlingInfo.transportBuildingError.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, ok := genericapirequest.UserFrom(req.Context())
|
user, ok := genericapirequest.UserFrom(req.Context())
|
||||||
if !ok {
|
if !ok {
|
||||||
http.Error(w, "missing user", http.StatusInternalServerError)
|
proxyError(w, req, "missing user", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +131,7 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
rloc, err := r.serviceResolver.ResolveEndpoint(handlingInfo.serviceNamespace, handlingInfo.serviceName)
|
rloc, err := r.serviceResolver.ResolveEndpoint(handlingInfo.serviceNamespace, handlingInfo.serviceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("error resolving %s/%s: %v", handlingInfo.serviceNamespace, handlingInfo.serviceName, err)
|
klog.Errorf("error resolving %s/%s: %v", handlingInfo.serviceNamespace, handlingInfo.serviceName, err)
|
||||||
http.Error(w, "service unavailable", http.StatusServiceUnavailable)
|
proxyError(w, req, "service unavailable", http.StatusServiceUnavailable)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
location.Host = rloc.Host
|
location.Host = rloc.Host
|
||||||
@ -128,14 +144,14 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
newReq.URL = location
|
newReq.URL = location
|
||||||
|
|
||||||
if handlingInfo.proxyRoundTripper == nil {
|
if handlingInfo.proxyRoundTripper == nil {
|
||||||
http.Error(w, "", http.StatusNotFound)
|
proxyError(w, req, "", http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need to wrap the roundtripper in another roundtripper which will apply the front proxy headers
|
// we need to wrap the roundtripper in another roundtripper which will apply the front proxy headers
|
||||||
proxyRoundTripper, upgrade, err := maybeWrapForConnectionUpgrades(handlingInfo.restConfig, handlingInfo.proxyRoundTripper, req)
|
proxyRoundTripper, upgrade, err := maybeWrapForConnectionUpgrades(handlingInfo.restConfig, handlingInfo.proxyRoundTripper, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
proxyError(w, req, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
proxyRoundTripper = transport.NewAuthProxyRoundTripper(user.GetName(), user.GetGroups(), user.GetExtra(), proxyRoundTripper)
|
proxyRoundTripper = transport.NewAuthProxyRoundTripper(user.GetName(), user.GetGroups(), user.GetExtra(), proxyRoundTripper)
|
||||||
@ -197,6 +213,7 @@ func (r *proxyHandler) updateAPIService(apiService *apiregistrationapi.APIServic
|
|||||||
}
|
}
|
||||||
|
|
||||||
newInfo := proxyHandlingInfo{
|
newInfo := proxyHandlingInfo{
|
||||||
|
name: apiService.Name,
|
||||||
restConfig: &restclient.Config{
|
restConfig: &restclient.Config{
|
||||||
TLSClientConfig: restclient.TLSClientConfig{
|
TLSClientConfig: restclient.TLSClientConfig{
|
||||||
Insecure: apiService.Spec.InsecureSkipTLSVerify,
|
Insecure: apiService.Spec.InsecureSkipTLSVerify,
|
||||||
|
@ -8,7 +8,10 @@ load(
|
|||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = ["available_controller.go"],
|
srcs = [
|
||||||
|
"available_controller.go",
|
||||||
|
"metrics.go",
|
||||||
|
],
|
||||||
importmap = "k8s.io/kubernetes/vendor/k8s.io/kube-aggregator/pkg/controllers/status",
|
importmap = "k8s.io/kubernetes/vendor/k8s.io/kube-aggregator/pkg/controllers/status",
|
||||||
importpath = "k8s.io/kube-aggregator/pkg/controllers/status",
|
importpath = "k8s.io/kube-aggregator/pkg/controllers/status",
|
||||||
deps = [
|
deps = [
|
||||||
@ -16,7 +19,6 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||||
@ -30,6 +32,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/kube-aggregator/pkg/client/informers/internalversion/apiregistration/internalversion:go_default_library",
|
"//staging/src/k8s.io/kube-aggregator/pkg/client/informers/internalversion/apiregistration/internalversion:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-aggregator/pkg/client/listers/apiregistration/internalversion:go_default_library",
|
"//staging/src/k8s.io/kube-aggregator/pkg/client/listers/apiregistration/internalversion:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-aggregator/pkg/controllers:go_default_library",
|
"//staging/src/k8s.io/kube-aggregator/pkg/controllers:go_default_library",
|
||||||
|
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -29,7 +29,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
@ -154,7 +153,6 @@ func (c *AvailableConditionController) sync(key string) error {
|
|||||||
availableCondition := apiregistration.APIServiceCondition{
|
availableCondition := apiregistration.APIServiceCondition{
|
||||||
Type: apiregistration.Available,
|
Type: apiregistration.Available,
|
||||||
Status: apiregistration.ConditionTrue,
|
Status: apiregistration.ConditionTrue,
|
||||||
LastTransitionTime: metav1.Now(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// local API services are always considered available
|
// local API services are always considered available
|
||||||
@ -283,7 +281,30 @@ func updateAPIServiceStatus(client apiregistrationclient.APIServicesGetter, orig
|
|||||||
if equality.Semantic.DeepEqual(originalAPIService.Status, newAPIService.Status) {
|
if equality.Semantic.DeepEqual(originalAPIService.Status, newAPIService.Status) {
|
||||||
return newAPIService, nil
|
return newAPIService, nil
|
||||||
}
|
}
|
||||||
return client.APIServices().UpdateStatus(newAPIService)
|
|
||||||
|
newAPIService, err := client.APIServices().UpdateStatus(newAPIService)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update metrics
|
||||||
|
wasAvailable := apiregistration.IsAPIServiceConditionTrue(originalAPIService, apiregistration.Available)
|
||||||
|
isAvailable := apiregistration.IsAPIServiceConditionTrue(newAPIService, apiregistration.Available)
|
||||||
|
if isAvailable != wasAvailable {
|
||||||
|
if isAvailable {
|
||||||
|
unavailableGauge.WithLabelValues(newAPIService.Name).Set(0.0)
|
||||||
|
} else {
|
||||||
|
unavailableGauge.WithLabelValues(newAPIService.Name).Set(1.0)
|
||||||
|
|
||||||
|
reason := "UnknownReason"
|
||||||
|
if newCondition := apiregistration.GetAPIServiceConditionByType(newAPIService, apiregistration.Available); newCondition != nil {
|
||||||
|
reason = newCondition.Reason
|
||||||
|
}
|
||||||
|
unavailableCounter.WithLabelValues(newAPIService.Name, reason).Inc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newAPIService, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AvailableConditionController) Run(threadiness int, stopCh <-chan struct{}) {
|
func (c *AvailableConditionController) Run(threadiness int, stopCh <-chan struct{}) {
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 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 apiserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
unavailableCounter = prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "aggregator_unavailable_apiservice_count",
|
||||||
|
Help: "Counter of APIServices which are marked as unavailable broken down by APIService name and reason.",
|
||||||
|
},
|
||||||
|
[]string{"name", "reason"},
|
||||||
|
)
|
||||||
|
unavailableGauge = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "aggregator_unavailable_apiserver_gauge",
|
||||||
|
Help: "Gauge of APIServices which are marked as unavailable broken down by APIService name.",
|
||||||
|
},
|
||||||
|
[]string{"name"},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
prometheus.MustRegister(unavailableCounter)
|
||||||
|
prometheus.MustRegister(unavailableGauge)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user