Skip to content

Commit 3bbd0e9

Browse files
authored
Merge pull request kubernetes#75228 from haiyanmeng/kubelet
Add metrics to monitor the kubelet http server
2 parents dc071b8 + 538cd87 commit 3bbd0e9

File tree

5 files changed

+179
-0
lines changed

5 files changed

+179
-0
lines changed

pkg/kubelet/server/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ go_library(
2323
"//pkg/kubelet/apis/resourcemetrics/v1alpha1:go_default_library",
2424
"//pkg/kubelet/container:go_default_library",
2525
"//pkg/kubelet/prober:go_default_library",
26+
"//pkg/kubelet/server/metrics:go_default_library",
2627
"//pkg/kubelet/server/portforward:go_default_library",
2728
"//pkg/kubelet/server/remotecommand:go_default_library",
2829
"//pkg/kubelet/server/stats:go_default_library",
@@ -105,6 +106,7 @@ filegroup(
105106
name = "all-srcs",
106107
srcs = [
107108
":package-srcs",
109+
"//pkg/kubelet/server/metrics:all-srcs",
108110
"//pkg/kubelet/server/portforward:all-srcs",
109111
"//pkg/kubelet/server/remotecommand:all-srcs",
110112
"//pkg/kubelet/server/stats:all-srcs",

pkg/kubelet/server/metrics/BUILD

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package(default_visibility = ["//visibility:public"])
2+
3+
load(
4+
"@io_bazel_rules_go//go:def.bzl",
5+
"go_library",
6+
)
7+
8+
go_library(
9+
name = "go_default_library",
10+
srcs = ["metrics.go"],
11+
importpath = "k8s.io/kubernetes/pkg/kubelet/server/metrics",
12+
deps = [
13+
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
14+
],
15+
)
16+
17+
filegroup(
18+
name = "package-srcs",
19+
srcs = glob(["**"]),
20+
tags = ["automanaged"],
21+
visibility = ["//visibility:private"],
22+
)
23+
24+
filegroup(
25+
name = "all-srcs",
26+
srcs = [":package-srcs"],
27+
tags = ["automanaged"],
28+
)
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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 metrics
18+
19+
import (
20+
"sync"
21+
"time"
22+
23+
"github.com/prometheus/client_golang/prometheus"
24+
)
25+
26+
const (
27+
kubeletSubsystem = "kubelet"
28+
)
29+
30+
var (
31+
// HTTPRequests tracks the number of the http requests received since the server started.
32+
HTTPRequests = prometheus.NewCounterVec(
33+
prometheus.CounterOpts{
34+
Subsystem: kubeletSubsystem,
35+
Name: "http_requests_total",
36+
Help: "Number of the http requests received since the server started",
37+
},
38+
// server_type aims to differentiate the readonly server and the readwrite server.
39+
// long_running marks whether the request is long-running or not.
40+
// Currently, long-running requests include exec/attach/portforward/debug.
41+
[]string{"method", "path", "host", "server_type", "long_running"},
42+
)
43+
// HTTPRequestsDuration tracks the duration in seconds to serve http requests.
44+
HTTPRequestsDuration = prometheus.NewHistogramVec(
45+
prometheus.HistogramOpts{
46+
Subsystem: kubeletSubsystem,
47+
Name: "http_requests_duration_seconds",
48+
Help: "Duration in seconds to serve http requests",
49+
// Use DefBuckets for now, will customize the buckets if necessary.
50+
Buckets: prometheus.DefBuckets,
51+
},
52+
[]string{"method", "path", "host", "server_type", "long_running"},
53+
)
54+
// HTTPInflightRequests tracks the number of the inflight http requests.
55+
HTTPInflightRequests = prometheus.NewGaugeVec(
56+
prometheus.GaugeOpts{
57+
Subsystem: kubeletSubsystem,
58+
Name: "http_inflight_requests",
59+
Help: "Number of the inflight http requests",
60+
},
61+
[]string{"method", "path", "host", "server_type", "long_running"},
62+
)
63+
)
64+
65+
var registerMetrics sync.Once
66+
67+
// Register all metrics.
68+
func Register() {
69+
registerMetrics.Do(func() {
70+
prometheus.MustRegister(HTTPRequests)
71+
prometheus.MustRegister(HTTPRequestsDuration)
72+
prometheus.MustRegister(HTTPInflightRequests)
73+
})
74+
}
75+
76+
// SinceInSeconds gets the time since the specified start in seconds.
77+
func SinceInSeconds(start time.Time) float64 {
78+
return time.Since(start).Seconds()
79+
}

pkg/kubelet/server/server.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import (
6363
"k8s.io/kubernetes/pkg/kubelet/apis/resourcemetrics/v1alpha1"
6464
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
6565
"k8s.io/kubernetes/pkg/kubelet/prober"
66+
servermetrics "k8s.io/kubernetes/pkg/kubelet/server/metrics"
6667
"k8s.io/kubernetes/pkg/kubelet/server/portforward"
6768
remotecommandserver "k8s.io/kubernetes/pkg/kubelet/server/remotecommand"
6869
"k8s.io/kubernetes/pkg/kubelet/server/stats"
@@ -807,6 +808,33 @@ func (s *Server) getPortForward(request *restful.Request, response *restful.Resp
807808
proxyStream(response.ResponseWriter, request.Request, url)
808809
}
809810

811+
// trimURLPath trims a URL path.
812+
// For paths in the format of "/metrics/xxx", "metrics/xxx" is returned;
813+
// For all other paths, the first part of the path is returned.
814+
func trimURLPath(path string) string {
815+
parts := strings.SplitN(strings.TrimPrefix(path, "/"), "/", 3)
816+
if len(parts) == 0 {
817+
return path
818+
}
819+
820+
if parts[0] == "metrics" && len(parts) > 1 {
821+
return fmt.Sprintf("%s/%s", parts[0], parts[1])
822+
823+
}
824+
return parts[0]
825+
}
826+
827+
// isLongRunningRequest determines whether the request is long-running or not.
828+
func isLongRunningRequest(path string) bool {
829+
longRunningRequestPaths := []string{"exec", "attach", "portforward", "debug"}
830+
for _, p := range longRunningRequestPaths {
831+
if p == path {
832+
return true
833+
}
834+
}
835+
return false
836+
}
837+
810838
// ServeHTTP responds to HTTP requests on the Kubelet.
811839
func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
812840
defer httplog.NewLogged(req, &w).StacktraceWhen(
@@ -820,6 +848,27 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
820848
http.StatusSwitchingProtocols,
821849
),
822850
).Log()
851+
852+
// monitor http requests
853+
var serverType string
854+
if s.auth == nil {
855+
serverType = "readonly"
856+
} else {
857+
serverType = "readwrite"
858+
}
859+
860+
method, path, host := req.Method, trimURLPath(req.URL.Path), req.URL.Host
861+
862+
longRunning := strconv.FormatBool(isLongRunningRequest(path))
863+
864+
servermetrics.HTTPRequests.WithLabelValues(method, path, host, serverType, longRunning).Inc()
865+
866+
servermetrics.HTTPInflightRequests.WithLabelValues(method, path, host, serverType, longRunning).Inc()
867+
defer servermetrics.HTTPInflightRequests.WithLabelValues(method, path, host, serverType, longRunning).Dec()
868+
869+
startTime := time.Now()
870+
defer servermetrics.HTTPRequestsDuration.WithLabelValues(method, path, host, serverType, longRunning).Observe(servermetrics.SinceInSeconds(startTime))
871+
823872
s.restfulCont.ServeHTTP(w, req)
824873
}
825874

pkg/kubelet/server/server_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,3 +1666,24 @@ func TestDebuggingDisabledHandlers(t *testing.T) {
16661666
assert.Equal(t, http.StatusOK, resp.StatusCode)
16671667

16681668
}
1669+
1670+
func TestTrimURLPath(t *testing.T) {
1671+
tests := []struct {
1672+
path, expected string
1673+
}{
1674+
{"", ""},
1675+
{"//", ""},
1676+
{"/pods", "pods"},
1677+
{"pods", "pods"},
1678+
{"pods/", "pods"},
1679+
{"good/", "good"},
1680+
{"pods/probes", "pods"},
1681+
{"metrics", "metrics"},
1682+
{"metrics/resource", "metrics/resource"},
1683+
{"metrics/hello", "metrics/hello"},
1684+
}
1685+
1686+
for _, test := range tests {
1687+
assert.Equal(t, test.expected, trimURLPath(test.path), fmt.Sprintf("path is: %s", test.path))
1688+
}
1689+
}

0 commit comments

Comments
 (0)