From 8317ac5b9a79b10ee82688d471e0127356045c96 Mon Sep 17 00:00:00 2001 From: William Petit Date: Mon, 23 Sep 2024 10:12:42 +0200 Subject: [PATCH] feat: add configurable profiling endpoints (#38) --- doc/README.md | 1 + doc/fr/tutorials/profiling.md | 20 +++++++++++++++++--- internal/admin/server.go | 29 +++++++++++++++++++++++++++++ internal/config/admin_server.go | 22 ++++++++++++---------- internal/config/profiling.go | 15 +++++++++++++++ internal/config/proxy_server.go | 2 ++ internal/config/sentry.go | 4 ++-- internal/proxy/server.go | 29 +++++++++++++++++++++++++++++ misc/packaging/common/config.yml | 28 +++++++++++++++++++++++++++- 9 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 internal/config/profiling.go diff --git a/doc/README.md b/doc/README.md index 996d2a4..dc05327 100644 --- a/doc/README.md +++ b/doc/README.md @@ -24,6 +24,7 @@ - [(FR) - Ajouter une authentification OpenID Connect](./fr/tutorials/add-oidc-authn-layer.md) - [(FR) - Amorçage d'un serveur Bouncer via la configuration](./fr/tutorials/bootstrapping.md) - [(FR) - Intégration avec Kubernetes](./fr/tutorials/kubernetes-integration.md) +- [(FR) - Profilage](./fr/tutorials/profiling.md) ### Développement diff --git a/doc/fr/tutorials/profiling.md b/doc/fr/tutorials/profiling.md index 906ca42..5a5a153 100644 --- a/doc/fr/tutorials/profiling.md +++ b/doc/fr/tutorials/profiling.md @@ -1,10 +1,24 @@ # Étudier les performances de Bouncer +## In situ + +Il est possible d'activer via la configuration de Bouncer de endpoints capable de générer des fichiers de profil au format [`pprof`](https://github.com/google/pprof). Par défaut, le point d'entrée est `.bouncer/profiling` (l'activation et la personnalisation de ce point d'entrée sont modifiables via la [configuration](../../../misc/packaging/common/config.yml)). + +**Exemple:** Visualiser l'utilisation mémoire de Bouncer + +```bash +go tool pprof -web http:///.bouncer/profiling/heap +``` + +L'ensemble des profils disponibles sont visibles à l'adresse `http:///.bouncer/profiling`. + +## En développement + Le package `./internal` est dédié à l'étude des performances de Bouncer. Il contient une suite de benchmarks simulant de proxies avec différentes configurations de layers afin d'évaluer les points d'engorgement sur le traitement des requêtes. Voir le répertoire `./internal/bench/testdata/proxies` pour voir les différentes configurations de cas. -## Lancer les benchmarks +### Lancer les benchmarks Le plus simple est d'utiliser la commande `make bench` qui exécutera séquentiellement tous les benchmarks. Il est également possible de lancer un benchmark spécifique via la commande suivante: @@ -19,7 +33,7 @@ Par exemple: go test -bench='BenchmarkProxies/basic-auth' -run='^$' ./internal/bench ``` -## Visualiser les profils d'exécution +### Visualiser les profils d'exécution Vous pouvez visualiser les profils d'exécution via la commande suivante: @@ -35,7 +49,7 @@ Par exemple: go tool pprof -web ./internal/bench/testdata/proxies/basic-auth.prof ``` -## Comparer les évolutions +### Comparer les évolutions ```bash # Lancer un premier benchmark diff --git a/internal/admin/server.go b/internal/admin/server.go index a4873b5..10000d3 100644 --- a/internal/admin/server.go +++ b/internal/admin/server.go @@ -6,6 +6,7 @@ import ( "log" "net" "net/http" + "net/http/pprof" "forge.cadoles.com/cadoles/bouncer/internal/auth" "forge.cadoles.com/cadoles/bouncer/internal/auth/jwt" @@ -155,6 +156,34 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e }) } + if s.serverConfig.Profiling.Enabled { + profiling := s.serverConfig.Profiling + logger.Info(ctx, "enabling profiling", logger.F("endpoint", profiling.Endpoint)) + + router.Group(func(r chi.Router) { + if profiling.BasicAuth != nil { + logger.Info(ctx, "enabling authentication on metrics endpoint") + + r.Use(middleware.BasicAuth( + "profiling", + profiling.BasicAuth.CredentialsMap(), + )) + } + + r.Route(string(profiling.Endpoint), func(r chi.Router) { + r.HandleFunc("/", pprof.Index) + r.HandleFunc("/cmdline", pprof.Cmdline) + r.HandleFunc("/profile", pprof.Profile) + r.HandleFunc("/symbol", pprof.Symbol) + r.HandleFunc("/trace", pprof.Trace) + r.HandleFunc("/{name}", func(w http.ResponseWriter, r *http.Request) { + name := chi.URLParam(r, "name") + pprof.Handler(name).ServeHTTP(w, r) + }) + }) + }) + } + router.Route("/api/v1", func(r chi.Router) { r.Group(func(r chi.Router) { r.Use(auth.Middleware( diff --git a/internal/config/admin_server.go b/internal/config/admin_server.go index dbcbda0..4b4cc78 100644 --- a/internal/config/admin_server.go +++ b/internal/config/admin_server.go @@ -1,20 +1,22 @@ package config type AdminServerConfig struct { - HTTP HTTPConfig `yaml:"http"` - CORS CORSConfig `yaml:"cors"` - Auth AuthConfig `yaml:"auth"` - Metrics MetricsConfig `yaml:"metrics"` - Sentry SentryConfig `yaml:"sentry"` + HTTP HTTPConfig `yaml:"http"` + CORS CORSConfig `yaml:"cors"` + Auth AuthConfig `yaml:"auth"` + Metrics MetricsConfig `yaml:"metrics"` + Profiling ProfilingConfig `yaml:"profiling"` + Sentry SentryConfig `yaml:"sentry"` } func NewDefaultAdminServerConfig() AdminServerConfig { return AdminServerConfig{ - HTTP: NewHTTPConfig("127.0.0.1", 8081), - CORS: NewDefaultCORSConfig(), - Auth: NewDefaultAuthConfig(), - Metrics: NewDefaultMetricsConfig(), - Sentry: NewDefaultSentryConfig(), + HTTP: NewHTTPConfig("127.0.0.1", 8081), + CORS: NewDefaultCORSConfig(), + Auth: NewDefaultAuthConfig(), + Metrics: NewDefaultMetricsConfig(), + Sentry: NewDefaultSentryConfig(), + Profiling: NewDefaultProfilingConfig(), } } diff --git a/internal/config/profiling.go b/internal/config/profiling.go new file mode 100644 index 0000000..21ab972 --- /dev/null +++ b/internal/config/profiling.go @@ -0,0 +1,15 @@ +package config + +type ProfilingConfig struct { + Enabled InterpolatedBool `yaml:"enabled"` + Endpoint InterpolatedString `yaml:"endpoint"` + BasicAuth *BasicAuthConfig `yaml:"basicAuth"` +} + +func NewDefaultProfilingConfig() ProfilingConfig { + return ProfilingConfig{ + Enabled: true, + Endpoint: "/.bouncer/profiling", + BasicAuth: nil, + } +} diff --git a/internal/config/proxy_server.go b/internal/config/proxy_server.go index 104ba28..51b31e5 100644 --- a/internal/config/proxy_server.go +++ b/internal/config/proxy_server.go @@ -10,6 +10,7 @@ type ProxyServerConfig struct { Debug InterpolatedBool `yaml:"debug"` HTTP HTTPConfig `yaml:"http"` Metrics MetricsConfig `yaml:"metrics"` + Profiling ProfilingConfig `yaml:"profiling"` Transport TransportConfig `yaml:"transport"` Dial DialConfig `yaml:"dial"` Sentry SentryConfig `yaml:"sentry"` @@ -27,6 +28,7 @@ func NewDefaultProxyServerConfig() ProxyServerConfig { Sentry: NewDefaultSentryConfig(), Cache: NewDefaultCacheConfig(), Templates: NewDefaultTemplatesConfig(), + Profiling: NewDefaultProfilingConfig(), } } diff --git a/internal/config/sentry.go b/internal/config/sentry.go index 5da2cdf..60c2236 100644 --- a/internal/config/sentry.go +++ b/internal/config/sentry.go @@ -28,10 +28,10 @@ func NewDefaultSentryConfig() SentryConfig { Debug: false, FlushTimeout: NewInterpolatedDuration(2 * time.Second), AttachStacktrace: true, - SampleRate: 1, + SampleRate: 0.2, EnableTracing: true, TracesSampleRate: 0.2, - ProfilesSampleRate: 1, + ProfilesSampleRate: 0.2, IgnoreErrors: []string{}, SendDefaultPII: false, ServerName: "", diff --git a/internal/proxy/server.go b/internal/proxy/server.go index fec04f9..6d8dece 100644 --- a/internal/proxy/server.go +++ b/internal/proxy/server.go @@ -8,6 +8,7 @@ import ( "net" "net/http" "net/http/httputil" + "net/http/pprof" "net/url" "path/filepath" "strconv" @@ -146,6 +147,34 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e }) } + if s.serverConfig.Profiling.Enabled { + profiling := s.serverConfig.Profiling + logger.Info(ctx, "enabling profiling", logger.F("endpoint", profiling.Endpoint)) + + router.Group(func(r chi.Router) { + if profiling.BasicAuth != nil { + logger.Info(ctx, "enabling authentication on metrics endpoint") + + r.Use(middleware.BasicAuth( + "profiling", + profiling.BasicAuth.CredentialsMap(), + )) + } + + r.Route(string(profiling.Endpoint), func(r chi.Router) { + r.HandleFunc("/", pprof.Index) + r.HandleFunc("/cmdline", pprof.Cmdline) + r.HandleFunc("/profile", pprof.Profile) + r.HandleFunc("/symbol", pprof.Symbol) + r.HandleFunc("/trace", pprof.Trace) + r.HandleFunc("/{name}", func(w http.ResponseWriter, r *http.Request) { + name := chi.URLParam(r, "name") + pprof.Handler(name).ServeHTTP(w, r) + }) + }) + }) + } + router.Group(func(r chi.Router) { r.Use(director.Middleware()) diff --git a/misc/packaging/common/config.yml b/misc/packaging/common/config.yml index 9ca6e0d..1c1559c 100644 --- a/misc/packaging/common/config.yml +++ b/misc/packaging/common/config.yml @@ -49,6 +49,19 @@ admin: # Mettre à null pour désactiver l'authentification basicAuth: null + # Profiling + profiling: + # Activer ou désactiver les endpoints de profiling + enabled: true + # Route de publication des endpoints de profiling + endpoint: /.bouncer/profiling + # Authentification "basic auth" sur les endpoints + # de profiling + # Mettre à null pour désactiver l'authentification + basicAuth: + credentials: + prof: iling + # Configuration de l'intégration Sentry # Voir https://pkg.go.dev/github.com/getsentry/sentry-go?utm_source=godoc#ClientOptions sentry: @@ -59,7 +72,7 @@ admin: sampleRate: 1 enableTracing: true tracesSampleRate: 0.2 - profilesSampleRate: 1 + profilesSampleRate: 0.2 ignoreErrors: [] sendDefaultPII: false serverName: "" @@ -99,6 +112,19 @@ proxy: credentials: prom: etheus + # Profiling + profiling: + # Activer ou désactiver les endpoints de profiling + enabled: true + # Route de publication des endpoints de profiling + endpoint: /.bouncer/profiling + # Authentification "basic auth" sur les endpoints + # de profiling + # Mettre à null pour désactiver l'authentification + basicAuth: + credentials: + prof: iling + # Configuration de la mise en cache # locale des données proxy/layers cache: