mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 03:11:40 +00:00
Merge pull request #87913 from cheftako/master
Add code to fix kubelet/metrics memory issue.
This commit is contained in:
commit
b3ba969756
@ -89,6 +89,7 @@ type Server struct {
|
|||||||
auth AuthInterface
|
auth AuthInterface
|
||||||
host HostInterface
|
host HostInterface
|
||||||
restfulCont containerInterface
|
restfulCont containerInterface
|
||||||
|
metricsBuckets map[string]bool
|
||||||
resourceAnalyzer stats.ResourceAnalyzer
|
resourceAnalyzer stats.ResourceAnalyzer
|
||||||
redirectContainerStreaming bool
|
redirectContainerStreaming bool
|
||||||
}
|
}
|
||||||
@ -225,6 +226,7 @@ func NewServer(
|
|||||||
resourceAnalyzer: resourceAnalyzer,
|
resourceAnalyzer: resourceAnalyzer,
|
||||||
auth: auth,
|
auth: auth,
|
||||||
restfulCont: &filteringContainer{Container: restful.NewContainer()},
|
restfulCont: &filteringContainer{Container: restful.NewContainer()},
|
||||||
|
metricsBuckets: make(map[string]bool),
|
||||||
redirectContainerStreaming: redirectContainerStreaming,
|
redirectContainerStreaming: redirectContainerStreaming,
|
||||||
}
|
}
|
||||||
if auth != nil {
|
if auth != nil {
|
||||||
@ -280,14 +282,32 @@ func (s *Server) InstallAuthFilter() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addMetricsBucketMatcher adds a regexp matcher and the relevant bucket to use when
|
||||||
|
// it matches. Please be aware this is not thread safe and should not be used dynamically
|
||||||
|
func (s *Server) addMetricsBucketMatcher(bucket string) {
|
||||||
|
s.metricsBuckets[bucket] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMetricBucket find the appropriate metrics reporting bucket for the given path
|
||||||
|
func (s *Server) getMetricBucket(path string) string {
|
||||||
|
root := getURLRootPath(path)
|
||||||
|
if s.metricsBuckets[root] == true {
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
return "Invalid path"
|
||||||
|
}
|
||||||
|
|
||||||
// InstallDefaultHandlers registers the default set of supported HTTP request
|
// InstallDefaultHandlers registers the default set of supported HTTP request
|
||||||
// patterns with the restful Container.
|
// patterns with the restful Container.
|
||||||
func (s *Server) InstallDefaultHandlers(enableCAdvisorJSONEndpoints bool) {
|
func (s *Server) InstallDefaultHandlers(enableCAdvisorJSONEndpoints bool) {
|
||||||
|
s.addMetricsBucketMatcher("healthz")
|
||||||
healthz.InstallHandler(s.restfulCont,
|
healthz.InstallHandler(s.restfulCont,
|
||||||
healthz.PingHealthz,
|
healthz.PingHealthz,
|
||||||
healthz.LogHealthz,
|
healthz.LogHealthz,
|
||||||
healthz.NamedCheck("syncloop", s.syncLoopHealthCheck),
|
healthz.NamedCheck("syncloop", s.syncLoopHealthCheck),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("pods")
|
||||||
ws := new(restful.WebService)
|
ws := new(restful.WebService)
|
||||||
ws.
|
ws.
|
||||||
Path("/pods").
|
Path("/pods").
|
||||||
@ -297,7 +317,14 @@ func (s *Server) InstallDefaultHandlers(enableCAdvisorJSONEndpoints bool) {
|
|||||||
Operation("getPods"))
|
Operation("getPods"))
|
||||||
s.restfulCont.Add(ws)
|
s.restfulCont.Add(ws)
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("stats")
|
||||||
s.restfulCont.Add(stats.CreateHandlers(statsPath, s.host, s.resourceAnalyzer, enableCAdvisorJSONEndpoints))
|
s.restfulCont.Add(stats.CreateHandlers(statsPath, s.host, s.resourceAnalyzer, enableCAdvisorJSONEndpoints))
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("metrics")
|
||||||
|
s.addMetricsBucketMatcher("metrics/cadvisor")
|
||||||
|
s.addMetricsBucketMatcher("metrics/probes")
|
||||||
|
s.addMetricsBucketMatcher("metrics/resource/v1alpha1")
|
||||||
|
s.addMetricsBucketMatcher("metrics/resource")
|
||||||
//lint:ignore SA1019 https://github.com/kubernetes/enhancements/issues/1206
|
//lint:ignore SA1019 https://github.com/kubernetes/enhancements/issues/1206
|
||||||
s.restfulCont.Handle(metricsPath, legacyregistry.Handler())
|
s.restfulCont.Handle(metricsPath, legacyregistry.Handler())
|
||||||
|
|
||||||
@ -321,12 +348,14 @@ func (s *Server) InstallDefaultHandlers(enableCAdvisorJSONEndpoints bool) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// deprecated endpoint which will be removed in release 1.20.0+.
|
// deprecated endpoint which will be removed in release 1.20.0+.
|
||||||
|
s.addMetricsBucketMatcher("metrics/resource/v1alpha1")
|
||||||
v1alpha1ResourceRegistry := compbasemetrics.NewKubeRegistry()
|
v1alpha1ResourceRegistry := compbasemetrics.NewKubeRegistry()
|
||||||
v1alpha1ResourceRegistry.CustomMustRegister(stats.NewPrometheusResourceMetricCollector(s.resourceAnalyzer, v1alpha1.Config()))
|
v1alpha1ResourceRegistry.CustomMustRegister(stats.NewPrometheusResourceMetricCollector(s.resourceAnalyzer, v1alpha1.Config()))
|
||||||
s.restfulCont.Handle(path.Join(resourceMetricsPath, v1alpha1.Version),
|
s.restfulCont.Handle(path.Join(resourceMetricsPath, v1alpha1.Version),
|
||||||
compbasemetrics.HandlerFor(v1alpha1ResourceRegistry, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}),
|
compbasemetrics.HandlerFor(v1alpha1ResourceRegistry, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("metrics/resource")
|
||||||
resourceRegistry := compbasemetrics.NewKubeRegistry()
|
resourceRegistry := compbasemetrics.NewKubeRegistry()
|
||||||
resourceRegistry.CustomMustRegister(collectors.NewResourceMetricsCollector(s.resourceAnalyzer))
|
resourceRegistry.CustomMustRegister(collectors.NewResourceMetricsCollector(s.resourceAnalyzer))
|
||||||
s.restfulCont.Handle(resourceMetricsPath,
|
s.restfulCont.Handle(resourceMetricsPath,
|
||||||
@ -335,6 +364,7 @@ func (s *Server) InstallDefaultHandlers(enableCAdvisorJSONEndpoints bool) {
|
|||||||
|
|
||||||
// prober metrics are exposed under a different endpoint
|
// prober metrics are exposed under a different endpoint
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("metrics/probes")
|
||||||
p := compbasemetrics.NewKubeRegistry()
|
p := compbasemetrics.NewKubeRegistry()
|
||||||
_ = compbasemetrics.RegisterProcessStartTime(p.Register)
|
_ = compbasemetrics.RegisterProcessStartTime(p.Register)
|
||||||
p.MustRegister(prober.ProberResults)
|
p.MustRegister(prober.ProberResults)
|
||||||
@ -342,6 +372,7 @@ func (s *Server) InstallDefaultHandlers(enableCAdvisorJSONEndpoints bool) {
|
|||||||
compbasemetrics.HandlerFor(p, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}),
|
compbasemetrics.HandlerFor(p, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("spec")
|
||||||
if enableCAdvisorJSONEndpoints {
|
if enableCAdvisorJSONEndpoints {
|
||||||
ws := new(restful.WebService)
|
ws := new(restful.WebService)
|
||||||
ws.
|
ws.
|
||||||
@ -361,6 +392,7 @@ const pprofBasePath = "/debug/pprof/"
|
|||||||
func (s *Server) InstallDebuggingHandlers(criHandler http.Handler) {
|
func (s *Server) InstallDebuggingHandlers(criHandler http.Handler) {
|
||||||
klog.Infof("Adding debug handlers to kubelet server.")
|
klog.Infof("Adding debug handlers to kubelet server.")
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("run")
|
||||||
ws := new(restful.WebService)
|
ws := new(restful.WebService)
|
||||||
ws.
|
ws.
|
||||||
Path("/run")
|
Path("/run")
|
||||||
@ -372,6 +404,7 @@ func (s *Server) InstallDebuggingHandlers(criHandler http.Handler) {
|
|||||||
Operation("getRun"))
|
Operation("getRun"))
|
||||||
s.restfulCont.Add(ws)
|
s.restfulCont.Add(ws)
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("exec")
|
||||||
ws = new(restful.WebService)
|
ws = new(restful.WebService)
|
||||||
ws.
|
ws.
|
||||||
Path("/exec")
|
Path("/exec")
|
||||||
@ -389,6 +422,7 @@ func (s *Server) InstallDebuggingHandlers(criHandler http.Handler) {
|
|||||||
Operation("getExec"))
|
Operation("getExec"))
|
||||||
s.restfulCont.Add(ws)
|
s.restfulCont.Add(ws)
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("attach")
|
||||||
ws = new(restful.WebService)
|
ws = new(restful.WebService)
|
||||||
ws.
|
ws.
|
||||||
Path("/attach")
|
Path("/attach")
|
||||||
@ -406,6 +440,7 @@ func (s *Server) InstallDebuggingHandlers(criHandler http.Handler) {
|
|||||||
Operation("getAttach"))
|
Operation("getAttach"))
|
||||||
s.restfulCont.Add(ws)
|
s.restfulCont.Add(ws)
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("portForward")
|
||||||
ws = new(restful.WebService)
|
ws = new(restful.WebService)
|
||||||
ws.
|
ws.
|
||||||
Path("/portForward")
|
Path("/portForward")
|
||||||
@ -423,6 +458,7 @@ func (s *Server) InstallDebuggingHandlers(criHandler http.Handler) {
|
|||||||
Operation("getPortForward"))
|
Operation("getPortForward"))
|
||||||
s.restfulCont.Add(ws)
|
s.restfulCont.Add(ws)
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("logs")
|
||||||
ws = new(restful.WebService)
|
ws = new(restful.WebService)
|
||||||
ws.
|
ws.
|
||||||
Path(logsPath)
|
Path(logsPath)
|
||||||
@ -435,6 +471,7 @@ func (s *Server) InstallDebuggingHandlers(criHandler http.Handler) {
|
|||||||
Param(ws.PathParameter("logpath", "path to the log").DataType("string")))
|
Param(ws.PathParameter("logpath", "path to the log").DataType("string")))
|
||||||
s.restfulCont.Add(ws)
|
s.restfulCont.Add(ws)
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("containerLogs")
|
||||||
ws = new(restful.WebService)
|
ws = new(restful.WebService)
|
||||||
ws.
|
ws.
|
||||||
Path("/containerLogs")
|
Path("/containerLogs")
|
||||||
@ -443,8 +480,10 @@ func (s *Server) InstallDebuggingHandlers(criHandler http.Handler) {
|
|||||||
Operation("getContainerLogs"))
|
Operation("getContainerLogs"))
|
||||||
s.restfulCont.Add(ws)
|
s.restfulCont.Add(ws)
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("configz")
|
||||||
configz.InstallHandler(s.restfulCont)
|
configz.InstallHandler(s.restfulCont)
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("debug")
|
||||||
handlePprofEndpoint := func(req *restful.Request, resp *restful.Response) {
|
handlePprofEndpoint := func(req *restful.Request, resp *restful.Response) {
|
||||||
name := strings.TrimPrefix(req.Request.URL.Path, pprofBasePath)
|
name := strings.TrimPrefix(req.Request.URL.Path, pprofBasePath)
|
||||||
switch name {
|
switch name {
|
||||||
@ -460,7 +499,6 @@ func (s *Server) InstallDebuggingHandlers(criHandler http.Handler) {
|
|||||||
pprof.Index(resp, req.Request)
|
pprof.Index(resp, req.Request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup pprof handlers.
|
// Setup pprof handlers.
|
||||||
ws = new(restful.WebService).Path(pprofBasePath)
|
ws = new(restful.WebService).Path(pprofBasePath)
|
||||||
ws.Route(ws.GET("/{subpath:*}").To(func(req *restful.Request, resp *restful.Response) {
|
ws.Route(ws.GET("/{subpath:*}").To(func(req *restful.Request, resp *restful.Response) {
|
||||||
@ -473,6 +511,7 @@ func (s *Server) InstallDebuggingHandlers(criHandler http.Handler) {
|
|||||||
s.restfulCont.Handle("/debug/flags/v", routes.StringFlagPutHandler(logs.GlogSetter))
|
s.restfulCont.Handle("/debug/flags/v", routes.StringFlagPutHandler(logs.GlogSetter))
|
||||||
|
|
||||||
// The /runningpods endpoint is used for testing only.
|
// The /runningpods endpoint is used for testing only.
|
||||||
|
s.addMetricsBucketMatcher("runningpods")
|
||||||
ws = new(restful.WebService)
|
ws = new(restful.WebService)
|
||||||
ws.
|
ws.
|
||||||
Path("/runningpods/").
|
Path("/runningpods/").
|
||||||
@ -482,6 +521,7 @@ func (s *Server) InstallDebuggingHandlers(criHandler http.Handler) {
|
|||||||
Operation("getRunningPods"))
|
Operation("getRunningPods"))
|
||||||
s.restfulCont.Add(ws)
|
s.restfulCont.Add(ws)
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("cri")
|
||||||
if criHandler != nil {
|
if criHandler != nil {
|
||||||
s.restfulCont.Handle("/cri/", criHandler)
|
s.restfulCont.Handle("/cri/", criHandler)
|
||||||
}
|
}
|
||||||
@ -493,6 +533,14 @@ func (s *Server) InstallDebuggingDisabledHandlers() {
|
|||||||
http.Error(w, "Debug endpoints are disabled.", http.StatusMethodNotAllowed)
|
http.Error(w, "Debug endpoints are disabled.", http.StatusMethodNotAllowed)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
s.addMetricsBucketMatcher("run")
|
||||||
|
s.addMetricsBucketMatcher("exec")
|
||||||
|
s.addMetricsBucketMatcher("attach")
|
||||||
|
s.addMetricsBucketMatcher("portForward")
|
||||||
|
s.addMetricsBucketMatcher("containerLogs")
|
||||||
|
s.addMetricsBucketMatcher("runningpods")
|
||||||
|
s.addMetricsBucketMatcher("pprof")
|
||||||
|
s.addMetricsBucketMatcher("logs")
|
||||||
paths := []string{
|
paths := []string{
|
||||||
"/run/", "/exec/", "/attach/", "/portForward/", "/containerLogs/",
|
"/run/", "/exec/", "/attach/", "/portForward/", "/containerLogs/",
|
||||||
"/runningpods/", pprofBasePath, logsPath}
|
"/runningpods/", pprofBasePath, logsPath}
|
||||||
@ -809,10 +857,10 @@ 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.
|
// getURLRootPath trims a URL path.
|
||||||
// For paths in the format of "/metrics/xxx", "metrics/xxx" is returned;
|
// For paths in the format of "/metrics/xxx", "metrics/xxx" is returned;
|
||||||
// For all other paths, the first part of the path is returned.
|
// For all other paths, the first part of the path is returned.
|
||||||
func trimURLPath(path string) string {
|
func getURLRootPath(path string) string {
|
||||||
parts := strings.SplitN(strings.TrimPrefix(path, "/"), "/", 3)
|
parts := strings.SplitN(strings.TrimPrefix(path, "/"), "/", 3)
|
||||||
if len(parts) == 0 {
|
if len(parts) == 0 {
|
||||||
return path
|
return path
|
||||||
@ -860,7 +908,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
serverType = "readwrite"
|
serverType = "readwrite"
|
||||||
}
|
}
|
||||||
|
|
||||||
method, path := req.Method, trimURLPath(req.URL.Path)
|
method, path := req.Method, s.getMetricBucket(req.URL.Path)
|
||||||
|
|
||||||
longRunning := strconv.FormatBool(isLongRunningRequest(path))
|
longRunning := strconv.FormatBool(isLongRunningRequest(path))
|
||||||
|
|
||||||
|
@ -1471,6 +1471,58 @@ func TestCRIHandler(t *testing.T) {
|
|||||||
assert.Equal(t, query, fw.criHandler.RequestReceived.URL.RawQuery)
|
assert.Equal(t, query, fw.criHandler.RequestReceived.URL.RawQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMetricBuckets(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
url string
|
||||||
|
bucket string
|
||||||
|
}{
|
||||||
|
"healthz endpoint": {url: "/healthz", bucket: "healthz"},
|
||||||
|
"attach": {url: "/attach/podNamespace/podID/containerName", bucket: "attach"},
|
||||||
|
"attach with uid": {url: "/attach/podNamespace/podID/uid/containerName", bucket: "attach"},
|
||||||
|
"configz": {url: "/configz", bucket: "configz"},
|
||||||
|
"containerLogs": {url: "/containerLogs/podNamespace/podID/containerName", bucket: "containerLogs"},
|
||||||
|
"cri": {url: "/cri/", bucket: "cri"},
|
||||||
|
"cri with sub": {url: "/cri/foo", bucket: "cri"},
|
||||||
|
"debug v flags": {url: "/debug/flags/v", bucket: "debug"},
|
||||||
|
"pprof with sub": {url: "/debug/pprof/subpath", bucket: "debug"},
|
||||||
|
"exec": {url: "/exec/podNamespace/podID/containerName", bucket: "exec"},
|
||||||
|
"exec with uid": {url: "/exec/podNamespace/podID/uid/containerName", bucket: "exec"},
|
||||||
|
"healthz": {url: "/healthz/", bucket: "healthz"},
|
||||||
|
"healthz log sub": {url: "/healthz/log", bucket: "healthz"},
|
||||||
|
"healthz ping": {url: "/healthz/ping", bucket: "healthz"},
|
||||||
|
"healthz sync loop": {url: "/healthz/syncloop", bucket: "healthz"},
|
||||||
|
"logs": {url: "/logs/", bucket: "logs"},
|
||||||
|
"logs with path": {url: "/logs/logpath", bucket: "logs"},
|
||||||
|
"metrics": {url: "/metrics", bucket: "metrics"},
|
||||||
|
"metrics cadvisor sub": {url: "/metrics/cadvisor", bucket: "metrics/cadvisor"},
|
||||||
|
"metrics probes sub": {url: "/metrics/probes", bucket: "metrics/probes"},
|
||||||
|
"metrics resource v1alpha1": {url: "/metrics/resource/v1alpha1", bucket: "metrics/resource"},
|
||||||
|
"metrics resource sub": {url: "/metrics/resource", bucket: "metrics/resource"},
|
||||||
|
"pods": {url: "/pods/", bucket: "pods"},
|
||||||
|
"portForward": {url: "/portForward/podNamespace/podID", bucket: "portForward"},
|
||||||
|
"portForward with uid": {url: "/portForward/podNamespace/podID/uid", bucket: "portForward"},
|
||||||
|
"run": {url: "/run/podNamespace/podID/containerName", bucket: "run"},
|
||||||
|
"run with uid": {url: "/run/podNamespace/podID/uid/containerName", bucket: "run"},
|
||||||
|
"runningpods": {url: "/runningpods/", bucket: "runningpods"},
|
||||||
|
"spec": {url: "/spec/", bucket: "spec"},
|
||||||
|
"stats": {url: "/stats/", bucket: "stats"},
|
||||||
|
"stats container sub": {url: "/stats/container", bucket: "stats"},
|
||||||
|
"stats summary sub": {url: "/stats/summary", bucket: "stats"},
|
||||||
|
"stats containerName with uid": {url: "/stats/namespace/podName/uid/containerName", bucket: "stats"},
|
||||||
|
"stats containerName": {url: "/stats/podName/containerName", bucket: "stats"},
|
||||||
|
"invalid path": {url: "/junk", bucket: "Invalid path"},
|
||||||
|
"invalid path starting with good": {url: "/healthzjunk", bucket: "Invalid path"},
|
||||||
|
}
|
||||||
|
fw := newServerTest()
|
||||||
|
defer fw.testHTTPServer.Close()
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
path := test.url
|
||||||
|
bucket := test.bucket
|
||||||
|
require.Equal(t, fw.serverUnderTest.getMetricBucket(path), bucket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDebuggingDisabledHandlers(t *testing.T) {
|
func TestDebuggingDisabledHandlers(t *testing.T) {
|
||||||
fw := newServerTestWithDebug(false, false, nil)
|
fw := newServerTestWithDebug(false, false, nil)
|
||||||
defer fw.testHTTPServer.Close()
|
defer fw.testHTTPServer.Close()
|
||||||
@ -1544,6 +1596,6 @@ func TestTrimURLPath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
assert.Equal(t, test.expected, trimURLPath(test.path), fmt.Sprintf("path is: %s", test.path))
|
assert.Equal(t, test.expected, getURLRootPath(test.path), fmt.Sprintf("path is: %s", test.path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user