RousselTMLEARNING

Observabilité

Grafana Alloy: Continuous Profiling with Pyroscope

Discover how to configure continuous profiling in your environments using Grafana Alloy and Pyroscope to identify bottlenecks in your applications.

1. Introduction to continuous profiling #

Continuous profiling with Pyroscope.

The fourth signal #

Metrics say that the service consumes too much, traces say where the request loses time, and profiles say which function is responsible. A profile samples what the program does: call stack on the CPU, memory allocations, goroutines.

SignalQuestionExample answer
MetricIs there a problem?CPU at 90%
TraceWhich step is slow?Cart calculation: 800 ms
ProfileWhich line of code?calculTVA(): 60% of CPU

Why “continuous”? #

Classic profiling is started by hand once the problem is spotted, and it is often too late to reproduce it. Continuous profiling records all the time, with low overhead. You can then compare yesterday's version with today's, or examine an incident from last night.

Profile types #

TypeMeasuresTo find
CPUTime spent per functionExpensive computations, loops
Memory (allocations)What gets allocatedGarbage collector pressure
Memory (in use)What stays in memoryLeaks
Goroutines, threadsCount and stacksDeadlocks, goroutine leaks
Mutex, blockWaiting timeContention

Reading a flame graph #

Pyroscope displays profiles as a flame graph: each rectangle is a function, its width shows its share of the total. Look for wide rectangles: those are the ones to optimize. The horizontal axis is not time.

Pyroscope is the successor to Grafana Phlare. See the dedicated Grafana Pyroscope course to go further.

2. Setting up data collection #

Configuration of scraping and forwarding for Pyroscope.

Three ways to collect #

Three ways to collect profiles with Alloy
ComponentPrincipleRequirement
pyroscope.scrapeQueries pprof endpoints over HTTPThe application exposes pprof (Go…)
pyroscope.ebpfObserves the CPU from the kernelLinux, root privileges; CPU only
pyroscope.javaAttaches async-profiler to JVMsAccess to Java processes

Collecting over HTTP #

In Go, exposing the pprof endpoints is enough:

import _ "net/http/pprof"

go func() {
    http.ListenAndServe(":6060", nil)
}()

Alloy queries them like metrics:

pyroscope.scrape "app" {
  targets         = [{ "__address__" = "app:6060", "service_name" = "boutique" }]
  scrape_interval = "15s"
  forward_to      = [pyroscope.write.backend.receiver]

  profiling_config {
    profile.process_cpu {
      enabled = true
    }
    profile.memory {
      enabled = true
    }
    profile.goroutine {
      enabled = true
    }
  }
}

pyroscope.write "backend" {
  endpoint {
    url = "http://pyroscope:4040"
  }
  external_labels = { env = "prod" }
}
SettingDefault
scrape_interval15s
scrape_timeout10s
delta_profiling_duration14s

Tested by profiling Alloy itself, which exposes pprof on its HTTP port: in just over 30 seconds, the backend received 6 pushes on /push.v1.PusherService/Push.

The service name matters: add service_name to your targets: it is how you find the application in Pyroscope. And never expose the pprof port on the Internet.

Without touching the code: eBPF #

discovery.process "tous" {
}

pyroscope.ebpf "hote" {
  targets    = discovery.process.tous.targets
  forward_to = [pyroscope.write.backend.receiver]
}

pyroscope.write "backend" {
  endpoint {
    url = "http://pyroscope:4040"
  }
}

pyroscope.ebpf profiles processes from the kernel, in any language. It requires running Alloy as root (or in a privileged container), and it only measures CPU. We validated this configuration with alloy validate without running it: the test requires root privileges.

Choosing #

SituationChoice
Go applicationspyroscope.scrape: memory, goroutines and CPU
Mixed fleet, code that cannot changepyroscope.ebpf
Java applicationspyroscope.java
Need for fine business labelsPyroscope SDK in the application, direct push

You can link profiles to traces: add the same labels (service_name, env) on both sides to navigate from one to the other in Grafana.

Related articles

Grafana Alloy environment variables: GOMEMLIMIT, sys.env and OTEL_RESOURCE_ATTRIBUTES

Which variables does Grafana Alloy actually read? GOMEMLIMIT, HTTP_PROXY, sys.env, OTEL_RESOURCE_ATTRIBUTES w…

La stack LGTM (Loki, Grafana, Tempo, Mimir) : architecture et mise en place

Comprendre la stack LGTM de Grafana Labs : Loki pour les logs, Grafana pour la visualisation, Tempo pour les …

OpenTelemetry Collector, Grafana Alloy et Bindplane : comparer trois approches des pipelines de télémétrie

Comparatif détaillé de trois façons de gérer des pipelines de télémétrie : l'OpenTelemetry Collector en YAML,…

Keep learning

Support us

♥ SUPPORT US

Support RousselTM Learning

Courses, articles, labs and certificates stay free with no intrusive ads. A voluntary contribution, of any amount you choose, pays for hosting, lab environments and new content.

Contribute with PayPal Any amount · card or PayPal accountLearn more
QR code PayPal
Or scan this QR code with your phone

Thank you, every contribution counts!