Skip to content

Commit 6963125

Browse files
authored
chore: optionally enables pprof on the controller (#1406)
**Description** This also enables pprof server optionally on the controller in addition to extproc. **Related Issues/PRs (if applicable)** Follow up on #1395 --------- Signed-off-by: Takeshi Yoneda <[email protected]>
1 parent d8de326 commit 6963125

File tree

6 files changed

+112
-27
lines changed

6 files changed

+112
-27
lines changed

cmd/controller/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"github.com/envoyproxy/ai-gateway/internal/controller"
3434
"github.com/envoyproxy/ai-gateway/internal/extensionserver"
3535
"github.com/envoyproxy/ai-gateway/internal/internalapi"
36+
"github.com/envoyproxy/ai-gateway/internal/pprof"
3637
)
3738

3839
type flags struct {
@@ -306,6 +307,7 @@ func main() {
306307
setupLog.Info("configuring kubernetes cache", "watch-namespaces", parsedFlags.watchNamespaces, "sync-timeout", parsedFlags.cacheSyncTimeout)
307308

308309
ctx := ctrl.SetupSignalHandler()
310+
pprof.Run(ctx)
309311
mgrOpts := ctrl.Options{
310312
Cache: setupCache(parsedFlags),
311313
Controller: config.Controller{CacheSyncTimeout: parsedFlags.cacheSyncTimeout},

cmd/extproc/main.go

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,21 @@ package main
77

88
import (
99
"context"
10-
"errors"
1110
"log"
12-
"net/http"
13-
"net/http/pprof"
1411
"os"
1512
"os/signal"
1613
"syscall"
1714
"time"
1815

1916
"github.com/envoyproxy/ai-gateway/cmd/extproc/mainlib"
17+
"github.com/envoyproxy/ai-gateway/internal/pprof"
2018
)
2119

2220
func main() {
23-
// Run the pprof server if the DISABLE_PPROF environment variable is not set.
24-
//
25-
// Enabling the pprof server by default helps with debugging performance issues in production.
26-
// The impact should be negligible when the actual pprof endpoints are not being accessed.
27-
if _, ok := os.LookupEnv("DISABLE_PPROF"); !ok {
28-
go func() {
29-
const pprofPort = "6060" // The same default port as in th Go pprof documentation.
30-
mux := http.NewServeMux()
31-
mux.HandleFunc("/debug/pprof/", pprof.Index)
32-
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
33-
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
34-
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
35-
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
36-
server := &http.Server{Addr: ":" + pprofPort, Handler: mux, ReadHeaderTimeout: 5 * time.Second}
37-
log.Printf("starting pprof server on port %s", pprofPort)
38-
if err := server.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) {
39-
log.Printf("pprof server stopped: %v", err)
40-
}
41-
}()
42-
}
43-
4421
ctx, cancel := context.WithCancel(context.Background())
4522
signalsChan := make(chan os.Signal, 1)
4623
signal.Notify(signalsChan, syscall.SIGINT, syscall.SIGTERM)
24+
pprof.Run(ctx)
4725
go func() {
4826
<-signalsChan
4927
log.Printf("signal received, shutting down...")

internal/pprof/pprof.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright Envoy AI Gateway Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
// The full text of the Apache license is available in the LICENSE file at
4+
// the root of the repo.
5+
6+
package pprof
7+
8+
import (
9+
"context"
10+
"errors"
11+
"log"
12+
"net/http"
13+
"net/http/pprof"
14+
"os"
15+
"time"
16+
)
17+
18+
const (
19+
pprofPort = "6060" // The same default port as in th Go pprof documentation.
20+
// DisableEnvVarKey is the environment variable name to disable the pprof server.
21+
// If this environment variable is set to any value, the pprof server will not be started.
22+
DisableEnvVarKey = "DISABLE_PPROF"
23+
)
24+
25+
// Run the pprof server if the DISABLE_PPROF environment variable is not set.
26+
// This is non-blocking and will run the pprof server in a separate goroutine until the provided context is cancelled.
27+
//
28+
// Enabling the pprof server by default helps with debugging performance issues in production.
29+
// The impact should be negligible when the actual pprof endpoints are not being accessed.
30+
func Run(ctx context.Context) {
31+
if _, ok := os.LookupEnv(DisableEnvVarKey); !ok {
32+
mux := http.NewServeMux()
33+
mux.HandleFunc("/debug/pprof/", pprof.Index)
34+
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
35+
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
36+
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
37+
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
38+
server := &http.Server{Addr: ":" + pprofPort, Handler: mux, ReadHeaderTimeout: 5 * time.Second}
39+
go func() {
40+
log.Printf("starting pprof server on port %s", pprofPort)
41+
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
42+
log.Printf("pprof server stopped: %v", err)
43+
}
44+
}()
45+
go func() {
46+
<-ctx.Done()
47+
log.Printf("shutting down pprof server...")
48+
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
49+
defer cancel()
50+
if err := server.Shutdown(shutdownCtx); err != nil {
51+
log.Printf("error shutting down pprof server: %v", err)
52+
} else {
53+
log.Print("pprof server shut down gracefully")
54+
}
55+
}()
56+
}
57+
}

internal/pprof/pprof_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright Envoy AI Gateway Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
// The full text of the Apache license is available in the LICENSE file at
4+
// the root of the repo.
5+
6+
package pprof
7+
8+
import (
9+
"context"
10+
"io"
11+
"net/http"
12+
"testing"
13+
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
func TestRun_disabled(t *testing.T) {
18+
t.Setenv(DisableEnvVarKey, "anything")
19+
ctx, cancel := context.WithCancel(context.Background())
20+
Run(ctx)
21+
// Try accessing the pprof server here if needed.
22+
response, err := http.Get("http://localhost:6060/debug/pprof/") //nolint:bodyclose
23+
require.Error(t, err)
24+
require.Nil(t, response)
25+
cancel()
26+
}
27+
28+
func TestRun_enabled(t *testing.T) {
29+
ctx, cancel := context.WithCancel(context.Background())
30+
Run(ctx)
31+
// Try accessing the pprof server here if needed.
32+
resp, err := http.Get("http://localhost:6060/debug/pprof/cmdline")
33+
require.NoError(t, err)
34+
defer func() {
35+
require.NoError(t, resp.Body.Close())
36+
}()
37+
require.NotNil(t, resp)
38+
body, err := io.ReadAll(resp.Body)
39+
require.NoError(t, err)
40+
require.Contains(t, string(body),
41+
// Test binary name should be present in the cmdline output.
42+
"pprof.test")
43+
cancel()
44+
}

manifests/charts/ai-gateway-helm/values.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,10 @@ controller:
107107
## @param controller.extraEnvVars Array with extra environment variables to add to controller containers
108108
## e.g:
109109
## extraEnvVars:
110-
## - name: FOO
111-
## value: "bar"
112-
##
110+
## # This disables pprof endpoint at "localhost:6060/debug/pprof" that can be accessed via port-forwarding for profiling.
111+
## # Enabled by default for troubleshooting purposes given the impact is negligible when not in use.
112+
## - name: DISABLE_PPROF
113+
## value: "true"
113114
extraEnvVars: []
114115

115116
# Example of volumes

tests/internal/testenvironment/test_environment.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"golang.org/x/sync/errgroup"
2525

2626
"github.com/envoyproxy/ai-gateway/cmd/extproc/mainlib"
27+
"github.com/envoyproxy/ai-gateway/internal/pprof"
2728
internaltesting "github.com/envoyproxy/ai-gateway/internal/testing"
2829
testsinternal "github.com/envoyproxy/ai-gateway/tests/internal"
2930
)
@@ -313,6 +314,8 @@ func requireExtProc(t testing.TB, out io.Writer, bin, config string, env []strin
313314
"-mcpWriteTimeout", mcpWriteTimeout.String(),
314315
"-logLevel", "info",
315316
}
317+
// Disable pprof for tests to avoid port conflicts.
318+
env = append(env, fmt.Sprintf("%s=true", pprof.DisableEnvVarKey))
316319
t.Logf("Starting ExtProc with args: %v", args)
317320
if inProcess {
318321
go func() {

0 commit comments

Comments
 (0)