Merge pull request #128811 from zhifei92/statusz

add statusz endpoint  for kubelet
This commit is contained in:
Kubernetes Prow Robot 2024-12-22 11:24:10 +01:00 committed by GitHub
commit 3ec9c7f4d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 29 additions and 10 deletions

View File

@ -132,14 +132,9 @@ func init() {
otel.SetMeterProvider(noop.NewMeterProvider()) otel.SetMeterProvider(noop.NewMeterProvider())
} }
const (
// Kubelet component name
componentKubelet = "kubelet"
)
// NewKubeletCommand creates a *cobra.Command object with default parameters // NewKubeletCommand creates a *cobra.Command object with default parameters
func NewKubeletCommand() *cobra.Command { func NewKubeletCommand() *cobra.Command {
cleanFlagSet := pflag.NewFlagSet(componentKubelet, pflag.ContinueOnError) cleanFlagSet := pflag.NewFlagSet(server.ComponentKubelet, pflag.ContinueOnError)
cleanFlagSet.SetNormalizeFunc(cliflag.WordSepNormalizeFunc) cleanFlagSet.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)
kubeletFlags := options.NewKubeletFlags() kubeletFlags := options.NewKubeletFlags()
@ -151,7 +146,7 @@ func NewKubeletCommand() *cobra.Command {
} }
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: componentKubelet, Use: server.ComponentKubelet,
Long: `The kubelet is the primary "node agent" that runs on each Long: `The kubelet is the primary "node agent" that runs on each
node. It can register the node with the apiserver using one of: the hostname; a flag to node. It can register the node with the apiserver using one of: the hostname; a flag to
override the hostname; or specific logic for a cloud provider. override the hostname; or specific logic for a cloud provider.
@ -562,7 +557,7 @@ func makeEventRecorder(ctx context.Context, kubeDeps *kubelet.Dependencies, node
return return
} }
eventBroadcaster := record.NewBroadcaster(record.WithContext(ctx)) eventBroadcaster := record.NewBroadcaster(record.WithContext(ctx))
kubeDeps.Recorder = eventBroadcaster.NewRecorder(legacyscheme.Scheme, v1.EventSource{Component: componentKubelet, Host: string(nodeName)}) kubeDeps.Recorder = eventBroadcaster.NewRecorder(legacyscheme.Scheme, v1.EventSource{Component: server.ComponentKubelet, Host: string(nodeName)})
eventBroadcaster.StartStructuredLogging(3) eventBroadcaster.StartStructuredLogging(3)
if kubeDeps.EventClient != nil { if kubeDeps.EventClient != nil {
klog.V(4).InfoS("Sending events to api server") klog.V(4).InfoS("Sending events to api server")
@ -1386,7 +1381,7 @@ func newTracerProvider(s *options.KubeletServer) (oteltrace.TracerProvider, erro
} }
resourceOpts := []otelsdkresource.Option{ resourceOpts := []otelsdkresource.Option{
otelsdkresource.WithAttributes( otelsdkresource.WithAttributes(
semconv.ServiceNameKey.String(componentKubelet), semconv.ServiceNameKey.String(server.ComponentKubelet),
semconv.HostNameKey.String(hostname), semconv.HostNameKey.String(hostname),
), ),
} }

View File

@ -27,6 +27,7 @@ import (
"k8s.io/apiserver/pkg/server/healthz" "k8s.io/apiserver/pkg/server/healthz"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/component-base/configz" "k8s.io/component-base/configz"
"k8s.io/component-base/zpages/statusz"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
) )
@ -68,6 +69,7 @@ func isSubpath(subpath, path string) bool {
// /metrics/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=metrics // /metrics/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=metrics
// /logs/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=log // /logs/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=log
// /checkpoint/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=checkpoint // /checkpoint/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=checkpoint
// /statusz => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=statusz
// /pods/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=pods,proxy // /pods/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=pods,proxy
// /runningPods/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=pods,proxy // /runningPods/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=pods,proxy
// /healthz/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=healthz,proxy // /healthz/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=healthz,proxy
@ -116,6 +118,8 @@ func (n nodeAuthorizerAttributesGetter) GetRequestAttributes(u user.Info, r *htt
subresources = append(subresources, "log") subresources = append(subresources, "log")
case isSubpath(requestPath, checkpointPath): case isSubpath(requestPath, checkpointPath):
subresources = append(subresources, "checkpoint") subresources = append(subresources, "checkpoint")
case isSubpath(requestPath, statusz.DefaultStatuszPath):
subresources = append(subresources, "statusz")
default: default:
subresources = append(subresources, "proxy") subresources = append(subresources, "proxy")
} }

View File

@ -125,6 +125,7 @@ func AuthzTestCases(fineGrained bool) []AuthzTestCase {
"/attach/{podNamespace}/{podID}/{uid}/{containerName}": {"proxy"}, "/attach/{podNamespace}/{podID}/{uid}/{containerName}": {"proxy"},
"/checkpoint/{podNamespace}/{podID}/{containerName}": {"checkpoint"}, "/checkpoint/{podNamespace}/{podID}/{containerName}": {"checkpoint"},
"/configz": {"proxy"}, "/configz": {"proxy"},
"/statusz": {"statusz"},
"/containerLogs/{podNamespace}/{podID}/{containerName}": {"proxy"}, "/containerLogs/{podNamespace}/{podID}/{containerName}": {"proxy"},
"/debug/flags/v": {"proxy"}, "/debug/flags/v": {"proxy"},
"/debug/pprof/{subpath:*}": {"proxy"}, "/debug/pprof/{subpath:*}": {"proxy"},

View File

@ -67,6 +67,8 @@ import (
metricsfeatures "k8s.io/component-base/metrics/features" metricsfeatures "k8s.io/component-base/metrics/features"
"k8s.io/component-base/metrics/legacyregistry" "k8s.io/component-base/metrics/legacyregistry"
"k8s.io/component-base/metrics/prometheus/slis" "k8s.io/component-base/metrics/prometheus/slis"
zpagesfeatures "k8s.io/component-base/zpages/features"
"k8s.io/component-base/zpages/statusz"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
"k8s.io/cri-client/pkg/util" "k8s.io/cri-client/pkg/util"
podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1" podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1"
@ -107,6 +109,11 @@ const (
runningPodsPath = "/runningpods/" runningPodsPath = "/runningpods/"
) )
const (
// Kubelet component name
ComponentKubelet = "kubelet"
)
// Server is a http.Handler which exposes kubelet functionality over HTTP. // Server is a http.Handler which exposes kubelet functionality over HTTP.
type Server struct { type Server struct {
auth AuthInterface auth AuthInterface
@ -567,6 +574,11 @@ func (s *Server) InstallDebuggingHandlers() {
s.addMetricsBucketMatcher("configz") s.addMetricsBucketMatcher("configz")
configz.InstallHandler(s.restfulCont) configz.InstallHandler(s.restfulCont)
if utilfeature.DefaultFeatureGate.Enabled(zpagesfeatures.ComponentStatusz) {
s.addMetricsBucketMatcher("statusz")
statusz.Install(s.restfulCont, ComponentKubelet, statusz.NewRegistry())
}
// The /runningpods endpoint is used for testing only. // The /runningpods endpoint is used for testing only.
s.addMetricsBucketMatcher("runningpods") s.addMetricsBucketMatcher("runningpods")
ws = new(restful.WebService) ws = new(restful.WebService)

View File

@ -57,6 +57,7 @@ import (
"k8s.io/apiserver/pkg/server/healthz" "k8s.io/apiserver/pkg/server/healthz"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing" featuregatetesting "k8s.io/component-base/featuregate/testing"
zpagesfeatures "k8s.io/component-base/zpages/features"
"k8s.io/kubelet/pkg/cri/streaming" "k8s.io/kubelet/pkg/cri/streaming"
"k8s.io/kubelet/pkg/cri/streaming/portforward" "k8s.io/kubelet/pkg/cri/streaming/portforward"
remotecommandserver "k8s.io/kubelet/pkg/cri/streaming/remotecommand" remotecommandserver "k8s.io/kubelet/pkg/cri/streaming/remotecommand"
@ -572,6 +573,7 @@ func TestAuthzCoverage(t *testing.T) {
func TestAuthFilters(t *testing.T) { func TestAuthFilters(t *testing.T) {
// Enable features.ContainerCheckpoint during test // Enable features.ContainerCheckpoint during test
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ContainerCheckpoint, true) featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ContainerCheckpoint, true)
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, zpagesfeatures.ComponentStatusz, true)
fw := newServerTest() fw := newServerTest()
defer fw.testHTTPServer.Close() defer fw.testHTTPServer.Close()
@ -1617,6 +1619,8 @@ func TestServePortForward(t *testing.T) {
} }
func TestMetricBuckets(t *testing.T) { func TestMetricBuckets(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, zpagesfeatures.ComponentStatusz, true)
tests := map[string]struct { tests := map[string]struct {
url string url string
bucket string bucket string
@ -1648,6 +1652,7 @@ func TestMetricBuckets(t *testing.T) {
"runningpods": {url: "/runningpods/", bucket: "runningpods"}, "runningpods": {url: "/runningpods/", bucket: "runningpods"},
"stats": {url: "/stats/", bucket: "stats"}, "stats": {url: "/stats/", bucket: "stats"},
"stats summary sub": {url: "/stats/summary", bucket: "stats"}, "stats summary sub": {url: "/stats/summary", bucket: "stats"},
"statusz": {url: "/statusz", bucket: "statusz"},
"invalid path": {url: "/junk", bucket: "other"}, "invalid path": {url: "/junk", bucket: "other"},
"invalid path starting with good": {url: "/healthzjunk", bucket: "other"}, "invalid path starting with good": {url: "/healthzjunk", bucket: "other"},
} }

View File

@ -35,6 +35,8 @@ var (
errUnsupportedMediaType = fmt.Errorf("media type not acceptable, must be: text/plain") errUnsupportedMediaType = fmt.Errorf("media type not acceptable, must be: text/plain")
) )
const DefaultStatuszPath = "/statusz"
const ( const (
headerFmt = ` headerFmt = `
%s statusz %s statusz
@ -73,7 +75,7 @@ func Install(m mux, componentName string, reg statuszRegistry) {
klog.Errorf("error while parsing gotemplates: %v", err) klog.Errorf("error while parsing gotemplates: %v", err)
return return
} }
m.Handle("/statusz", handleStatusz(componentName, dataTmpl, reg)) m.Handle(DefaultStatuszPath, handleStatusz(componentName, dataTmpl, reg))
} }
func initializeTemplates() (*template.Template, error) { func initializeTemplates() (*template.Template, error) {