mirror of https://github.com/k3s-io/k3s
Merge pull request #75228 from haiyanmeng/kubelet
Add metrics to monitor the kubelet http serverk3s-v1.15.3
commit
3bbd0e92e8
|
@ -23,6 +23,7 @@ go_library(
|
||||||
"//pkg/kubelet/apis/resourcemetrics/v1alpha1:go_default_library",
|
"//pkg/kubelet/apis/resourcemetrics/v1alpha1:go_default_library",
|
||||||
"//pkg/kubelet/container:go_default_library",
|
"//pkg/kubelet/container:go_default_library",
|
||||||
"//pkg/kubelet/prober:go_default_library",
|
"//pkg/kubelet/prober:go_default_library",
|
||||||
|
"//pkg/kubelet/server/metrics:go_default_library",
|
||||||
"//pkg/kubelet/server/portforward:go_default_library",
|
"//pkg/kubelet/server/portforward:go_default_library",
|
||||||
"//pkg/kubelet/server/remotecommand:go_default_library",
|
"//pkg/kubelet/server/remotecommand:go_default_library",
|
||||||
"//pkg/kubelet/server/stats:go_default_library",
|
"//pkg/kubelet/server/stats:go_default_library",
|
||||||
|
@ -105,6 +106,7 @@ filegroup(
|
||||||
name = "all-srcs",
|
name = "all-srcs",
|
||||||
srcs = [
|
srcs = [
|
||||||
":package-srcs",
|
":package-srcs",
|
||||||
|
"//pkg/kubelet/server/metrics:all-srcs",
|
||||||
"//pkg/kubelet/server/portforward:all-srcs",
|
"//pkg/kubelet/server/portforward:all-srcs",
|
||||||
"//pkg/kubelet/server/remotecommand:all-srcs",
|
"//pkg/kubelet/server/remotecommand:all-srcs",
|
||||||
"//pkg/kubelet/server/stats:all-srcs",
|
"//pkg/kubelet/server/stats:all-srcs",
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
|
"go_library",
|
||||||
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["metrics.go"],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/kubelet/server/metrics",
|
||||||
|
deps = [
|
||||||
|
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
)
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 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 (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
kubeletSubsystem = "kubelet"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// HTTPRequests tracks the number of the http requests received since the server started.
|
||||||
|
HTTPRequests = prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Subsystem: kubeletSubsystem,
|
||||||
|
Name: "http_requests_total",
|
||||||
|
Help: "Number of the http requests received since the server started",
|
||||||
|
},
|
||||||
|
// server_type aims to differentiate the readonly server and the readwrite server.
|
||||||
|
// long_running marks whether the request is long-running or not.
|
||||||
|
// Currently, long-running requests include exec/attach/portforward/debug.
|
||||||
|
[]string{"method", "path", "host", "server_type", "long_running"},
|
||||||
|
)
|
||||||
|
// HTTPRequestsDuration tracks the duration in seconds to serve http requests.
|
||||||
|
HTTPRequestsDuration = prometheus.NewHistogramVec(
|
||||||
|
prometheus.HistogramOpts{
|
||||||
|
Subsystem: kubeletSubsystem,
|
||||||
|
Name: "http_requests_duration_seconds",
|
||||||
|
Help: "Duration in seconds to serve http requests",
|
||||||
|
// Use DefBuckets for now, will customize the buckets if necessary.
|
||||||
|
Buckets: prometheus.DefBuckets,
|
||||||
|
},
|
||||||
|
[]string{"method", "path", "host", "server_type", "long_running"},
|
||||||
|
)
|
||||||
|
// HTTPInflightRequests tracks the number of the inflight http requests.
|
||||||
|
HTTPInflightRequests = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Subsystem: kubeletSubsystem,
|
||||||
|
Name: "http_inflight_requests",
|
||||||
|
Help: "Number of the inflight http requests",
|
||||||
|
},
|
||||||
|
[]string{"method", "path", "host", "server_type", "long_running"},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
var registerMetrics sync.Once
|
||||||
|
|
||||||
|
// Register all metrics.
|
||||||
|
func Register() {
|
||||||
|
registerMetrics.Do(func() {
|
||||||
|
prometheus.MustRegister(HTTPRequests)
|
||||||
|
prometheus.MustRegister(HTTPRequestsDuration)
|
||||||
|
prometheus.MustRegister(HTTPInflightRequests)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SinceInSeconds gets the time since the specified start in seconds.
|
||||||
|
func SinceInSeconds(start time.Time) float64 {
|
||||||
|
return time.Since(start).Seconds()
|
||||||
|
}
|
|
@ -63,6 +63,7 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/kubelet/apis/resourcemetrics/v1alpha1"
|
"k8s.io/kubernetes/pkg/kubelet/apis/resourcemetrics/v1alpha1"
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/prober"
|
"k8s.io/kubernetes/pkg/kubelet/prober"
|
||||||
|
servermetrics "k8s.io/kubernetes/pkg/kubelet/server/metrics"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/server/portforward"
|
"k8s.io/kubernetes/pkg/kubelet/server/portforward"
|
||||||
remotecommandserver "k8s.io/kubernetes/pkg/kubelet/server/remotecommand"
|
remotecommandserver "k8s.io/kubernetes/pkg/kubelet/server/remotecommand"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/server/stats"
|
"k8s.io/kubernetes/pkg/kubelet/server/stats"
|
||||||
|
@ -807,6 +808,33 @@ func (s *Server) getPortForward(request *restful.Request, response *restful.Resp
|
||||||
proxyStream(response.ResponseWriter, request.Request, url)
|
proxyStream(response.ResponseWriter, request.Request, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trimURLPath trims a URL path.
|
||||||
|
// For paths in the format of "/metrics/xxx", "metrics/xxx" is returned;
|
||||||
|
// For all other paths, the first part of the path is returned.
|
||||||
|
func trimURLPath(path string) string {
|
||||||
|
parts := strings.SplitN(strings.TrimPrefix(path, "/"), "/", 3)
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
if parts[0] == "metrics" && len(parts) > 1 {
|
||||||
|
return fmt.Sprintf("%s/%s", parts[0], parts[1])
|
||||||
|
|
||||||
|
}
|
||||||
|
return parts[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// isLongRunningRequest determines whether the request is long-running or not.
|
||||||
|
func isLongRunningRequest(path string) bool {
|
||||||
|
longRunningRequestPaths := []string{"exec", "attach", "portforward", "debug"}
|
||||||
|
for _, p := range longRunningRequestPaths {
|
||||||
|
if p == path {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// ServeHTTP responds to HTTP requests on the Kubelet.
|
// ServeHTTP responds to HTTP requests on the Kubelet.
|
||||||
func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
defer httplog.NewLogged(req, &w).StacktraceWhen(
|
defer httplog.NewLogged(req, &w).StacktraceWhen(
|
||||||
|
@ -820,6 +848,27 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
http.StatusSwitchingProtocols,
|
http.StatusSwitchingProtocols,
|
||||||
),
|
),
|
||||||
).Log()
|
).Log()
|
||||||
|
|
||||||
|
// monitor http requests
|
||||||
|
var serverType string
|
||||||
|
if s.auth == nil {
|
||||||
|
serverType = "readonly"
|
||||||
|
} else {
|
||||||
|
serverType = "readwrite"
|
||||||
|
}
|
||||||
|
|
||||||
|
method, path, host := req.Method, trimURLPath(req.URL.Path), req.URL.Host
|
||||||
|
|
||||||
|
longRunning := strconv.FormatBool(isLongRunningRequest(path))
|
||||||
|
|
||||||
|
servermetrics.HTTPRequests.WithLabelValues(method, path, host, serverType, longRunning).Inc()
|
||||||
|
|
||||||
|
servermetrics.HTTPInflightRequests.WithLabelValues(method, path, host, serverType, longRunning).Inc()
|
||||||
|
defer servermetrics.HTTPInflightRequests.WithLabelValues(method, path, host, serverType, longRunning).Dec()
|
||||||
|
|
||||||
|
startTime := time.Now()
|
||||||
|
defer servermetrics.HTTPRequestsDuration.WithLabelValues(method, path, host, serverType, longRunning).Observe(servermetrics.SinceInSeconds(startTime))
|
||||||
|
|
||||||
s.restfulCont.ServeHTTP(w, req)
|
s.restfulCont.ServeHTTP(w, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1666,3 +1666,24 @@ func TestDebuggingDisabledHandlers(t *testing.T) {
|
||||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTrimURLPath(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
path, expected string
|
||||||
|
}{
|
||||||
|
{"", ""},
|
||||||
|
{"//", ""},
|
||||||
|
{"/pods", "pods"},
|
||||||
|
{"pods", "pods"},
|
||||||
|
{"pods/", "pods"},
|
||||||
|
{"good/", "good"},
|
||||||
|
{"pods/probes", "pods"},
|
||||||
|
{"metrics", "metrics"},
|
||||||
|
{"metrics/resource", "metrics/resource"},
|
||||||
|
{"metrics/hello", "metrics/hello"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
assert.Equal(t, test.expected, trimURLPath(test.path), fmt.Sprintf("path is: %s", test.path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue