Split View: eBPF 완전 가이드 2025: 커널 프로그래밍, Observability, 네트워킹, 보안의 혁명
eBPF 완전 가이드 2025: 커널 프로그래밍, Observability, 네트워킹, 보안의 혁명
TL;DR
- eBPF는 Linux 커널의 안전한 프로그래밍 환경. 커널 코드 변경 없이 추적/네트워킹/보안 기능 추가
- 3대 활용 분야: Observability(Pixie/Parca), Networking(Cilium/XDP), Security(Falco/Tetragon)
- CO-RE: 한 번 컴파일하면 다양한 커널 버전에서 실행 — 배포 부담 제거
- bpftrace: DTrace 같은 고수준 언어로 즉석 트레이싱 (
bpftrace -e 'tracepoint:syscalls:sys_enter_open { @[comm] = count(); }') - XDP: 네트워크 카드 수준에서 패킷 처리 — Cloudflare가 DDoS 방어에 사용 (초당 1천만+ 패킷)
1. eBPF란 무엇인가?
1.1 한 줄 정의
eBPF는 운영체제 커널을 수정하지 않고도 커널 내부에서 샌드박스화된 프로그램을 실행할 수 있는 기술이다.
전통적으로 커널 기능을 확장하려면 두 가지 방법밖에 없었습니다:
- 커널 자체에 코드 추가 → 기여하기 어렵고, 머지되어도 사용자가 새 커널 버전을 기다려야 함
- 커널 모듈 로드 → 잘못 작성하면 시스템 크래시 위험. 커널 ABI 변경에 취약
eBPF는 제3의 길을 제시합니다 — 커널 안에서 안전하게 커스텀 코드를 실행.
1.2 어떻게 안전성을 보장하는가?
eBPF 프로그램은 커널에 로드되기 전 **BPF Verifier(검증기)**의 정적 분석을 통과해야 합니다:
- 종료 보장: 무한 루프 금지 (백워드 점프 제한)
- 메모리 안전성: 모든 포인터 접근을 추적, 경계 검사
- 함수 호출 제한: 허용된 헬퍼 함수만 호출 가능
- 스택 크기: 최대 512바이트
- 명령어 수: 백만 개 이하 (커널 5.2+)
검증기 통과 후 JIT 컴파일러가 네이티브 머신 코드로 변환하여 거의 네이티브 속도로 실행됩니다.
1.3 cBPF에서 eBPF로의 진화
| 특성 | cBPF (1992) | eBPF (2014~) |
|---|---|---|
| 용도 | 패킷 필터링만 | 범용 커널 프로그래밍 |
| 레지스터 | 2개 (32비트) | 11개 (64비트) |
| 명령어 | 22개 | 100+ |
| 헬퍼 함수 | 없음 | 200+ |
| 맵 | 없음 | 30+ 종류 |
| JIT | 일부 아키텍처 | 대부분 아키텍처 |
tcpdump가 사용하던 cBPF는 단순 패킷 필터였지만, eBPF는 소형 가상머신으로 진화했습니다.
2. eBPF 아키텍처 심층 분석
2.1 컴포넌트 개요
┌─────────────────────────────────────────────┐
│ 사용자 공간 (User Space) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ bpftool │ │ bpftrace │ │ Cilium │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ │ bpf() syscall │
└─────────────────────┼───────────────────────┘
▼
┌─────────────────────────────────────────────┐
│ 커널 공간 (Kernel Space) │
│ ┌─────────────────────────────────────┐ │
│ │ BPF Verifier (안전성 검증) │ │
│ └──────────────┬──────────────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ JIT Compiler (네이티브 코드 변환) │ │
│ └──────────────┬──────────────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Hooks: kprobe, tracepoint, XDP, │ │
│ │ perf_event, socket, cgroup, LSM │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
2.2 주요 훅(Hook) 종류
eBPF 프로그램은 다양한 커널 이벤트에 부착(attach)할 수 있습니다:
| 훅 유형 | 설명 | 예시 |
|---|---|---|
| kprobe / kretprobe | 커널 함수 진입/종료 | do_sys_open 호출 추적 |
| uprobe / uretprobe | 사용자 공간 함수 | OpenSSL의 SSL_read 추적 |
| tracepoint | 커널 정적 추적 포인트 | sched:sched_switch |
| perf_event | 하드웨어/소프트웨어 카운터 | CPU 사이클, 캐시 미스 |
| XDP | 네트워크 드라이버 | DDoS 필터링 |
| TC (Traffic Control) | 트래픽 셰이핑 | QoS, 로드 밸런싱 |
| socket | 소켓 작업 | 소켓 필터 |
| cgroup | cgroup 이벤트 | 네트워크 격리 |
| LSM (Linux Security Modules) | 보안 정책 | 파일 접근 제어 |
2.3 BPF Maps — 핵심 데이터 구조
eBPF 프로그램과 사용자 공간 간 데이터 공유는 BPF Maps를 통해 이루어집니다:
| 맵 종류 | 용도 |
|---|---|
BPF_MAP_TYPE_HASH | 일반 키-값 저장 |
BPF_MAP_TYPE_ARRAY | 배열 (인덱스 기반) |
BPF_MAP_TYPE_PERF_EVENT_ARRAY | 사용자 공간으로 이벤트 스트리밍 |
BPF_MAP_TYPE_RINGBUF | 더 효율적인 이벤트 스트리밍 (커널 5.8+) |
BPF_MAP_TYPE_LRU_HASH | LRU 캐시 |
BPF_MAP_TYPE_LPM_TRIE | 최장 접두사 일치 (라우팅에 사용) |
BPF_MAP_TYPE_PROG_ARRAY | tail call 점프 테이블 |
// BPF_MAP_TYPE_HASH 예시: 시스템 콜 호출 횟수 카운팅
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __u32); // PID
__type(value, __u64); // count
__uint(max_entries, 10240);
} syscall_count SEC(".maps");
3. 개발 도구 비교
eBPF 프로그램을 작성하는 방법은 여러 가지가 있습니다:
3.1 BCC (BPF Compiler Collection)
- 언어: C로 BPF 작성, Python/Lua로 사용자 공간
- 장점: 풍부한 도구 (bcc-tools 패키지에 100+ 도구)
- 단점: 컴파일러 의존성 (실행 시 LLVM 필요), 큰 바이너리
# BCC 예시: openat 시스템 콜 추적
from bcc import BPF
prog = """
int hello(void *ctx) {
bpf_trace_printk("openat called!\\n");
return 0;
}
"""
b = BPF(text=prog)
b.attach_kprobe(event="do_sys_openat2", fn_name="hello")
b.trace_print()
3.2 bpftrace — DTrace의 후계자
- 언어: 고수준 awk 같은 DSL
- 용도: 즉석 트레이싱, 디버깅
- 장점: 한 줄로 강력한 분석
# 프로세스별 시스템 콜 호출 횟수
bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[comm] = count(); }'
# 1초 이상 걸리는 read() 시스템 콜 추적
bpftrace -e '
kprobe:vfs_read { @start[tid] = nsecs; }
kretprobe:vfs_read /@start[tid]/ {
$duration = nsecs - @start[tid];
if ($duration > 1000000000) {
printf("%s pid %d took %d ms\n", comm, pid, $duration / 1000000);
}
delete(@start[tid]);
}'
# CPU 사용량이 가장 높은 함수 (on-CPU 프로파일링)
bpftrace -e 'profile:hz:99 { @[ustack] = count(); }'
3.3 libbpf + CO-RE — 프로덕션 표준
- CO-RE (Compile Once, Run Everywhere): BTF(BPF Type Format)로 커널 구조체 정보를 임베드 → 다양한 커널 버전에서 동작
- 장점: 작은 바이너리, 컴파일러 의존성 없음, 빠른 시작
- 단점: 학습 곡선
// libbpf 예시: openat() 추적
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
char LICENSE[] SEC("license") = "GPL";
SEC("tracepoint/syscalls/sys_enter_openat")
int handle_openat(struct trace_event_raw_sys_enter *ctx) {
char comm[16];
bpf_get_current_comm(&comm, sizeof(comm));
bpf_printk("openat by %s\n", comm);
return 0;
}
3.4 도구 비교표
| 도구 | 학습 곡선 | 성능 | 배포 용이성 | 적합한 용도 |
|---|---|---|---|---|
| BCC | 중간 | 중간 | 어려움 (LLVM 필요) | 학습, 일회성 분석 |
| bpftrace | 쉬움 | 높음 | 중간 | 즉석 트레이싱, 디버깅 |
| libbpf + CO-RE | 어려움 | 매우 높음 | 쉬움 | 프로덕션 도구 개발 |
| Aya (Rust) | 어려움 | 매우 높음 | 쉬움 | Rust 생태계 통합 |
| ebpf-go | 중간 | 매우 높음 | 쉬움 | Go 애플리케이션 통합 |
4. eBPF의 3대 활용 분야
4.1 Observability — 관측성
eBPF는 애플리케이션 코드 변경 없이 시스템 동작을 깊게 관찰합니다.
Pixie — Kubernetes Native Observability
- New Relic이 인수한 오픈소스 프로젝트
- 사이드카 없이 모든 Pod의 HTTP/gRPC/MySQL 요청 자동 추적
- BPF 코드가 OpenSSL의
SSL_read/SSL_write에 uprobe를 부착하여 암호화 전 데이터 캡처
Parca — Continuous Profiling
- 프로덕션에서 24/7 CPU 프로파일링
- 오버헤드 1% 미만
- Polar Signals가 개발한 오픈소스
bpftop — Top for eBPF
- 실행 중인 eBPF 프로그램의 CPU 사용량을
top명령처럼 표시
4.2 Networking — 네트워킹
Cilium — eBPF 기반 K8s 네트워킹의 표준
- CNCF Graduated 프로젝트
- kube-proxy 완전 대체 — iptables 대신 eBPF로 서비스 라우팅
- L7 정책 (HTTP, gRPC, Kafka) 지원
- Hubble: Cilium 기반 네트워크 옵저버빌리티
# Cilium L7 Network Policy 예시
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-api-only
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: '8080'
rules:
http:
- method: 'GET'
path: '/api/v1/.*'
XDP (eXpress Data Path) — 초고속 패킷 처리
XDP는 네트워크 드라이버 레벨에서 패킷을 처리합니다 — 커널 네트워크 스택을 거치지 않음.
- Cloudflare: DDoS 방어에 XDP 사용 — 초당 1천만+ 패킷 필터링
- Facebook Katran: L4 로드 밸런서 — 단일 서버에서 200Gbps 처리
- kube-proxy 대체: Cilium의 host routing 모드는 XDP로 패킷 라우팅
// XDP 예시: 특정 IP 차단
SEC("xdp")
int xdp_drop_blocked_ip(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
if ((void *)eth + sizeof(*eth) > data_end)
return XDP_PASS;
if (eth->h_proto != htons(ETH_P_IP))
return XDP_PASS;
struct iphdr *ip = (void *)eth + sizeof(*eth);
if ((void *)ip + sizeof(*ip) > data_end)
return XDP_PASS;
// 특정 IP 차단 (예: 1.2.3.4)
if (ip->saddr == htonl(0x01020304))
return XDP_DROP;
return XDP_PASS;
}
4.3 Security — 보안
Falco — 런타임 보안 모니터링
- CNCF Incubating
- 시스템 콜과 K8s 이벤트를 추적하여 이상 행동 탐지
- 룰 기반 (Sysdig 필터 언어)
# Falco rule: 컨테이너에서 셸 실행 탐지
- rule: Terminal shell in container
desc: A shell was used as the entrypoint/exec point into a container
condition: >
spawned_process and container
and shell_procs and proc.tty != 0
output: >
Shell spawned in container (user=%user.name container=%container.name
shell=%proc.name pid=%proc.pid)
priority: WARNING
Tetragon — Cilium 팀의 차세대 보안
- eBPF 기반 보안 옵저버빌리티 + 런타임 강제
- 시스템 콜뿐 아니라 LSM 훅도 활용
- 실시간 차단 가능 (Falco는 탐지만)
Aqua Security, Sysdig Secure
- 상용 솔루션. eBPF 기반 컨테이너 런타임 보안
5. 실전: bpftrace로 디버깅하기
5.1 시나리오: 디스크 I/O가 갑자기 느려졌다
# 1. 어떤 프로세스가 디스크 I/O를 일으키는가?
sudo bpftrace -e '
tracepoint:block:block_rq_issue {
@[comm] = count();
}
interval:s:5 {
print(@);
clear(@);
}'
# 2. I/O 지연시간 분포 (히스토그램)
sudo bpftrace -e '
kprobe:blk_account_io_start { @start[arg0] = nsecs; }
kprobe:blk_account_io_done /@start[arg0]/ {
@ms = hist((nsecs - @start[arg0]) / 1000000);
delete(@start[arg0]);
}'
5.2 시나리오: 알 수 없는 파일이 열리고 있다
sudo bpftrace -e '
tracepoint:syscalls:sys_enter_openat {
printf("%s opened: %s\n", comm, str(args->filename));
}'
5.3 시나리오: TCP 연결이 끊긴다
sudo bpftrace -e '
kprobe:tcp_close {
$sk = (struct sock *)arg0;
$state = $sk->__sk_common.skc_state;
if ($state != 7) { // TCP_CLOSE
printf("PID %d (%s) closed TCP connection in state %d\n",
pid, comm, $state);
}
}'
6. CO-RE: 한 번 컴파일, 어디서든 실행
6.1 문제: 커널 버전마다 구조체가 다르다
// 커널 5.10에서:
struct task_struct {
int pid;
char comm[16];
// ...
};
// 커널 5.15에서 새 필드 추가:
struct task_struct {
int pid;
int tgid; // 새로 추가
char comm[16];
};
전통적인 BCC는 실행 시점에 커널 헤더를 사용해 컴파일했습니다. 하지만 컨테이너에서는 호스트 커널 헤더에 접근하기 어렵고, 컴파일 시간도 길었습니다.
6.2 해결책: BTF + CO-RE
BTF (BPF Type Format): 커널이 자신의 구조체 정보를 /sys/kernel/btf/vmlinux에 노출합니다.
CO-RE: 컴파일러가 구조체 필드 접근을 "심볼릭"하게 표현하고, 로더가 런타임에 BTF 정보로 재배치합니다.
// CO-RE 매크로 사용
#include <bpf/bpf_core_read.h>
SEC("kprobe/do_unlinkat")
int handle_unlinkat(struct pt_regs *ctx) {
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
// BPF_CORE_READ: BTF 기반 안전한 필드 읽기
pid_t pid = BPF_CORE_READ(task, pid);
bpf_printk("unlink by pid %d\n", pid);
return 0;
}
효과: 한 번 빌드한 .o 파일이 5.4부터 6.5까지 모든 커널에서 동작.
7. 프로덕션 사례
7.1 Netflix — bpftrace로 성능 디버깅
Netflix의 Brendan Gregg는 eBPF의 가장 영향력 있는 옹호자입니다. Netflix는 수만 개의 EC2 인스턴스에서 BCC와 bpftrace를 활용해:
- off-CPU 분석: 프로세스가 왜 멈춰있는지 (락, I/O 대기) 추적
- TCP 재전송 추적: 마이크로서비스 간 네트워크 문제 진단
- 파일시스템 지연시간: 어떤 파일이 느린지 즉시 식별
7.2 Cloudflare — XDP로 DDoS 방어
- 2017년 도입 후 지속 확장
- L4Drop: 초당 수천만 패킷의 악성 트래픽을 네트워크 드라이버 레벨에서 차단
- iptables 대비 10배 이상 효율
- CPU 사용량 절감으로 인프라 비용 30% 감소
7.3 Meta (Facebook) — 모든 곳에 eBPF
- Katran: L4 로드 밸런서 (XDP 기반)
- DCCP 프로파일링: 데이터센터 전반의 성능 분석
- kernel networking subsystem 대체: 일부 케이스에서 커널 스택을 우회
7.4 Google — Cilium으로 GKE 데이터플레인 V2
- GKE Dataplane V2는 Cilium 기반
- iptables 기반 kube-proxy를 완전히 대체
- 클러스터 규모가 커져도 일정한 성능
8. eBPF의 한계와 도전
8.1 검증기의 제약
- 명령어 수 제한 (현재 100만)
- 루프 제한 (BPF_LOOP 헬퍼로 완화됨)
- 스택 크기 512바이트 — 큰 구조체 조작 어려움
- "검증기와 싸우기"가 eBPF 개발의 가장 큰 학습 곡선
8.2 커널 버전 의존성
- 일부 기능은 최신 커널 필요 (예: ringbuf는 5.8+, BPF_LSM은 5.7+)
- CO-RE가 대부분 해결하지만, 새 헬퍼 함수는 여전히 커널 의존적
8.3 디버깅의 어려움
- printf("디버깅")은 불가능 —
bpf_printk()만 사용 (느림) - 런타임 에러 분석이 어려움
- 검증기 에러 메시지가 난해함
8.4 보안 우려
- eBPF 자체에서 발견된 CVE들 (검증기 우회)
- 비특권 BPF는 대부분 비활성화됨 (성능 저하)
- 잘못 작성된 프로그램이 시스템 성능에 영향
9. eBPF의 미래
9.1 BPF in Windows
Microsoft가 eBPF for Windows를 개발 중. Windows에서도 같은 eBPF 프로그램 실행 가능 → 진정한 크로스 플랫폼 커널 프로그래밍.
9.2 sched_ext — 스케줄러를 eBPF로
커널 6.12+에서 도입. CPU 스케줄러 자체를 eBPF로 작성 가능 → 워크로드별 맞춤 스케줄링.
9.3 BPF FUSE — 파일시스템
eBPF로 파일시스템 작성 가능성 탐구 중.
9.4 Confidential Computing
eBPF를 활용한 워크로드 격리와 attestation.
10. eBPF 시작하기 — 학습 로드맵
Week 1: 기초
bpftool prog list— 시스템에 로드된 BPF 프로그램 확인bpftrace설치 후 bpftrace/tools 둘러보기- "Linux Observability with BPF" (O'Reilly) 1-3장 읽기
Week 2-3: BCC
bcc-tools패키지 설치execsnoop,opensnoop,tcptop같은 도구 사용- BCC tutorial로 첫 BPF 프로그램 작성
Week 4-6: libbpf + CO-RE
- libbpf-bootstrap 클론
- 간단한 트레이싱 도구 작성 (예: 시스템 콜 카운터)
- BTF와 CO-RE 매크로 익히기
Week 7+: 실전 프로젝트
- 자신의 프로덕션 환경에 적용
- Cilium, Falco, Pixie 중 하나 깊이 학습
- 컨퍼런스: eBPF Summit, KubeCon (eBPF Day)
퀴즈
1. eBPF 프로그램이 안전하다고 보장되는 이유는 무엇인가요?
답: 커널에 로드되기 전 BPF Verifier가 정적 분석을 수행합니다. 무한 루프 금지, 메모리 접근 검증, 허용된 헬퍼 함수만 호출, 명령어 수 제한 등을 확인합니다. 검증을 통과하면 JIT 컴파일러가 네이티브 머신 코드로 변환하여 거의 네이티브 속도로 실행됩니다.
2. CO-RE(Compile Once, Run Everywhere)가 해결하는 문제는?
답: 전통적인 BCC는 실행 시점에 커널 헤더로 컴파일해야 했습니다. 이는 LLVM 의존성, 긴 시작 시간, 컨테이너 환경에서의 어려움을 야기했습니다. CO-RE는 BTF(BPF Type Format)를 활용해 컴파일러가 구조체 필드 접근을 "심볼릭"하게 표현하고, 로더가 런타임에 BTF 정보로 재배치합니다. 한 번 빌드한 .o 파일이 다양한 커널 버전에서 동작합니다.
3. XDP가 iptables보다 빠른 이유는?
답: XDP는 네트워크 드라이버 수준에서 패킷을 처리합니다. 패킷이 커널 네트워크 스택(skb 할당, conntrack 등)을 거치기 전에 처리하므로 오버헤드가 매우 적습니다. Cloudflare는 XDP로 단일 서버에서 초당 1천만+ 패킷을 처리하며, iptables 대비 10배 이상 효율적입니다.
4. Cilium이 kube-proxy를 어떻게 대체하나요?
답: kube-proxy는 iptables 또는 IPVS 규칙으로 Service ClusterIP를 백엔드 Pod로 라우팅합니다. 클러스터가 커지면 iptables 규칙이 수천 개로 늘어나 성능이 저하됩니다. Cilium은 eBPF 프로그램이 소켓 작업과 패킷 처리에서 직접 백엔드를 선택하므로 iptables 규칙이 필요 없습니다. 결과적으로 일정한 성능을 유지하며 CPU 사용량이 낮습니다.
5. bpftrace와 libbpf의 차이는 언제 어떤 것을 사용해야 하나요?
답: bpftrace는 즉석 트레이싱과 디버깅에 적합합니다. DTrace 같은 고수준 DSL로 한 줄로 강력한 분석이 가능합니다. libbpf + CO-RE는 프로덕션 도구 개발에 적합합니다. 작은 바이너리, 컴파일러 의존성 없음, 빠른 시작, 다양한 커널 호환성을 제공합니다. 개인 디버깅은 bpftrace, 배포할 도구는 libbpf로 작성하는 것이 일반적입니다.
참고 자료
- ebpf.io — 공식 사이트
- Linux Observability with BPF — David Calavera, Lorenzo Fontana
- BPF Performance Tools — Brendan Gregg
- libbpf-bootstrap — CO-RE 시작 템플릿
- bcc-tools — 100+ BPF 도구
- bpftrace — DTrace 같은 고수준 트레이싱
- Cilium Documentation
- Falco Documentation
- Brendan Gregg's eBPF page
- Awesome eBPF
- eBPF Summit — 연례 컨퍼런스
- KubeCon eBPF Day — Cilium 팀 발표
eBPF Complete Guide 2025: Kernel Programming Revolution for Observability, Networking, Security
TL;DR
- eBPF is a safe programming environment inside the Linux kernel. Add tracing/networking/security features without changing kernel code
- 3 major use cases: Observability (Pixie/Parca), Networking (Cilium/XDP), Security (Falco/Tetragon)
- CO-RE: Compile once, run on various kernel versions — eliminates deployment burden
- bpftrace: DTrace-like high-level language for ad-hoc tracing (
bpftrace -e 'tracepoint:syscalls:sys_enter_open { @[comm] = count(); }') - XDP: Packet processing at the network card level — Cloudflare uses it for DDoS defense (10M+ packets per second)
1. What is eBPF?
1.1 One-line definition
eBPF is a technology that allows you to run sandboxed programs inside the operating system kernel without modifying it.
Traditionally, extending kernel functionality required two approaches:
- Add code to the kernel itself — hard to contribute, and even when merged, users wait for new kernel versions
- Load kernel modules — risky if poorly written, vulnerable to kernel ABI changes
eBPF offers a third path: safely run custom code inside the kernel.
1.2 How is safety ensured?
Before loading, eBPF programs must pass BPF Verifier static analysis:
- Termination guarantee: No infinite loops (limits on backward jumps)
- Memory safety: Tracks all pointer accesses, bounds checks
- Function call restrictions: Only allowed helper functions
- Stack size: Maximum 512 bytes
- Instruction count: Less than 1 million (kernel 5.2+)
After verification, the JIT compiler converts to native machine code, executing at near-native speed.
1.3 Evolution from cBPF to eBPF
| Feature | cBPF (1992) | eBPF (2014~) |
|---|---|---|
| Purpose | Packet filtering only | General-purpose kernel programming |
| Registers | 2 (32-bit) | 11 (64-bit) |
| Instructions | 22 | 100+ |
| Helper functions | None | 200+ |
| Maps | None | 30+ types |
| JIT | Some architectures | Most architectures |
cBPF used by tcpdump was a simple packet filter, but eBPF evolved into a mini virtual machine.
2. eBPF Architecture Deep Dive
2.1 Component Overview
┌─────────────────────────────────────────────┐
│ User Space │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ bpftool │ │ bpftrace │ │ Cilium │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ │ bpf() syscall │
└─────────────────────┼───────────────────────┘
▼
┌─────────────────────────────────────────────┐
│ Kernel Space │
│ ┌─────────────────────────────────────┐ │
│ │ BPF Verifier (safety verification) │ │
│ └──────────────┬──────────────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ JIT Compiler (native code) │ │
│ └──────────────┬──────────────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Hooks: kprobe, tracepoint, XDP, │ │
│ │ perf_event, socket, cgroup, LSM │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
2.2 Major Hook Types
eBPF programs can attach to various kernel events:
| Hook Type | Description | Example |
|---|---|---|
| kprobe / kretprobe | Kernel function entry/exit | Trace do_sys_open calls |
| uprobe / uretprobe | User-space functions | Trace OpenSSL SSL_read |
| tracepoint | Static kernel tracepoints | sched:sched_switch |
| perf_event | Hardware/software counters | CPU cycles, cache misses |
| XDP | Network driver | DDoS filtering |
| TC (Traffic Control) | Traffic shaping | QoS, load balancing |
| socket | Socket operations | Socket filters |
| cgroup | Cgroup events | Network isolation |
| LSM (Linux Security Modules) | Security policies | File access control |
2.3 BPF Maps — Core Data Structures
Data sharing between eBPF programs and user space happens through BPF Maps:
| Map Type | Purpose |
|---|---|
BPF_MAP_TYPE_HASH | General key-value storage |
BPF_MAP_TYPE_ARRAY | Array (index-based) |
BPF_MAP_TYPE_PERF_EVENT_ARRAY | Streaming events to user space |
BPF_MAP_TYPE_RINGBUF | More efficient event streaming (kernel 5.8+) |
BPF_MAP_TYPE_LRU_HASH | LRU cache |
BPF_MAP_TYPE_LPM_TRIE | Longest prefix match (used in routing) |
BPF_MAP_TYPE_PROG_ARRAY | Tail call jump table |
// BPF_MAP_TYPE_HASH example: counting syscalls per PID
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __u32); // PID
__type(value, __u64); // count
__uint(max_entries, 10240);
} syscall_count SEC(".maps");
3. Development Tools Comparison
3.1 BCC (BPF Compiler Collection)
- Languages: C for BPF, Python/Lua for user space
- Pros: Rich tooling (100+ tools in bcc-tools package)
- Cons: Compiler dependency (LLVM at runtime), large binaries
3.2 bpftrace — DTrace's Successor
- Language: High-level awk-like DSL
- Use case: Ad-hoc tracing, debugging
- Strength: Powerful analysis in one line
# Count syscalls per process
bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[comm] = count(); }'
# Trace read() calls taking longer than 1 second
bpftrace -e '
kprobe:vfs_read { @start[tid] = nsecs; }
kretprobe:vfs_read /@start[tid]/ {
$duration = nsecs - @start[tid];
if ($duration > 1000000000) {
printf("%s pid %d took %d ms\n", comm, pid, $duration / 1000000);
}
delete(@start[tid]);
}'
# Top CPU functions (on-CPU profiling)
bpftrace -e 'profile:hz:99 { @[ustack] = count(); }'
3.3 libbpf + CO-RE — Production Standard
- CO-RE (Compile Once, Run Everywhere): Embeds kernel struct info via BTF — runs across kernel versions
- Pros: Small binaries, no compiler dependency, fast startup
- Cons: Steep learning curve
// libbpf example: tracing openat()
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
char LICENSE[] SEC("license") = "GPL";
SEC("tracepoint/syscalls/sys_enter_openat")
int handle_openat(struct trace_event_raw_sys_enter *ctx) {
char comm[16];
bpf_get_current_comm(&comm, sizeof(comm));
bpf_printk("openat by %s\n", comm);
return 0;
}
3.4 Tools Comparison
| Tool | Learning Curve | Performance | Deployment | Best For |
|---|---|---|---|---|
| BCC | Medium | Medium | Hard (needs LLVM) | Learning, one-off analysis |
| bpftrace | Easy | High | Medium | Ad-hoc tracing, debugging |
| libbpf + CO-RE | Hard | Very High | Easy | Production tool development |
| Aya (Rust) | Hard | Very High | Easy | Rust ecosystem integration |
| ebpf-go | Medium | Very High | Easy | Go application integration |
4. Three Major Use Cases of eBPF
4.1 Observability
eBPF observes system behavior deeply without changing application code.
Pixie — Kubernetes Native Observability
- Open-source project acquired by New Relic
- Auto-traces HTTP/gRPC/MySQL requests in all Pods without sidecars
- BPF code attaches uprobes to OpenSSL
SSL_read/SSL_writeto capture data before encryption
Parca — Continuous Profiling
- 24/7 CPU profiling in production
- Less than 1% overhead
- Open source by Polar Signals
4.2 Networking
Cilium — eBPF-based K8s Networking Standard
- CNCF Graduated project
- Complete kube-proxy replacement — eBPF for service routing instead of iptables
- L7 policy support (HTTP, gRPC, Kafka)
- Hubble: Cilium-based network observability
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-api-only
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: '8080'
rules:
http:
- method: 'GET'
path: '/api/v1/.*'
XDP — Ultra-fast Packet Processing
XDP processes packets at the network driver level — bypassing the kernel network stack.
- Cloudflare: Uses XDP for DDoS defense — filters 10M+ packets per second
- Facebook Katran: L4 load balancer — 200Gbps on a single server
- kube-proxy replacement: Cilium's host routing mode uses XDP
4.3 Security
Falco — Runtime Security Monitoring
- CNCF Incubating
- Tracks system calls and K8s events to detect anomalies
- Rule-based (Sysdig filter language)
- rule: Terminal shell in container
desc: A shell was used as the entrypoint/exec point into a container
condition: >
spawned_process and container
and shell_procs and proc.tty != 0
output: >
Shell spawned in container (user=%user.name container=%container.name
shell=%proc.name pid=%proc.pid)
priority: WARNING
Tetragon — Cilium Team's Next-Generation Security
- eBPF-based security observability + runtime enforcement
- Uses LSM hooks in addition to syscalls
- Real-time blocking capability (Falco only detects)
5. Practical: Debugging with bpftrace
5.1 Scenario: Disk I/O suddenly slow
# 1. Which process is causing disk I/O?
sudo bpftrace -e '
tracepoint:block:block_rq_issue {
@[comm] = count();
}
interval:s:5 {
print(@);
clear(@);
}'
# 2. I/O latency distribution (histogram)
sudo bpftrace -e '
kprobe:blk_account_io_start { @start[arg0] = nsecs; }
kprobe:blk_account_io_done /@start[arg0]/ {
@ms = hist((nsecs - @start[arg0]) / 1000000);
delete(@start[arg0]);
}'
5.2 Scenario: Unknown files being opened
sudo bpftrace -e '
tracepoint:syscalls:sys_enter_openat {
printf("%s opened: %s\n", comm, str(args->filename));
}'
6. CO-RE: Compile Once, Run Everywhere
6.1 Problem: Struct definitions vary between kernels
Traditional BCC compiled with kernel headers at runtime. But containers struggle to access host kernel headers, and compile times were long.
6.2 Solution: BTF + CO-RE
BTF (BPF Type Format): Kernel exposes its struct info at /sys/kernel/btf/vmlinux.
CO-RE: Compiler represents struct field accesses "symbolically", and the loader relocates them at runtime using BTF info.
#include <bpf/bpf_core_read.h>
SEC("kprobe/do_unlinkat")
int handle_unlinkat(struct pt_regs *ctx) {
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
// BPF_CORE_READ: BTF-based safe field reading
pid_t pid = BPF_CORE_READ(task, pid);
bpf_printk("unlink by pid %d\n", pid);
return 0;
}
Result: A single compiled .o file works on kernels 5.4 to 6.5.
7. Production Case Studies
7.1 Netflix — Performance Debugging with bpftrace
Netflix's Brendan Gregg is eBPF's most influential advocate. Netflix uses BCC and bpftrace across tens of thousands of EC2 instances:
- Off-CPU analysis: Why processes are blocked (locks, I/O wait)
- TCP retransmission tracing: Diagnosing network issues between microservices
- Filesystem latency: Instantly identifying slow files
7.2 Cloudflare — DDoS Defense with XDP
- Adopted in 2017, continuously expanded
- L4Drop: Blocks tens of millions of malicious packets per second at the network driver level
- 10x more efficient than iptables
- 30% infrastructure cost reduction from CPU savings
7.3 Meta — eBPF Everywhere
- Katran: L4 load balancer (XDP-based)
- DCCP profiling: Performance analysis across data centers
- Kernel networking subsystem replacement: Bypassing kernel stack in some cases
7.4 Google — GKE Dataplane V2 with Cilium
- GKE Dataplane V2 is Cilium-based
- Completely replaces iptables-based kube-proxy
- Consistent performance even as cluster size grows
8. Limitations and Challenges
8.1 Verifier Constraints
- Instruction count limit (currently 1 million)
- Loop restrictions (eased by BPF_LOOP helper)
- 512-byte stack — hard to manipulate large structs
- "Fighting the verifier" is the steepest learning curve
8.2 Kernel Version Dependency
- Some features need recent kernels (e.g., ringbuf is 5.8+, BPF_LSM is 5.7+)
- CO-RE solves most issues, but new helpers are still kernel-dependent
8.3 Debugging Difficulty
- printf("debug") is impossible — only
bpf_printk()(slow) - Runtime error analysis is hard
- Verifier error messages are cryptic
9. The Future of eBPF
9.1 BPF in Windows
Microsoft is developing eBPF for Windows. Same eBPF programs can run on Windows → true cross-platform kernel programming.
9.2 sched_ext — Scheduler in eBPF
Introduced in kernel 6.12+. Write the CPU scheduler itself in eBPF → workload-specific custom scheduling.
10. Getting Started — Learning Roadmap
Week 1: Basics
bpftool prog list— Check loaded BPF programs- Install
bpftraceand explore bpftrace/tools - Read "Linux Observability with BPF" (O'Reilly) chapters 1-3
Week 2-3: BCC
- Install
bcc-toolspackage - Use tools like
execsnoop,opensnoop,tcptop - Write your first BPF program with BCC tutorial
Week 4-6: libbpf + CO-RE
- Clone libbpf-bootstrap
- Write a simple tracing tool (e.g., syscall counter)
- Master BTF and CO-RE macros
Week 7+: Real Projects
- Apply to your production environment
- Deep dive into Cilium, Falco, or Pixie
- Conferences: eBPF Summit, KubeCon (eBPF Day)
Quiz
1. Why are eBPF programs guaranteed to be safe?
Answer: Before loading into the kernel, the BPF Verifier performs static analysis. It checks for infinite loops, memory access validation, only allows approved helper functions, and limits instruction count. After verification passes, the JIT compiler converts to native machine code, running at near-native speed.
2. What problem does CO-RE solve?
Answer: Traditional BCC required compiling with kernel headers at runtime. This caused LLVM dependency, long startup times, and difficulties in container environments. CO-RE uses BTF to allow the compiler to represent struct field accesses "symbolically", with the loader performing runtime relocation. A single compiled .o file works across various kernel versions.
3. Why is XDP faster than iptables?
Answer: XDP processes packets at the network driver level. Packets are processed before they enter the kernel network stack (skb allocation, conntrack, etc.), resulting in very low overhead. Cloudflare processes 10M+ packets per second on a single server with XDP, achieving 10x more efficiency than iptables.
4. How does Cilium replace kube-proxy?
Answer: kube-proxy uses iptables or IPVS rules to route Service ClusterIPs to backend Pods. As clusters grow, iptables rules can reach thousands, degrading performance. Cilium's eBPF programs select backends directly during socket operations and packet processing, eliminating the need for iptables rules. The result is consistent performance with low CPU usage.
5. When should you use bpftrace vs libbpf?
Answer: bpftrace is suitable for ad-hoc tracing and debugging. Its DTrace-like high-level DSL allows powerful analysis in one line. libbpf + CO-RE is suitable for production tool development — small binaries, no compiler dependency, fast startup, broad kernel compatibility. Personal debugging uses bpftrace; tools you ship use libbpf.
References
- ebpf.io — Official site
- Linux Observability with BPF — David Calavera, Lorenzo Fontana
- BPF Performance Tools — Brendan Gregg
- libbpf-bootstrap — CO-RE starter template
- bcc-tools — 100+ BPF tools
- bpftrace — DTrace-like high-level tracing
- Cilium Documentation
- Falco Documentation
- Brendan Gregg's eBPF page
- Awesome eBPF
- eBPF Summit — Annual conference
- KubeCon eBPF Day — Cilium team presentations