docs/sources/configure-client/opentelemetry/ebpf-profiler.md
Pyroscope supports receiving profiles from the OpenTelemetry eBPF profiler via OTLP. The eBPF profiler collects system-wide CPU profiles from all processes on a Linux host and sends them through the OpenTelemetry Collector pipeline to Pyroscope.
Consider the following limitations:
This feature is suitable for development and testing. Evaluate carefully before production use.
The profiling pipeline has three components:
{{< mermaid >}} flowchart LR A["OTel eBPF Profiler <i>collects CPU profiles system-wide via eBPF</i>"] -- "OTLP gRPC" --> B["Pyroscope <i>stores & aggregates profiling data</i>"] B -- query --> C["Grafana <i>visualize as flamegraphs</i>"] {{< /mermaid >}}
profiling receiver. It attaches eBPF probes to collect CPU stack traces at 97 samples per second from every process on the host.The profiler requires host PID namespace access and several host filesystem mounts (/proc, /sys/kernel, /lib/modules) to resolve stack traces.
The profiler runs as a specialized OpenTelemetry Collector. Configure it with a profiling receiver and an otlp_grpc exporter pointing to Pyroscope:
receivers:
profiling:
samples_per_second: 97
exporters:
otlp_grpc:
endpoint: pyroscope:4040
tls:
insecure: true
service:
pipelines:
profiles:
receivers: [profiling]
exporters: [otlp_grpc]
The collector must be started with the --feature-gates=service.profilesSupport flag.
By default, the profiler sets process.executable.name on each profile but does not set service_name, which Pyroscope uses as the primary label. To map executable names to service names, configure Pyroscope with an ingestion relabeling rule:
limits:
ingestion_relabeling_rules:
- action: labelmap
regex: ^process.executable.name$
replacement: service_name
In Kubernetes, you can add a k8sattributes processor to enrich profiles with pod, namespace, deployment, and node metadata:
processors:
k8sattributes/profiles:
auth_type: serviceAccount
passthrough: false
extract:
metadata:
- k8s.pod.name
- k8s.pod.uid
- k8s.namespace.name
- k8s.deployment.name
- k8s.node.name
- k8s.container.name
- container.image.name
- container.image.tag
- service.name
- service.namespace
- service.instance.id
otel_annotations: true
pod_association:
- sources:
- from: resource_attribute
name: container.id
service:
pipelines:
profiles:
receivers: [profiling]
processors: [k8sattributes/profiles]
exporters: [otlp_grpc]
This requires a ServiceAccount with RBAC permissions to read pods, namespaces, nodes, and workload resources. See the example RBAC manifest.
A minimal Docker Compose setup runs the profiler, Pyroscope, and Grafana:
services:
otel-ebpf-profiler:
image: otel/opentelemetry-collector-ebpf-profiler:0.147.0
command:
- --config=/etc/ebpf-profiler-config.yaml
- --feature-gates=service.profilesSupport
privileged: true
pid: "host"
volumes:
- ./config/ebpf-profiler-config.yaml:/etc/ebpf-profiler-config.yaml:ro
- /sys/kernel/debug:/sys/kernel/debug:ro
- /sys/fs/cgroup:/sys/fs/cgroup:ro
- /proc:/proc:ro
pyroscope:
image: grafana/pyroscope:1.18.1
command:
- -self-profiling.disable-push=true
- -config.file=/etc/pyroscope.yaml
ports:
- "4040:4040"
grafana:
image: grafana/grafana:latest
environment:
- GF_PLUGINS_PREINSTALL_SYNC=grafana-pyroscope-app
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
- GF_AUTH_DISABLE_LOGIN_FORM=true
ports:
- "3000:3000"
For a complete working example, see the Docker example.
On Kubernetes, deploy the profiler as a DaemonSet so it runs on every node:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: otel-ebpf-profiler
spec:
selector:
matchLabels:
app: otel-ebpf-profiler
template:
metadata:
labels:
app: otel-ebpf-profiler
spec:
hostPID: true
serviceAccountName: otel-ebpf-profiler
containers:
- name: profiler
image: otel/opentelemetry-collector-ebpf-profiler:0.147.0
args:
- "--config=/etc/otel/config.yaml"
- "--feature-gates=+service.profilesSupport"
securityContext:
privileged: true
volumeMounts:
- name: sys-kernel
mountPath: /sys/kernel
readOnly: true
- name: proc
mountPath: /proc
readOnly: true
volumes:
- name: sys-kernel
hostPath:
path: /sys/kernel
- name: proc
hostPath:
path: /proc
tolerations:
- operator: Exists
For a complete working example with kustomize, Pyroscope, Grafana, RBAC, and a sample workload, see the Kubernetes example.
Deploy it with:
git clone --depth 1 --filter=tree:0 --no-checkout https://github.com/grafana/pyroscope.git
cd pyroscope
git sparse-checkout set examples/otel-collector/ebpf
git checkout
kubectl apply -k examples/otel-collector/ebpf/kubernetes/
After deploying, open Grafana (http://localhost:3000 for Docker, or port-forward in Kubernetes) and navigate to Explore with the Pyroscope data source. You should see profiles grouped by service_name appearing within a few minutes.