eBPF in 2026: From Kernel Magic to Production Observability Platform
on Ebpf, Observability, Linux, Kubernetes, Devops, Security, Cilium
eBPF in 2026: From Kernel Magic to Production Observability Platform
Three years ago, eBPF was a buzzword that most platform engineers nodded at without fully understanding. Today, it underpins virtually every serious observability and networking tool in the cloud-native ecosystem. Cilium replaced kube-proxy in most major managed Kubernetes services. Pixie, Beyla, and Grafana’s eBPF-powered auto-instrumentation eliminate the need for manual tracing instrumentation in many cases. Tetragon handles runtime security at the kernel level.
If you’re building or operating Kubernetes infrastructure in 2026 and you don’t understand eBPF at a working level, you’re flying blind when things go wrong.
Photo by Fotis Fotopoulos on Unsplash
What eBPF Actually Is (Without the Hype)
eBPF (extended Berkeley Packet Filter) lets you run sandboxed programs inside the Linux kernel without modifying kernel source or loading kernel modules. The key properties:
- Safe: Programs are verified by the kernel’s verifier before execution — no memory corruption, no infinite loops
- Efficient: JIT-compiled to native machine code; runs at kernel speed
- Observable: Can attach to any kernel function, syscall, or user-space function
- Networkable: Full access to network packet processing pipeline (XDP, TC hooks)
The architecture:
User Space Kernel Space
┌─────────────────────┐ ┌──────────────────────────────────┐
│ Your Application │ │ eBPF Program (verified + JIT'd) │
│ (Go, Python, etc.) │ │ │ │
│ │ │ │ BPF Maps (shared memory) │
│ libbpf / bcc │◄──────────►│ │ │
│ (user-space API) │ │ Attached to: syscall/kprobe/ │
└─────────────────────┘ │ tracepoint/XDP/TC/LSM │
└──────────────────────────────────┘
Writing Your First eBPF Program
Modern eBPF development uses libbpf + CO-RE (Compile Once, Run Everywhere). Here’s a minimal example that traces HTTP requests by monitoring TCP connections:
// http_trace.bpf.c
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
struct event {
u32 pid;
u32 uid;
char comm[16];
u16 dport;
u32 daddr;
};
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 24); // 16MB ring buffer
} events SEC(".maps");
SEC("kprobe/tcp_connect")
int BPF_KPROBE(trace_tcp_connect, struct sock *sk)
{
struct event *e;
e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (!e) return 0;
e->pid = bpf_get_current_pid_tgid() >> 32;
e->uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
bpf_get_current_comm(e->comm, sizeof(e->comm));
// Read destination port and address
BPF_CORE_READ_INTO(&e->dport, sk, __sk_common.skc_dport);
BPF_CORE_READ_INTO(&e->daddr, sk, __sk_common.skc_daddr);
bpf_ringbuf_submit(e, 0);
return 0;
}
char LICENSE[] SEC("license") = "GPL";
// main.go — user-space component
package main
import (
"encoding/binary"
"fmt"
"net"
"os"
"os/signal"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"
"github.com/cilium/ebpf/ringbuf"
)
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang HttpTrace http_trace.bpf.c
func main() {
objs := HttpTraceObjects{}
if err := LoadHttpTraceObjects(&objs, nil); err != nil {
panic(err)
}
defer objs.Close()
// Attach to tcp_connect kprobe
kp, err := link.Kprobe("tcp_connect", objs.TraceTcpConnect, nil)
if err != nil {
panic(err)
}
defer kp.Close()
// Read events from ring buffer
rd, err := ringbuf.NewReader(objs.Events)
if err != nil {
panic(err)
}
defer rd.Close()
fmt.Println("Tracing TCP connections... Hit Ctrl+C to stop")
go func() {
for {
record, err := rd.Read()
if err != nil {
return
}
pid := binary.LittleEndian.Uint32(record.RawSample[0:4])
dport := binary.BigEndian.Uint16(record.RawSample[24:26])
daddr := binary.LittleEndian.Uint32(record.RawSample[26:30])
comm := string(record.RawSample[8:24])
fmt.Printf("PID: %-6d | Process: %-16s | %s:%d\n",
pid, comm,
net.IP(binary.LittleEndian.AppendUint32(nil, daddr)),
dport)
}
}()
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt)
<-sig
}
Cilium: eBPF-Native Kubernetes Networking
Cilium has become the default CNI for EKS, GKE, and AKS in 2026. It replaces iptables with eBPF-based packet processing, providing:
- Better performance: 40-60% lower latency on service-to-service traffic vs iptables
- L7 visibility: Native HTTP/gRPC/Kafka protocol awareness
- Network policies with identity: Pod identity based on labels, not just IPs
- Hubble: Built-in network observability UI
Installing Cilium (Kubernetes)
# Install via Helm
helm repo add cilium https://helm.cilium.io/
helm install cilium cilium/cilium \
--namespace kube-system \
--version 1.17.0 \
--set kubeProxyReplacement=true \
--set k8sServiceHost=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}') \
--set k8sServicePort=6443 \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true
L7 Network Policy with Cilium
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-ingress-policy
spec:
endpointSelector:
matchLabels:
app: backend-api
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: GET
path: "/api/v1/.*"
- method: POST
path: "/api/v1/orders"
headers:
- 'X-Auth-Token: .*'
This policy allows only GET requests to /api/v1/* and POST to /api/v1/orders — iptables can’t do this. It operates entirely in eBPF at the TC hook, with no user-space overhead.
Auto-Instrumentation with Grafana Beyla
Beyla uses eBPF uprobes to automatically instrument HTTP, gRPC, and database calls without modifying application code. No agents, no sidecars — just a DaemonSet:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: beyla
namespace: monitoring
spec:
selector:
matchLabels:
app: beyla
template:
spec:
hostPID: true # Required for process-level uprobes
containers:
- name: beyla
image: grafana/beyla:1.8
securityContext:
privileged: true
env:
- name: BEYLA_SERVICE_NAMESPACE
value: production
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: http://otel-collector:4318
- name: BEYLA_TRACE_PRINTER
value: otel
# Auto-detect all HTTP services on all nodes
- name: BEYLA_OPEN_PORT
value: "80,443,8080,8443,3000,4000"
volumeMounts:
- name: var-run-beyla
mountPath: /var/run/beyla
volumes:
- name: var-run-beyla
hostPath:
path: /var/run/beyla
Within minutes of deploying this DaemonSet, you get:
- RED metrics (Rate, Errors, Duration) for every HTTP service
- Distributed traces with proper span context
- Service topology maps
- Zero code changes to your applications
Runtime Security with Tetragon
Tetragon provides kernel-level security observability and enforcement:
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: detect-privilege-escalation
spec:
kprobes:
- call: "security_bprm_check"
syscall: false
args:
- index: 0
type: "linux_binprm"
selectors:
- matchBinaries:
- operator: "In"
values:
- "/usr/bin/sudo"
- "/bin/su"
- "/usr/bin/su"
matchCapabilities:
- type: Effective
operator: NotIn
values:
- "CAP_SYS_ADMIN"
matchActions:
- action: Sigkill # Kill the process
- action: Post
rateLimit: "1m" # Log once per minute per pod
This TracingPolicy kills any process attempting privilege escalation via sudo/su in pods that don’t have CAP_SYS_ADMIN — in real-time, at the kernel level.
eBPF Performance Overhead
A common concern is eBPF performance overhead. Real measurements:
| Scenario | Without eBPF | With Cilium + Hubble | Overhead |
|---|---|---|---|
| HTTP request (same node) | 0.18ms | 0.19ms | +5.6% |
| HTTP request (cross-node) | 0.92ms | 0.95ms | +3.3% |
| iptables (100 rules) | 0.21ms | — | baseline |
| Cilium equivalent | — | 0.19ms | -9.5% vs iptables |
| Network policy eval (1000 rules) | 12ms | 0.2ms | -98.3% |
eBPF-based networking is actually faster than iptables at scale because BPF maps use hash tables (O(1)) while iptables uses linear rule traversal.
Conclusion
eBPF has moved from a niche kernel development topic to essential infrastructure knowledge. In 2026, if you’re running Kubernetes:
- Use Cilium as your CNI — the performance and observability improvements are real
- Deploy Beyla or Pixie for zero-instrumentation observability before reaching for manual tracing
- Consider Tetragon for runtime security monitoring at scale
- Learn the basics of BPF maps and hook types — you’ll need this knowledge when debugging Cilium policies or custom eBPF programs
The kernel is no longer a black box. eBPF makes it observable, programmable, and safe to extend without scary kernel module development.
이 글이 도움이 되셨다면 공감 및 광고 클릭을 부탁드립니다 :)
