eBPF Observability
1. What is eBPF?
eBPF (extended Berkeley Packet Filter) lets you run sandboxed programs inside the Linux kernel, triggered by events — without modifying kernel source or loading modules.
graph LR
subgraph User["User Space"]
tool["bpftrace / BCC<br/>tool writes eBPF program"]
maps["eBPF Maps<br/>(shared memory)"]
end
subgraph Kernel["Kernel Space"]
verifier["eBPF Verifier<br/>(safety check)"]
jit["JIT Compiler"]
kprobe["kprobe / tracepoint<br/>/ XDP hook"]
prog["eBPF Program<br/>(runs on event)"]
end
tool -->|load bytecode| verifier
verifier -->|approved| jit
jit --> prog
kprobe -->|trigger| prog
prog -->|write results| maps
maps -->|read| tool
Hook types:
kprobe/kretprobe— kernel function entry/exittracepoint— stable kernel trace eventsuprobe— user-space function entryXDP— eXpress Data Path, runs at NIC driver level (pre-stack)tc— traffic control ingress/egress
2. Safety Model
The verifier enforces before any program runs:
- No unbounded loops (all loops must have a bounded iteration count)
- No invalid memory access (all pointer arithmetic checked)
- No unreachable instructions
- Must terminate (DAG check)
- Max 1M instructions (kernel 5.2+)
Result: kernel stability guaranteed — buggy eBPF cannot crash the kernel.
3. Tools
bpftrace one-liners
# syscall latency histogram (nanoseconds)
bpftrace -e 'tracepoint:syscalls:sys_enter_read { @start[tid] = nsecs; }
tracepoint:syscalls:sys_exit_read { @ns = hist(nsecs - @start[tid]); }'
# off-CPU time (who is blocked and for how long)
bpftrace -e 'tracepoint:sched:sched_switch { @off[args->prev_comm] = nsecs; }
tracepoint:sched:sched_switch { @ms[args->next_comm] = hist((nsecs - @off[args->next_comm])/1e6); }'
# TCP retransmits with details
bpftrace -e 'tracepoint:tcp:tcp_retransmit_skb {
printf("%s %s:%d -> %d<br>", comm,
ntop(args->saddr), args->sport, args->dport); }'
BCC tools
| Tool | What it shows |
|---|---|
execsnoop |
Every new process exec (pid, ppid, args) |
opensnoop |
Every file open call with path |
tcplife |
TCP connection durations + bytes |
biolatency |
Block I/O latency histogram |
runqlat |
CPU run-queue latency (scheduler) |
profile |
CPU flame graph via sampling |
tcpretrans |
TCP retransmit events |
# install bcc on Ubuntu
apt-get install bpfcc-tools
execsnoop-bpfcc # watch all execs
opensnoop-bpfcc -p 1234 # watch PID 1234 file opens
tcplife-bpfcc # show TCP session durations
4. Cilium: eBPF-based CNI
Cilium replaces iptables with eBPF for Kubernetes networking:
- CNI: pod-to-pod routing via eBPF maps (no kube-proxy)
- Network Policy: L3/L4 + L7 (HTTP method, gRPC service) enforcement
- Load balancing: eBPF replaces kube-proxy IPVS/iptables for Service VIP
helm install cilium cilium/cilium \
--set kubeProxyReplacement=strict \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true
5. Tetragon: Runtime Security
Tetragon uses eBPF to enforce security policies at kernel level:
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: detect-shell-exec
spec:
kprobes:
- call: "sys_execve"
syscall: true
args:
- index: 0
type: "string"
selectors:
- matchArgs:
- index: 0
operator: "Postfix"
values: ["/bin/sh", "/bin/bash"]
matchActions:
- action: Sigkill # kill the process
Detects: exec of shells, /etc/passwd reads, unexpected network connections, privilege escalation.
6. Hubble: Network Observability
Hubble is the observability layer for Cilium clusters.
graph TD
subgraph Node1["K8s Node 1"]
cilium1["Cilium Agent"]
hubble1["Hubble Observer<br/>(local gRPC)"]
cilium1 --> hubble1
end
subgraph Node2["K8s Node 2"]
cilium2["Cilium Agent"]
hubble2["Hubble Observer<br/>(local gRPC)"]
cilium2 --> hubble2
end
relay["Hubble Relay<br/>(aggregates all nodes)"]
ui["Hubble UI<br/>(service map)"]
cli["hubble CLI<br/>(flow queries)"]
hubble1 --> relay
hubble2 --> relay
relay --> ui
relay --> cli
hubble observe --namespace default --last 100
hubble observe --from-pod default/frontend --to-pod default/backend
hubble observe --verdict DROPPED # show dropped flows
7. Performance Advantage
| Approach | Overhead | Why |
|---|---|---|
| strace | ~100x slowdown | ptrace stops process per syscall |
| perf | ~5-10% | kernel→userspace copy per event |
| eBPF | ~1-3% | in-kernel aggregation, zero-copy maps |
eBPF programs aggregate data (histograms, counts) in kernel maps. Only summaries cross the user/kernel boundary — not raw events.
Zero-copy: BPF_MAP_TYPE_PERF_EVENT_ARRAY uses a shared ring buffer; userspace reads directly from mapped memory.
Architecture: Cilium + Hubble + Tetragon
graph TD
subgraph KernelSpace["Kernel Space"]
xdp["XDP Hook<br/>(ingress fast path)"]
tc["tc Hook<br/>(egress policy)"]
kp["kprobes<br/>(exec/file/net)"]
end
subgraph CiliumStack["Cilium Stack"]
agent["Cilium Agent<br/>(CNI + policy)"]
tetragon["Tetragon<br/>(runtime security)"]
hubble_obs["Hubble Observer<br/>(flow events)"]
end
subgraph Observability["Observability Layer"]
relay["Hubble Relay"]
ui["Hubble UI"]
prometheus["Prometheus<br/>(Cilium metrics)"]
end
xdp --> agent
tc --> agent
kp --> tetragon
agent --> hubble_obs
hubble_obs --> relay
relay --> ui
agent --> prometheus