Skip to content

Conversation

@simonswine
Copy link
Contributor

@simonswine simonswine commented Jan 21, 2026

This adds a reverse proxy command that unifies all Pyroscope microservices in Kubernetes into a single endpoint.

The command:

  • Auto-discovers services using label selectors
  • Provides path-based routing to correct services
  • Uses kubectl proxy on Unix socket for secure K8s API access
  • Listens on configurable TCP address (default 127.0.0.1:4242)
  • Filters to user-facing services only (distributor, query-frontend,
    tenant-settings, ad-hoc-profiles)

cc @jake-kramer we discussed the best way for debugging a problem, not too sure that is. But it works for me. In theory we could also inject a tenant id using a flag.

Add intelligent reverse proxy command that unifies all Pyroscope
microservices in Kubernetes into a single endpoint. The command:
- Auto-discovers services using label selectors
- Provides path-based routing to correct services
- Uses kubectl proxy on Unix socket for secure K8s API access
- Listens on configurable TCP address (default 127.0.0.1:4242)
- Filters to user-facing services only (distributor, query-frontend,
  tenant-settings, ad-hoc-profiles)
@simonswine simonswine requested a review from a team as a code owner January 21, 2026 14:40
Copy link
Contributor

@jake-kramer jake-kramer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting this together! To get it to work with drilldown locally, I needed the following changes:

diff --git a/cmd/profilecli/kube_proxy.go b/cmd/profilecli/kube_proxy.go
index e5696af8f..1037a8c80 100644
--- a/cmd/profilecli/kube_proxy.go
+++ b/cmd/profilecli/kube_proxy.go
@@ -381,13 +381,24 @@ func (m *reverseProxyManager) proxyToService(w http.ResponseWriter, r *http.Requ
 		return
 	}
 
-	// Copy headers
+	// Copy headers, but skip Authorization to avoid interfering with kubectl's auth
 	for key, values := range r.Header {
+		// Skip Authorization header - kubectl proxy uses kubeconfig auth, not incoming auth
+		if key == "Authorization" {
+			level.Debug(logger).Log("msg", "skipping Authorization header from incoming request")
+			continue
+		}
 		for _, value := range values {
 			proxyReq.Header.Add(key, value)
 		}
 	}
 
+	// Inject tenant ID if configured
+	if m.params.TenantID != "" {
+		proxyReq.Header.Set("X-Scope-OrgID", m.params.TenantID)
+		level.Debug(logger).Log("msg", "injected tenant ID", "tenant_id", m.params.TenantID)
+	}
+
 	// Execute request via Unix socket
 	client := &http.Client{
 		Transport: &http.Transport{

I was able to successfully run against a local drilldown with:

profilecli admin kube-proxy -v 
--url=<cluster url>
--tenant-id=<tenant id>
-n <namespace>
-c <cluster>
--listen-addr="127.0.0.1:4040"
-l "app.kubernetes.io/name=fire" # for dev

where drilldown Pyroscope datasource is set to :4040


func (m *reverseProxyManager) initRoutingRules() {
// Order matters - most specific first!
m.routingRules = []routingRule{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any straightforward way of programmatically generating this list?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants