Pyroscope client timeout and default upload rate (#56584)

* add pyroscope timeout and default upload rate

Signed-off-by: Evan Freed <evan.freed@goteleport.com>

* fix logging

Signed-off-by: Evan Freed <evan.freed@goteleport.com>

* fix lint

Signed-off-by: Evan Freed <evan.freed@goteleport.com>

* Update lib/service/pyroscope.go

Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com>

* Update lib/service/pyroscope.go

Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com>

* Update lib/service/pyroscope.go

Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com>

* Update lib/service/pyroscope.go

Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com>

* Update lib/service/pyroscope.go

Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com>

* Update lib/service/pyroscope.go

Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com>

* Update lib/service/pyroscope.go

Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com>

* review updates

Signed-off-by: Evan Freed <evan.freed@goteleport.com>

* fmt

Signed-off-by: Evan Freed <evan.freed@goteleport.com>

---------

Signed-off-by: Evan Freed <evan.freed@goteleport.com>
Co-authored-by: rosstimothy <39066650+rosstimothy@users.noreply.github.com>
This commit is contained in:
Evan Freed
2025-07-11 14:41:51 +00:00
committed by GitHub
co-authored by rosstimothy
parent d39b48022c
commit f3df6cbc42
7 changed files with 58 additions and 19 deletions
+1 -1
View File
@@ -139,7 +139,7 @@ require (
github.com/google/uuid v1.6.0
github.com/googleapis/gax-go/v2 v2.14.2
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
github.com/grafana/pyroscope-go v1.2.2
github.com/grafana/pyroscope-go v1.2.3-0.20250624101038-0410c20e9693
github.com/gravitational/license v0.0.0-20250329001817-070456fa8ec1
github.com/gravitational/roundtrip v1.0.2
github.com/gravitational/teleport/api v0.0.0
+2 -2
View File
@@ -1533,8 +1533,8 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY=
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
github.com/grafana/pyroscope-go v1.2.2 h1:uvKCyZMD724RkaCEMrSTC38Yn7AnFe8S2wiAIYdDPCE=
github.com/grafana/pyroscope-go v1.2.2/go.mod h1:zzT9QXQAp2Iz2ZdS216UiV8y9uXJYQiGE1q8v1FyhqU=
github.com/grafana/pyroscope-go v1.2.3-0.20250624101038-0410c20e9693 h1:oAnuX7YdawNRJN9jAJqs4uWfv11R+9sgBBMalTp2Xz0=
github.com/grafana/pyroscope-go v1.2.3-0.20250624101038-0410c20e9693/go.mod h1:zzT9QXQAp2Iz2ZdS216UiV8y9uXJYQiGE1q8v1FyhqU=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/gravitational/go-cassandra-native-protocol v0.0.0-teleport.1 h1:zGsdDzqXSuXI+1t+2TRRzdYiv+B3M4IgOPA8W/raFOA=
+1 -1
View File
@@ -257,7 +257,7 @@ require (
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/gosuri/uitable v0.0.4 // indirect
github.com/grafana/pyroscope-go v1.2.2 // indirect
github.com/grafana/pyroscope-go v1.2.3-0.20250624101038-0410c20e9693 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
github.com/gravitational/license v0.0.0-20250329001817-070456fa8ec1 // indirect
github.com/gravitational/roundtrip v1.0.2 // indirect
+2 -2
View File
@@ -689,8 +689,8 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY=
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
github.com/grafana/pyroscope-go v1.2.2 h1:uvKCyZMD724RkaCEMrSTC38Yn7AnFe8S2wiAIYdDPCE=
github.com/grafana/pyroscope-go v1.2.2/go.mod h1:zzT9QXQAp2Iz2ZdS216UiV8y9uXJYQiGE1q8v1FyhqU=
github.com/grafana/pyroscope-go v1.2.3-0.20250624101038-0410c20e9693 h1:oAnuX7YdawNRJN9jAJqs4uWfv11R+9sgBBMalTp2Xz0=
github.com/grafana/pyroscope-go v1.2.3-0.20250624101038-0410c20e9693/go.mod h1:zzT9QXQAp2Iz2ZdS216UiV8y9uXJYQiGE1q8v1FyhqU=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/gravitational/go-cassandra-native-protocol v0.0.0-teleport.1 h1:zGsdDzqXSuXI+1t+2TRRzdYiv+B3M4IgOPA8W/raFOA=
+1 -1
View File
@@ -260,7 +260,7 @@ require (
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/gosuri/uitable v0.0.4 // indirect
github.com/grafana/pyroscope-go v1.2.2 // indirect
github.com/grafana/pyroscope-go v1.2.3-0.20250624101038-0410c20e9693 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
github.com/gravitational/license v0.0.0-20250329001817-070456fa8ec1 // indirect
github.com/gravitational/roundtrip v1.0.2 // indirect
+2 -2
View File
@@ -790,8 +790,8 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY=
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
github.com/grafana/pyroscope-go v1.2.2 h1:uvKCyZMD724RkaCEMrSTC38Yn7AnFe8S2wiAIYdDPCE=
github.com/grafana/pyroscope-go v1.2.2/go.mod h1:zzT9QXQAp2Iz2ZdS216UiV8y9uXJYQiGE1q8v1FyhqU=
github.com/grafana/pyroscope-go v1.2.3-0.20250624101038-0410c20e9693 h1:oAnuX7YdawNRJN9jAJqs4uWfv11R+9sgBBMalTp2Xz0=
github.com/grafana/pyroscope-go v1.2.3-0.20250624101038-0410c20e9693/go.mod h1:zzT9QXQAp2Iz2ZdS216UiV8y9uXJYQiGE1q8v1FyhqU=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/gravitational/go-cassandra-native-protocol v0.0.0-teleport.1 h1:zGsdDzqXSuXI+1t+2TRRzdYiv+B3M4IgOPA8W/raFOA=
+49 -10
View File
@@ -20,6 +20,7 @@ import (
"context"
"fmt"
"log/slog"
"net/http"
"os"
"time"
@@ -27,6 +28,7 @@ import (
"github.com/gravitational/trace"
"github.com/gravitational/teleport"
logutils "github.com/gravitational/teleport/lib/utils/log"
)
// TODO: Replace logger when pyroscope uses slog
@@ -34,6 +36,24 @@ type pyroscopeLogger struct {
l *slog.Logger
}
type roundTripper struct {
tripper http.RoundTripper
timeout time.Duration
logger *slog.Logger
}
// CloseIdleConnections ensures idle connections of the wrapped
// [http.RoundTripper] are closed.
func (rt roundTripper) CloseIdleConnections() {
type closeIdler interface {
CloseIdleConnections()
}
if tr, ok := rt.tripper.(closeIdler); ok {
tr.CloseIdleConnections()
}
}
func (l pyroscopeLogger) Infof(format string, args ...any) {
if !l.l.Handler().Enabled(context.Background(), slog.LevelInfo) {
return
@@ -60,6 +80,19 @@ func (l pyroscopeLogger) Errorf(format string, args ...any) {
l.l.Error(fmt.Sprintf(format, args...))
}
func (rt roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
start := time.Now()
resp, err := rt.tripper.RoundTrip(req)
duration := time.Since(start)
threshold := rt.timeout * 90 / 100
if duration > threshold {
rt.logger.DebugContext(req.Context(), "Pyroscope upload exceeded threshold", "upload_duration", duration, "upload_threshold", threshold, "upload_url", logutils.StringerAttr(req.URL))
}
return resp, err
}
// createPyroscopeConfig generates the Pyroscope configuration for the Teleport process.
func createPyroscopeConfig(ctx context.Context, logger *slog.Logger, address string) (pyroscope.Config, error) {
if address == "" {
@@ -71,6 +104,17 @@ func createPyroscopeConfig(ctx context.Context, logger *slog.Logger, address str
hostname = "unknown"
}
const httpTimeout = 60 * time.Second
httpClient := &http.Client{
Timeout: httpTimeout,
Transport: roundTripper{
tripper: http.DefaultTransport,
timeout: httpTimeout,
logger: logger,
},
}
config := pyroscope.Config{
ApplicationName: teleport.ComponentTeleport,
ServerAddress: address,
@@ -80,6 +124,8 @@ func createPyroscopeConfig(ctx context.Context, logger *slog.Logger, address str
"version": teleport.Version,
"git_ref": teleport.Gitref,
},
HTTPClient: httpClient,
UploadRate: 60 * time.Second,
}
// Evaluate if profile configuration is customized
@@ -90,21 +136,14 @@ func createPyroscopeConfig(ctx context.Context, logger *slog.Logger, address str
logger.InfoContext(ctx, "Pyroscope will configure profiles from env")
}
var uploadRate *time.Duration
if rate := os.Getenv("TELEPORT_PYROSCOPE_UPLOAD_RATE"); rate != "" {
parsedRate, err := time.ParseDuration(rate)
if err != nil {
logger.InfoContext(ctx, "invalid TELEPORT_PYROSCOPE_UPLOAD_RATE, ignoring value", "provided_value", rate, "error", err)
} else {
uploadRate = &parsedRate
logger.InfoContext(ctx, "TELEPORT_PYROSCOPE_UPLOAD_RATE configured", "rate", parsedRate)
config.UploadRate = parsedRate
}
} else {
logger.InfoContext(ctx, "TELEPORT_PYROSCOPE_UPLOAD_RATE not specified, using default")
}
// Set UploadRate or fall back to defaults
if uploadRate != nil {
config.UploadRate = *uploadRate
}
if value, isSet := os.LookupEnv("TELEPORT_PYROSCOPE_KUBE_COMPONENT"); isSet {
@@ -131,13 +170,13 @@ func (process *TeleportProcess) initPyroscope(address string) {
if err != nil {
logger.ErrorContext(process.ExitContext(), "error starting pyroscope profiler", "address", address, "error", err)
} else {
logger.InfoContext(process.ExitContext(), "Pyroscope has successfully started")
process.OnExit("pyroscope.profiler", func(payload any) {
// Observed rare and inconsistent panics, short term solution is to not wait for flush
profiler.Flush(false)
_ = profiler.Stop()
})
}
logger.InfoContext(process.ExitContext(), "Pyroscope has successfully started")
}
// getPyroscopeProfileTypesFromEnv sets the profile types based on environment variables.