diff --git a/.github/workflows/linkinator.yaml b/.github/workflows/linkinator.yaml index 8f8f373..e9e902e 100644 --- a/.github/workflows/linkinator.yaml +++ b/.github/workflows/linkinator.yaml @@ -21,5 +21,6 @@ jobs: with: paths: "**/*.md" markdown: true + concurrency: 1 retry: true linksToSkip: "https://github.com/kedacore/http-add-on/pkgs/container/http-add-on-interceptor, https://github.com/kedacore/http-add-on/pkgs/container/http-add-on-operator, https://github.com/kedacore/http-add-on/pkgs/container/http-add-on-scaler,http://opentelemetry-collector.open-telemetry-system:4318,http://opentelemetry-collector.open-telemetry-system:4318/v1/traces, https://www.gnu.org/software/make/" diff --git a/.golangci.yml b/.golangci.yml index d60126d..b7a4f01 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -26,7 +26,7 @@ linters: - unconvert - ineffassign - staticcheck - - exportloopref + - copyloopvar #- depguard #https://github.com/kedacore/keda/issues/4980 - dogsled - errcheck diff --git a/CHANGELOG.md b/CHANGELOG.md index 11eda45..a2ee74c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ This changelog keeps track of work items that have been completed and are ready - **General**: Add configurable tracing support to the interceptor proxy ([#1021](https://github.com/kedacore/http-add-on/pull/1021)) - **General**: Allow using HSO and SO with different names ([#1293](https://github.com/kedacore/http-add-on/issues/1293)) +- **General**: Support profiling for KEDA components ([#4789](https://github.com/kedacore/keda/issues/4789)) ### Improvements diff --git a/interceptor/config/serving.go b/interceptor/config/serving.go index 88c75e9..3dfb0c5 100644 --- a/interceptor/config/serving.go +++ b/interceptor/config/serving.go @@ -47,6 +47,8 @@ type Serving struct { TLSSkipVerify bool `envconfig:"KEDA_HTTP_PROXY_TLS_SKIP_VERIFY" default:"false"` // TLSPort is the port that the server should serve on if TLS is enabled TLSPort int `envconfig:"KEDA_HTTP_PROXY_TLS_PORT" default:"8443"` + // ProfilingAddr if not empty, pprof will be available on this address, assuming host:port here + ProfilingAddr string `envconfig:"PROFILING_BIND_ADDRESS" default:""` } // Parse parses standard configs using envconfig and returns a pointer to the diff --git a/interceptor/main.go b/interceptor/main.go index 483f70d..3042072 100644 --- a/interceptor/main.go +++ b/interceptor/main.go @@ -8,6 +8,7 @@ import ( "flag" "fmt" "net/http" + _ "net/http/pprof" "os" "path/filepath" "runtime" @@ -81,6 +82,7 @@ func main() { proxyPort := servingCfg.ProxyPort adminPort := servingCfg.AdminPort proxyTLSEnabled := servingCfg.ProxyTLSEnabled + profilingAddr := servingCfg.ProfilingAddr // setup the configured metrics collectors metrics.NewMetricsCollectors(metricsCfg) @@ -218,6 +220,13 @@ func main() { return nil }) + if len(profilingAddr) > 0 { + eg.Go(func() error { + setupLog.Info("enabling pprof for profiling", "address", profilingAddr) + return http.ListenAndServe(profilingAddr, nil) + }) + } + build.PrintComponentInfo(ctrl.Log, "Interceptor") if err := eg.Wait(); err != nil && !errors.Is(err, context.Canceled) { diff --git a/operator/main.go b/operator/main.go index 3c29812..4844a26 100644 --- a/operator/main.go +++ b/operator/main.go @@ -57,11 +57,13 @@ func main() { var metricsAddr string var enableLeaderElection bool var probeAddr string + var profilingAddr string flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") flag.BoolVar(&enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") + flag.StringVar(&profilingAddr, "profiling-bind-address", "", "The address the profiling would be exposed on.") opts := zap.Options{ Development: true, } @@ -96,6 +98,7 @@ func main() { Metrics: server.Options{ BindAddress: metricsAddr, }, + PprofBindAddress: profilingAddr, HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, LeaderElectionID: "http-add-on.keda.sh", diff --git a/pkg/k8s/endpoints_test.go b/pkg/k8s/endpoints_test.go index 0faebd3..846822f 100644 --- a/pkg/k8s/endpoints_test.go +++ b/pkg/k8s/endpoints_test.go @@ -57,7 +57,6 @@ func TestGetEndpoints(t *testing.T) { addrLookup := map[string]*v1.EndpointAddress{} for _, subset := range endpoints.Subsets { for _, addr := range subset.Addresses { - addr := addr key := fmt.Sprintf("http://%s:%s", addr.IP, svcPort) addrLookup[key] = &addr } diff --git a/pkg/routing/table_test.go b/pkg/routing/table_test.go index e0bbf47..f87df76 100644 --- a/pkg/routing/table_test.go +++ b/pkg/routing/table_test.go @@ -190,8 +190,6 @@ var _ = Describe("Table", func() { defer cancel() for _, httpso := range httpsoList.Items { - httpso := httpso - key := *k8s.NamespacedNameFromObject(&httpso) t.httpScaledObjects[key] = &httpso } @@ -216,8 +214,6 @@ var _ = Describe("Table", func() { defer cancel() for _, httpso := range httpsoList.Items { - httpso := httpso - key := *k8s.NamespacedNameFromObject(&httpso) t.httpScaledObjects[key] = &httpso } @@ -285,8 +281,6 @@ var _ = Describe("Table", func() { It("returns new memory based on HTTPSOs", func() { for _, httpso := range httpsoList.Items { - httpso := httpso - key := *k8s.NamespacedNameFromObject(&httpso) t.httpScaledObjects[key] = &httpso } diff --git a/pkg/routing/tablememory_test.go b/pkg/routing/tablememory_test.go index 79eaac0..e2ec462 100644 --- a/pkg/routing/tablememory_test.go +++ b/pkg/routing/tablememory_test.go @@ -484,8 +484,6 @@ var _ = Describe("TableMemory", func() { store: iradix.New[*httpv1alpha1.HTTPScaledObject](), } for _, httpso := range httpsoList.Items { - httpso := httpso - tm = insertTrees(tm, &httpso) } diff --git a/scaler/config.go b/scaler/config.go index cce3f2e..fe1e968 100644 --- a/scaler/config.go +++ b/scaler/config.go @@ -34,6 +34,8 @@ type config struct { DeploymentCacheRsyncPeriod time.Duration `envconfig:"KEDA_HTTP_SCALER_DEPLOYMENT_INFORMER_RSYNC_PERIOD" default:"60m"` // QueueTickDuration is the duration between queue requests QueueTickDuration time.Duration `envconfig:"KEDA_HTTP_QUEUE_TICK_DURATION" default:"500ms"` + // ProfilingAddr if not empty, pprof will be available on this address, assuming host:port here + ProfilingAddr string `envconfig:"PROFILING_BIND_ADDRESS" default:""` } func mustParseConfig() *config { diff --git a/scaler/main.go b/scaler/main.go index c7d9a6b..154dda7 100644 --- a/scaler/main.go +++ b/scaler/main.go @@ -10,6 +10,8 @@ import ( "flag" "fmt" "net" + "net/http" + _ "net/http/pprof" "os" "time" @@ -47,6 +49,7 @@ func main() { deplName := cfg.TargetDeployment targetPortStr := fmt.Sprintf("%d", cfg.TargetPort) targetPendingRequests := cfg.TargetPendingRequests + profilingAddr := cfg.ProfilingAddr opts := zap.Options{} opts.BindFlags(flag.CommandLine) @@ -113,6 +116,13 @@ func main() { return nil }) + if len(profilingAddr) > 0 { + eg.Go(func() error { + setupLog.Info("enabling pprof for profiling", "address", profilingAddr) + return http.ListenAndServe(profilingAddr, nil) + }) + } + eg.Go(func() error { setupLog.Info("starting the grpc server")