diff --git a/vertical-pod-autoscaler/pkg/admission-controller/main.go b/vertical-pod-autoscaler/pkg/admission-controller/main.go index 113621619f4..f9d4d824eb1 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/main.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/main.go @@ -42,6 +42,7 @@ import ( "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/limitrange" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics" metrics_admission "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/admission" + "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/server" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/status" vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa" ) @@ -70,6 +71,7 @@ var ( kubeconfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") kubeApiQps = flag.Float64("kube-api-qps", 5.0, `QPS limit when making requests to Kubernetes apiserver`) kubeApiBurst = flag.Float64("kube-api-burst", 10.0, `QPS burst limit when making requests to Kubernetes apiserver`) + enableProfiling = flag.Bool("profiling", false, "Is debug/pprof endpoint enabled") namespace = os.Getenv("NAMESPACE") serviceName = flag.String("webhook-service", "vpa-webhook", "Kubernetes service under which webhook is registered. Used when registerByURL is set to false.") webhookAddress = flag.String("webhook-address", "", "Address under which webhook is registered. Used when registerByURL is set to true.") @@ -92,8 +94,8 @@ func main() { } healthCheck := metrics.NewHealthCheck(time.Minute) - metrics.Initialize(*address, healthCheck) metrics_admission.Register() + server.Initialize(enableProfiling, healthCheck, address) config := common.CreateKubeConfigOrDie(*kubeconfig, float32(*kubeApiQps), int(*kubeApiBurst)) diff --git a/vertical-pod-autoscaler/pkg/recommender/main.go b/vertical-pod-autoscaler/pkg/recommender/main.go index 7a73d5816d7..689a2874dff 100644 --- a/vertical-pod-autoscaler/pkg/recommender/main.go +++ b/vertical-pod-autoscaler/pkg/recommender/main.go @@ -51,6 +51,7 @@ import ( "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics" metrics_quality "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/quality" metrics_recommender "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/recommender" + "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/server" vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa" ) @@ -64,6 +65,7 @@ var ( kubeconfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") kubeApiQps = flag.Float64("kube-api-qps", 5.0, `QPS limit when making requests to Kubernetes apiserver`) kubeApiBurst = flag.Float64("kube-api-burst", 10.0, `QPS burst limit when making requests to Kubernetes apiserver`) + enableProfiling = flag.Bool("profiling", false, "Is debug/pprof endpoint enabled") storage = flag.String("storage", "", `Specifies storage mode. Supported values: prometheus, checkpoint (default)`) // prometheus history provider configs @@ -128,9 +130,9 @@ func main() { } healthCheck := metrics.NewHealthCheck(*metricsFetcherInterval * 5) - metrics.Initialize(*address, healthCheck) metrics_recommender.Register() metrics_quality.Register() + server.Initialize(enableProfiling, healthCheck, address) if !leaderElection.LeaderElect { run(healthCheck) diff --git a/vertical-pod-autoscaler/pkg/updater/main.go b/vertical-pod-autoscaler/pkg/updater/main.go index 3a72faad860..771ebd7e60f 100644 --- a/vertical-pod-autoscaler/pkg/updater/main.go +++ b/vertical-pod-autoscaler/pkg/updater/main.go @@ -45,6 +45,7 @@ import ( "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/limitrange" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics" metrics_updater "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/updater" + "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/server" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/status" vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa" ) @@ -65,10 +66,11 @@ var ( evictionRateBurst = flag.Int("eviction-rate-burst", 1, `Burst of pods that can be evicted.`) - address = flag.String("address", ":8943", "The address to expose Prometheus metrics.") - kubeconfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") - kubeApiQps = flag.Float64("kube-api-qps", 5.0, `QPS limit when making requests to Kubernetes apiserver`) - kubeApiBurst = flag.Float64("kube-api-burst", 10.0, `QPS burst limit when making requests to Kubernetes apiserver`) + address = flag.String("address", ":8943", "The address to expose Prometheus metrics.") + kubeconfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") + kubeApiQps = flag.Float64("kube-api-qps", 5.0, `QPS limit when making requests to Kubernetes apiserver`) + kubeApiBurst = flag.Float64("kube-api-burst", 10.0, `QPS burst limit when making requests to Kubernetes apiserver`) + enableProfiling = flag.Bool("profiling", false, "Is debug/pprof endpoint enabled") useAdmissionControllerStatus = flag.Bool("use-admission-controller-status", true, "If true, updater will only evict pods when admission controller status is valid.") @@ -99,7 +101,8 @@ func main() { } healthCheck := metrics.NewHealthCheck(*updaterInterval * 5) - metrics.Initialize(*address, healthCheck) + server.Initialize(enableProfiling, healthCheck, address) + metrics_updater.Register() if !leaderElection.LeaderElect { diff --git a/vertical-pod-autoscaler/pkg/utils/metrics/metrics.go b/vertical-pod-autoscaler/pkg/utils/metrics/metrics.go index fa8329025aa..cafb58716d7 100644 --- a/vertical-pod-autoscaler/pkg/utils/metrics/metrics.go +++ b/vertical-pod-autoscaler/pkg/utils/metrics/metrics.go @@ -19,14 +19,11 @@ package metrics import ( "math" - "net/http" "time" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" _ "k8s.io/component-base/metrics/prometheus/restclient" // for client-go metrics registration - "k8s.io/klog/v2" ) // ExecutionTimer measures execution time of a computation, split into major steps @@ -46,18 +43,6 @@ const ( MaxVpaSizeLog = 20 ) -// Initialize sets up Prometheus to expose metrics & (optionally) health-check on the given address -func Initialize(address string, healthCheck *HealthCheck) { - go func() { - http.Handle("/metrics", promhttp.Handler()) - if healthCheck != nil { - http.Handle("/health-check", healthCheck) - } - err := http.ListenAndServe(address, nil) - klog.Fatalf("Failed to start metrics: %v", err) - }() -} - // NewExecutionTimer provides a timer for admission latency; call ObserveXXX() on it to measure func NewExecutionTimer(histo *prometheus.HistogramVec) *ExecutionTimer { now := time.Now() diff --git a/vertical-pod-autoscaler/pkg/utils/server/server.go b/vertical-pod-autoscaler/pkg/utils/server/server.go new file mode 100644 index 00000000000..fc1fcce255f --- /dev/null +++ b/vertical-pod-autoscaler/pkg/utils/server/server.go @@ -0,0 +1,51 @@ +/* +Copyright 2024 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 server - common code for mux of all 3 VPA components +package server + +import ( + "net/http" + "net/http/pprof" + + "k8s.io/klog/v2" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics" +) + +// Initialize sets up Prometheus to expose metrics & (optionally) health-check and profiling on the given address +func Initialize(enableProfiling *bool, healthCheck *metrics.HealthCheck, address *string) { + go func() { + mux := http.NewServeMux() + + mux.Handle("/metrics", promhttp.Handler()) + if healthCheck != nil { + mux.Handle("/health-check", healthCheck) + } + + if *enableProfiling { + mux.HandleFunc("/debug/pprof/", http.HandlerFunc(pprof.Index)) + mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + mux.HandleFunc("/debug/pprof/profile", pprof.Profile) + mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + mux.HandleFunc("/debug/pprof/trace", pprof.Trace) + } + + err := http.ListenAndServe(*address, mux) + klog.Fatalf("Failed to start metrics: %v", err) + }() +}