RousselTMLEARNING

Observabilité

Instrumenter ses applications pour Pyroscope : SDK, Alloy et eBPF

Envoyer des profils à Pyroscope depuis Go, Java, Python et Node.js avec les SDK, collecter les points pprof avec Alloy et profiler tout un hôte avec eBPF, sans modifier le code.

1. Les SDK #

Le mode push, le plus riche.

Go #

go get github.com/grafana/pyroscope-go
import "github.com/grafana/pyroscope-go"

func main() {
    runtime.SetMutexProfileFraction(5)   // active les profils de verrous
    runtime.SetBlockProfileRate(5)

    pyroscope.Start(pyroscope.Config{
        ApplicationName: "checkout",
        ServerAddress:   "http://pyroscope:4040",
        Tags:            map[string]string{"env": "prod", "region": os.Getenv("REGION")},
        ProfileTypes: []pyroscope.ProfileType{
            pyroscope.ProfileCPU,
            pyroscope.ProfileAllocObjects, pyroscope.ProfileAllocSpace,
            pyroscope.ProfileInuseObjects, pyroscope.ProfileInuseSpace,
            pyroscope.ProfileGoroutines,
            pyroscope.ProfileMutexCount, pyroscope.ProfileMutexDuration,
            pyroscope.ProfileBlockCount, pyroscope.ProfileBlockDuration,
        },
    })
    // ...
}

Java #

curl -LO https://github.com/grafana/pyroscope-java/releases/latest/download/pyroscope.jar
PYROSCOPE_APPLICATION_NAME=orders \
PYROSCOPE_SERVER_ADDRESS=http://pyroscope:4040 \
PYROSCOPE_PROFILER_EVENT=itimer \
PYROSCOPE_LABELS="env=prod" \
java -javaagent:pyroscope.jar -jar orders.jar

L'agent Java s'appuie sur async-profiler : CPU, allocations, verrous et wall clock.

Python et Node.js #

# pip install pyroscope-io
import pyroscope
pyroscope.configure(
    application_name="billing",
    server_address="http://pyroscope:4040",
    tags={"env": "prod"},
)
// npm install @pyroscope/nodejs
const Pyroscope = require('@pyroscope/nodejs');
Pyroscope.init({ serverAddress: 'http://pyroscope:4040', appName: 'front-api', tags: { env: 'prod' } });
Pyroscope.start();

2. Des labels pour découper les profils #

Le vrai pouvoir du profiling continu.

Labels dynamiques #

Les labels permettent de filtrer un flame graph par endpoint, client ou type de tâche. Ils s'ajoutent autour d'un bloc de code :

pyroscope.TagWrapper(ctx, pyroscope.Labels("endpoint", "/checkout"), func(c context.Context) {
    processCheckout(c)
})
with pyroscope.tag_wrapper({"endpoint": "/invoice"}):
    generate_invoice()
Erreur Courante : Comme pour les métriques, la cardinalité compte : un label par identifiant de requête ou d'utilisateur crée une série de profils par valeur et fait exploser le stockage.

3. Alloy : pull pprof et eBPF #

Sans toucher (ou presque) au code.

Collecter les points pprof #

Une application Go qui importe net/http/pprof expose déjà ses profils sur /debug/pprof/. Alloy les collecte régulièrement :

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

pyroscope.scrape "go_apps" {
  targets = [
    {"__address__" = "checkout:6060", "service_name" = "checkout"},
  ]
  forward_to = [pyroscope.write.default.receiver]
  profiling_config {
    profile.process_cpu { enabled = true }
    profile.memory      { enabled = true }
    profile.goroutine   { enabled = true }
    profile.mutex       { enabled = true }
    profile.block       { enabled = true }
  }
}

Profiler tout l'hôte avec eBPF #

Le composant pyroscope.ebpf échantillonne le CPU de tous les processus via le noyau Linux : aucune modification de code, tous les langages compilés (Go, Rust, C/C++) et plusieurs langages interprétés (Python...) sont pris en charge.

discovery.docker "local" { host = "unix:///var/run/docker.sock" }

discovery.relabel "containers" {
  targets = discovery.docker.local.targets
  rule {
    source_labels = ["__meta_docker_container_name"]
    regex         = "/(.*)"
    target_label  = "service_name"
  }
}

pyroscope.ebpf "host" {
  targets    = discovery.relabel.containers.output
  forward_to = [pyroscope.write.default.receiver]
}
Erreur Courante : eBPF exige un noyau Linux récent (4.9+, idéalement 5.x) et un Alloy privilégié (privileged: true, pid: host). Il ne fournit que des profils CPU : pour la mémoire et les verrous, utilisez un SDK.

Choisir #

CritèreSDKAlloy pprofAlloy eBPF
Modification du codeOuiMinime (pprof)Non
Types de profilsTousTous (Go)CPU
Labels métierOuiNonNon
Liens traces → profilsOuiNonNon

Articles liés

Le profiling continu avec Grafana Pyroscope : pourquoi et comment

Le quatrième signal de l'observabilité : ce qu'est le profiling continu, les types de profils (CPU, mémoire, …

Architecture et déploiement de Grafana Pyroscope

Les composants de Pyroscope (distributor, ingester, querier, store-gateway, compactor), le stockage objet, le…

Analyser les profils dans Grafana : flame graphs, comparaisons et diagnostics

Méthode d'analyse des profils dans Grafana : Profiles Drilldown, flame graph et top table, comparaison de deu…

Pour aller plus loin