From a57c10d6b73b9c10b3ea2c649e774016fe597c2e Mon Sep 17 00:00:00 2001 From: Arpit Srivastava Date: Wed, 4 Feb 2026 15:46:48 +0530 Subject: [PATCH] Merge pull request #8350 from Arpit529Srivastava/kubelet-cmd-flag feat: add flag to disable insecure kubelet metrics port --- Documentation/platform/operator.md | 2 + cmd/operator/main.go | 3 + pkg/kubelet/controller.go | 132 ++++++++++++++++++++--------- pkg/kubelet/controller_test.go | 70 +++++++++++++++ 4 files changed, 165 insertions(+), 42 deletions(-) diff --git a/Documentation/platform/operator.md b/Documentation/platform/operator.md index f3389e609..f36afa36f 100644 --- a/Documentation/platform/operator.md +++ b/Documentation/platform/operator.md @@ -75,6 +75,8 @@ Arguments: Create Endpoints objects for kubelet targets. (default true) -kubelet-endpointslice Create EndpointSlice objects for kubelet targets. + -kubelet-http-metrics + Include HTTP metrics port (10255) in kubelet service. Set to false if your cluster has disabled the insecure kubelet read-only port (e.g., GKE 1.32+). (default true) -kubelet-node-address-priority value Node address priority used by kubelet. Either 'internal' or 'external'. Default: 'internal'. -kubelet-selector value diff --git a/cmd/operator/main.go b/cmd/operator/main.go index b208d9578..c1ca82be2 100644 --- a/cmd/operator/main.go +++ b/cmd/operator/main.go @@ -128,6 +128,7 @@ var ( kubeletEndpoints bool kubeletEndpointSlice bool kubeletSyncPeriod time.Duration + kubeletHTTPMetrics bool featureGates = k8sflag.NewMapStringBool(ptr.To(map[string]bool{})) ) @@ -150,6 +151,7 @@ func parseFlags(fs *flag.FlagSet) { fs.BoolVar(&kubeletEndpointSlice, "kubelet-endpointslice", false, "Create EndpointSlice objects for kubelet targets.") fs.BoolVar(&kubeletEndpoints, "kubelet-endpoints", true, "Create Endpoints objects for kubelet targets.") fs.DurationVar(&kubeletSyncPeriod, "kubelet-sync-period", 3*time.Minute, "How often the operator reconciles the kubelet Endpoints and EndpointSlice objects (e.g., 10s, 2m, 1h30m).") + fs.BoolVar(&kubeletHTTPMetrics, "kubelet-http-metrics", true, "Include HTTP metrics port (10255) in kubelet service. Set to false if your cluster has disabled the insecure kubelet read-only port (e.g., GKE 1.32+).") // The Prometheus config reloader image is released along with the // Prometheus Operator image, tagged with the same semver version. Default to @@ -661,6 +663,7 @@ func start() int { opts := []kubelet.ControllerOption{ kubelet.WithNodeAddressPriority(nodeAddressPriority.String()), kubelet.WithSyncPeriod(kubeletSyncPeriod), + kubelet.WithHTTPMetrics(kubeletHTTPMetrics), } kubeletService := strings.Split(kubeletObject, "/") diff --git a/pkg/kubelet/controller.go b/pkg/kubelet/controller.go index 725ec0631..72b3d0547 100644 --- a/pkg/kubelet/controller.go +++ b/pkg/kubelet/controller.go @@ -74,6 +74,11 @@ type Controller struct { manageEndpointSlice bool manageEndpoints bool syncPeriod time.Duration + + // httpMetricsEnabled controls whether to include the insecure HTTP metrics + // port (10255) in the kubelet Service. Set to false when the cluster has + // disabled the insecure kubelet read-only port (e.g., GKE 1.32+). + httpMetricsEnabled bool } type ControllerOption func(*Controller) @@ -108,6 +113,16 @@ func WithSyncPeriod(d time.Duration) ControllerOption { } } +// WithHTTPMetrics controls whether to include the insecure HTTP metrics port +// (10255) in the kubelet Service. When disabled, only the secure HTTPS port +// (10250) and cAdvisor port (4194) are included. This is useful when the +// cluster has disabled the insecure kubelet read-only port (e.g., GKE 1.32+). +func WithHTTPMetrics(enabled bool) ControllerOption { + return func(c *Controller) { + c.httpMetricsEnabled = enabled + } +} + func New( logger *slog.Logger, kclient kubernetes.Interface, @@ -433,20 +448,7 @@ func (c *Controller) syncEndpoints(ctx context.Context, addresses []nodeAddress) Subsets: []v1.EndpointSubset{ { Addresses: make([]v1.EndpointAddress, len(addresses)), - Ports: []v1.EndpointPort{ - { - Name: httpsPortName, - Port: httpsPort, - }, - { - Name: httpPortName, - Port: httpPort, - }, - { - Name: cAdvisorPortName, - Port: cAdvisorPort, - }, - }, + Ports: c.endpointPorts(), }, }, } @@ -486,20 +488,7 @@ func (c *Controller) syncService(ctx context.Context) (*v1.Service, error) { Spec: v1.ServiceSpec{ Type: v1.ServiceTypeClusterIP, ClusterIP: v1.ClusterIPNone, - Ports: []v1.ServicePort{ - { - Name: httpsPortName, - Port: httpsPort, - }, - { - Name: httpPortName, - Port: httpPort, - }, - { - Name: cAdvisorPortName, - Port: cAdvisorPort, - }, - }, + Ports: c.servicePorts(), }, } @@ -626,20 +615,7 @@ func (c *Controller) syncEndpointSlice(ctx context.Context, svc *v1.Service, add }, }, }, - Ports: []discoveryv1.EndpointPort{ - { - Name: ptr.To(httpsPortName), - Port: ptr.To(httpsPort), - }, - { - Name: ptr.To(httpPortName), - Port: ptr.To(httpPort), - }, - { - Name: ptr.To(cAdvisorPortName), - Port: ptr.To(cAdvisorPort), - }, - }, + Ports: c.endpointSlicePorts(), } if a.ipv4 { @@ -688,3 +664,75 @@ func (c *Controller) syncEndpointSlice(ctx context.Context, svc *v1.Service, add func (c *Controller) fullCapacity(eps []discoveryv1.Endpoint) bool { return len(eps) >= c.maxEndpointsPerSlice } + +// servicePorts returns the list of ServicePort for the kubelet Service. +// If httpMetricsEnabled is false, the insecure HTTP port (10255) is excluded. +func (c *Controller) servicePorts() []v1.ServicePort { + ports := []v1.ServicePort{ + { + Name: httpsPortName, + Port: httpsPort, + }, + { + Name: cAdvisorPortName, + Port: cAdvisorPort, + }, + } + + if c.httpMetricsEnabled { + ports = append(ports, v1.ServicePort{ + Name: httpPortName, + Port: httpPort, + }) + } + + return ports +} + +// endpointPorts returns the list of EndpointPort for the kubelet Endpoints. +// If httpMetricsEnabled is false, the insecure HTTP port (10255) is excluded. +func (c *Controller) endpointPorts() []v1.EndpointPort { + ports := []v1.EndpointPort{ + { + Name: httpsPortName, + Port: httpsPort, + }, + { + Name: cAdvisorPortName, + Port: cAdvisorPort, + }, + } + + if c.httpMetricsEnabled { + ports = append(ports, v1.EndpointPort{ + Name: httpPortName, + Port: httpPort, + }) + } + + return ports +} + +// endpointSlicePorts returns the list of EndpointPort for the kubelet EndpointSlice. +// If httpMetricsEnabled is false, the insecure HTTP port (10255) is excluded. +func (c *Controller) endpointSlicePorts() []discoveryv1.EndpointPort { + ports := []discoveryv1.EndpointPort{ + { + Name: ptr.To(httpsPortName), + Port: ptr.To(httpsPort), + }, + { + Name: ptr.To(cAdvisorPortName), + Port: ptr.To(cAdvisorPort), + }, + } + + if c.httpMetricsEnabled { + ports = append(ports, discoveryv1.EndpointPort{ + Name: ptr.To(httpPortName), + Port: ptr.To(httpPort), + }) + } + + return ports +} diff --git a/pkg/kubelet/controller_test.go b/pkg/kubelet/controller_test.go index 3181b7604..3c1ac7f11 100644 --- a/pkg/kubelet/controller_test.go +++ b/pkg/kubelet/controller_test.go @@ -584,3 +584,73 @@ func newLogger() *slog.Logger { return l } + +func TestHTTPMetricsPorts(t *testing.T) { + for _, tc := range []struct { + name string + httpMetricsEnabled bool + expectedServicePorts int + expectedEndpointPorts int + expectHTTPMetricsPort bool + }{ + { + name: "HTTP metrics enabled (default)", + httpMetricsEnabled: true, + expectedServicePorts: 3, // https-metrics, http-metrics, cadvisor + expectedEndpointPorts: 3, + expectHTTPMetricsPort: true, + }, + { + name: "HTTP metrics disabled", + httpMetricsEnabled: false, + expectedServicePorts: 2, // https-metrics, cadvisor (no http-metrics) + expectedEndpointPorts: 2, + expectHTTPMetricsPort: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + c := &Controller{ + httpMetricsEnabled: tc.httpMetricsEnabled, + } + + // Test servicePorts + svcPorts := c.servicePorts() + require.Len(t, svcPorts, tc.expectedServicePorts, "unexpected number of service ports") + + hasHTTPPort := false + for _, p := range svcPorts { + if p.Name == httpPortName && p.Port == httpPort { + hasHTTPPort = true + break + } + } + require.Equal(t, tc.expectHTTPMetricsPort, hasHTTPPort, "http-metrics port presence mismatch in service ports") + + // Test endpointPorts + epPorts := c.endpointPorts() + require.Len(t, epPorts, tc.expectedEndpointPorts, "unexpected number of endpoint ports") + + hasHTTPPort = false + for _, p := range epPorts { + if p.Name == httpPortName && p.Port == httpPort { + hasHTTPPort = true + break + } + } + require.Equal(t, tc.expectHTTPMetricsPort, hasHTTPPort, "http-metrics port presence mismatch in endpoint ports") + + // Test endpointSlicePorts + epsPorts := c.endpointSlicePorts() + require.Len(t, epsPorts, tc.expectedEndpointPorts, "unexpected number of endpointslice ports") + + hasHTTPPort = false + for _, p := range epsPorts { + if p.Name != nil && *p.Name == httpPortName && p.Port != nil && *p.Port == httpPort { + hasHTTPPort = true + break + } + } + require.Equal(t, tc.expectHTTPMetricsPort, hasHTTPPort, "http-metrics port presence mismatch in endpointslice ports") + }) + } +}