eBPF 2026 - Kiến trúc Observability, Networking và Runtime Security cho Linux Production với Cilium, Tetragon, Parca, OpenTelemetry eBPF, XDP, Sockmap, CO-RE và Auto-instrumentation không Sidecar
Posted on: 4/15/2026 8:11:22 PM
Table of contents
- 1. eBPF - Công nghệ đang viết lại cách chúng ta quan sát và điều khiển Linux
- 2. Từ BPF gốc tới eBPF hiện đại - bản đồ tiến hóa
- 3. Kiến trúc eBPF - verifier, JIT, maps và helpers
- 4. Hook points - eBPF gắn vào những đâu trong kernel?
- 5. eBPF cho Networking - Cilium, XDP và bypass netfilter
- 6. eBPF cho Observability - profiling và auto-instrumentation
- 7. eBPF cho Runtime Security - Tetragon, Falco và LSM BPF
- 8. CO-RE - điểm đổi đời cho eBPF portable
- 9. Kiến trúc tham chiếu: Observability + Networking + Security trên cùng node
- 10. So sánh eBPF với các cách tiếp cận truyền thống
- 11. Thực chiến - viết và nạp một chương trình eBPF với libbpf CO-RE
- 12. Giới hạn và những điểm cần tỉnh táo
- 13. Checklist triển khai eBPF production 2026
- 14. Kết luận - eBPF là tầng hạ tầng mới của Linux production
- 15. Nguồn tham khảo
1. eBPF - Công nghệ đang viết lại cách chúng ta quan sát và điều khiển Linux
Nếu phải chọn một công nghệ hạ tầng có tác động sâu rộng nhất tới cách các hệ thống Linux production được quan sát, bảo vệ và kết nối trong năm năm gần đây, câu trả lời gần như không có đối thủ: đó là eBPF. Từ chỗ là một nhánh nhỏ của Berkeley Packet Filter sinh ra để lọc gói tin tcpdump, eBPF đã trở thành nền tảng cho Cilium, Tetragon, Pixie, Parca, Pyroscope, Katran, và là xương sống của các dịch vụ đám mây như Facebook Load Balancer, Google Cloud Service Mesh, Azure CNI, AWS VPC CNI mới.
Điều làm eBPF đặc biệt không nằm ở việc nó cho phép viết "chương trình chạy trong kernel" - DTrace đã làm điều tương tự hơn hai thập kỷ trước. Điểm đột phá là eBPF đặt một verifier tĩnh và một JIT compiler giữa chương trình người dùng và đường thực thi kernel, biến mọi can thiệp trở thành an toàn theo kiểm chứng. Bạn có thể thêm một khối logic quan sát mới vào production kernel đang chạy mà không cần biên dịch lại kernel, không cần load module, không cần reboot, và không có nguy cơ panic.
Vì sao một kỹ sư hệ thống năm 2026 bắt buộc phải hiểu eBPF?
Các stack observability, networking, và runtime security thế hệ mới đều đang dịch chuyển từ mô hình sidecar/agent trong userspace sang mô hình in-kernel instrumentation. Điều này làm giảm đáng kể overhead, xóa bỏ vấn đề đồng bộ cấu hình, và mang lại một điểm quan sát duy nhất cho mọi tiến trình trên node - bất kể ngôn ngữ, runtime, hay container runtime. Nếu hệ thống bạn đang vận hành có Kubernetes, có service mesh, có APM, có profiling hoặc có compliance runtime, thì nhiều khả năng eBPF đã và đang ở dưới lớp hạ tầng đó - chỉ là bạn chưa nhìn thấy.
2. Từ BPF gốc tới eBPF hiện đại - bản đồ tiến hóa
Để hiểu tại sao eBPF lại có kiến trúc như hiện tại, cần nhìn lại hành trình hai thập kỷ của một con máy ảo nhỏ bé trong kernel Linux.
tcpdump mà không phải copy từng gói lên userspace. Một máy ảo nhỏ, 32-bit, hai thanh ghi, chạy trong kernel BSD.AF_PACKET và tcpdump. Trong gần hai thập kỷ sau đó, cBPF gần như không thay đổi - vẫn là một filter engine đơn giản cho gói tin.3. Kiến trúc eBPF - verifier, JIT, maps và helpers
Trước khi đi vào các ứng dụng như Cilium hay Pixie, cần hiểu rõ luồng sống của một chương trình eBPF từ lúc được viết cho tới lúc chạy trong kernel. Toàn bộ giá trị của eBPF - bao gồm cả tính an toàn, tính portable và hiệu năng - đều xuất phát từ chuỗi mắt xích này.
flowchart LR
A[Source C/Rust] --> B[Clang+LLVM BPF backend]
B --> C[ELF object with BTF]
C --> D[Loader libbpf]
D --> E[bpf syscall]
E --> F[Verifier]
F -->|safe| G[JIT compiler]
F -->|unsafe| X[Reject]
G --> H[Native code in kernel]
H --> I[Hook point: kprobe/tc/xdp/lsm...]
I --> J[Run on event]
J --> K[Read/Write Maps]
K -.-> L[Userspace reader]
Hình 1: Luồng sống của một chương trình eBPF - từ mã nguồn tới lúc chạy trong kernel
3.1. Verifier - vệ binh tĩnh của kernel
Verifier là thành phần làm eBPF trở thành eBPF. Trước khi một byte instruction nào được JIT và chạy trong kernel, verifier thực hiện một quá trình phân tích static nghiêm ngặt:
- Chứng minh chương trình kết thúc. Verifier đi qua mọi nhánh có thể có của control flow graph. Chương trình eBPF cổ điển không được phép có vòng lặp không cận; kernel 5.3+ cho phép bounded loops nhưng số lần lặp phải xác định được tĩnh.
- Chứng minh mọi truy cập bộ nhớ đều hợp lệ. Verifier theo dấu vùng nhớ gốc của mỗi con trỏ (packet, stack, map value, context) và từ chối bất kỳ truy cập nào ngoài biên. Không có dereference ngẫu nhiên.
- Chứng minh không có privilege leak. Các con trỏ kernel không được phép chảy về userspace qua maps hay return value.
- Giới hạn độ phức tạp. Mặc định 1 triệu instruction state-expansion. Vượt quá sẽ bị từ chối, ngay cả khi chương trình vốn đúng về mặt logic.
Đó là lý do viết eBPF "sạch" không giống viết C thông thường: mỗi con trỏ phải được kiểm tra null ngay sau khi nhận; mỗi vùng packet phải được bounds check trước khi dereference; mọi vòng lặp phải có giới hạn hằng số. Người viết lần đầu thường mất vài ngày chỉ để "hiểu verifier".
3.2. Maps - cấu trúc dữ liệu chia sẻ kernel <-> userspace
Chương trình eBPF không có toàn cục. Toàn bộ dữ liệu duy trì trạng thái đều nằm trong maps - những cấu trúc dữ liệu do kernel quản lý, có khóa bằng fd, có thể mmap từ userspace. Các loại map thường gặp:
| Loại map | Công dụng điển hình | Đặc điểm |
|---|---|---|
BPF_MAP_TYPE_HASH | Bảng tra cứu generic key-value | Concurrency qua spinlock, cỡ cố định |
BPF_MAP_TYPE_LRU_HASH | Cache connection, flow table | Eviction LRU tự động |
BPF_MAP_TYPE_PERCPU_HASH | Đếm sự kiện hot path | Không khóa, phải merge ở userspace |
BPF_MAP_TYPE_ARRAY | Counter, config cố định | Index nguyên, truy cập O(1) |
BPF_MAP_TYPE_RINGBUF | Streaming sự kiện lên userspace | MPSC, không mất event khi full, thay thế perf ring buffer |
BPF_MAP_TYPE_LPM_TRIE | Longest prefix match cho IP/CIDR | Xương sống của routing eBPF |
BPF_MAP_TYPE_SOCKHASH | Sockmap cho socket redirect | Cho phép zero-copy giữa hai socket |
BPF_MAP_TYPE_STACK_TRACE | Lưu stack trace kernel/user | Nền tảng của profiling eBPF |
3.3. Helpers và kfuncs - API ổn định giữa eBPF và kernel
Chương trình eBPF không thể gọi trực tiếp hàm kernel bất kỳ. Nó chỉ được gọi một tập hợp có kiểm soát gồm helpers và kfuncs. Helpers là tập API cũ, được kernel chấp nhận là ổn định (ví dụ bpf_map_lookup_elem, bpf_ktime_get_ns, bpf_get_current_pid_tgid). Kfuncs là cơ chế mới hơn, cho phép kernel module khai báo hàm mà eBPF có thể gọi với kiểu dữ liệu rõ ràng, verifier chịu trách nhiệm kiểm tra chữ ký. Về dài hạn kfunc đang dần thay thế helper vì tính linh hoạt và khả năng tiến hóa tốt hơn.
3.4. JIT - từ bytecode tới native code
Sau khi verifier cho qua, JIT compiler chuyển bytecode eBPF thành mã máy native cho kiến trúc đang chạy (x86_64, arm64, riscv, ppc64). Native code này nằm trong vùng nhớ kernel executable, không bị phân mảnh bởi interpret, và chạy gần như không phân biệt được với code kernel tự viết. Trên x86_64 mặc định mỗi call tới helper là một call trực tiếp sau khi JIT, không qua bảng dispatch.
4. Hook points - eBPF gắn vào những đâu trong kernel?
eBPF có thể gắn vào rất nhiều điểm trong kernel. Mỗi hook point có context riêng (cấu trúc dữ liệu mà chương trình nhận được) và ngữ nghĩa khác nhau về cái mà chương trình có thể đọc/ghi. Dưới đây là các loại hook phổ biến nhất:
| Hook | Vị trí | Ngữ cảnh | Dùng cho |
|---|---|---|---|
| kprobe / kretprobe | Đầu/cuối hàm kernel bất kỳ | pt_regs | Trace nội bộ kernel, debugging runtime |
| fentry / fexit | Đầu/cuối hàm kernel (BTF-based) | Tham số thật của hàm | Thay thế kprobe với overhead thấp hơn và type-safe |
| uprobe / uretprobe | Hàm userspace | pt_regs | Trace Go, Python, Node, JVM runtime |
| tracepoint / raw_tp | Các tracepoint tĩnh kernel định nghĩa sẵn | Struct tracepoint | Ổn định theo ABI, dùng cho observability chính thức |
| XDP | Đầu driver NIC, trước SKB | xdp_md | Packet processing tốc độ đường truyền |
| tc (traffic control) | Ingress/egress qdisc | __sk_buff | Policy mạng, load balancing L3/L4 |
| cgroup skb | Gói tin vào/ra cgroup | __sk_buff | Network policy theo pod/container |
| sock_ops / sockmap | Vòng đời TCP socket | bpf_sock_ops | Redirect socket, tối ưu TCP, service mesh không sidecar |
| LSM hook | Các security hook của kernel | Đối số LSM | Runtime security, access control |
| perf_event | Sự kiện phần cứng/PMU | Sample | Continuous profiling, CPU performance analysis |
Khi nào chọn hook nào?
Quy tắc thực dụng: tracepoint/raw_tp ổn định về ABI và nên ưu tiên cho observability production; fentry/fexit cho phép quan sát hàm kernel bất kỳ với overhead thấp nhất nhưng đòi kernel có BTF; kprobe chỉ nên dùng khi hai cái trên không phủ được; XDP/tc dành cho data plane mạng; LSM BPF cho runtime security.
5. eBPF cho Networking - Cilium, XDP và bypass netfilter
Cilium là minh chứng rõ nét nhất cho khả năng của eBPF ở tầng mạng. Thay vì dựa vào iptables - một cơ chế chuỗi rule tuần tự có độ phức tạp O(n) và sinh ra conntrack lớn khi số pod tăng - Cilium cài đặt lại toàn bộ data path Kubernetes bằng chương trình eBPF gắn vào tc và XDP.
5.1. Vì sao eBPF đánh bại iptables ở scale cụm lớn?
Một cụm Kubernetes trung bình có vài nghìn service và hàng chục nghìn pod. Kube-proxy với iptables phải build chuỗi rule có độ dài tỉ lệ tuyến tính với số service × số endpoint. Khi số rule tăng, cả thời gian build lại và thời gian match từng gói đều đi lên. Ngược lại, eBPF có thể dùng BPF_MAP_TYPE_HASH để tra cứu endpoint mục tiêu theo VIP trong O(1), và toàn bộ quyết định định tuyến xảy ra bên trong một chương trình JIT native.
flowchart TB
subgraph Host
direction TB
N[NIC] --> X[XDP program]
X -->|drop/pass| T[tc ingress]
T --> C[cgroup skb]
C --> P[Pod network namespace]
P --> E[cgroup egress]
E --> Y[tc egress]
Y --> N2[NIC out]
end
X -. lookup .-> M1[(LPM trie
IP blocks)]
T -. lookup .-> M2[(Hash map
Service VIP)]
C -. lookup .-> M3[(Hash map
Policy)]
Hình 2: Data path Cilium - chuỗi chương trình eBPF thay thế iptables và kube-proxy
5.2. XDP - tốc độ đường truyền trên commodity hardware
XDP chạy trước cả khi kernel allocate sk_buff, nghĩa là nó thấy gói tin ngay khi driver NIC đặt DMA descriptor. Ba hành động XDP trả về - XDP_DROP, XDP_PASS, XDP_TX, XDP_REDIRECT - đủ để cài đặt các load balancer như Katran (Meta) và XDP-based DDoS mitigation xử lý hàng chục triệu pps/core. Với DDoS mitigation dựa trên iptables, cùng lưu lượng sẽ làm CPU sụp từ rất sớm vì mỗi gói tạo SKB và chạy qua chuỗi netfilter; với XDP, quyết định drop xảy ra trước bước đó và về cơ bản không tốn gì ngoài vài chục chu kỳ CPU.
5.3. Sockmap - service mesh không sidecar
Hai tiến trình trên cùng một host giao tiếp qua TCP thường đi một đường vòng dài: user buffer → kernel socket → TCP stack → loopback → TCP stack → socket → user buffer. Với sockmap, eBPF có thể redirect hai socket "dính nhau": dữ liệu ghi vào sk_msg của socket này được chuyển thẳng sang socket đối tác, bỏ qua toàn bộ TCP stack trung gian. Đây là cơ chế làm cho service mesh eBPF-based (Cilium Service Mesh, Istio Ambient Mesh) có thể loại bỏ sidecar Envoy mà vẫn giữ đặc tính L7 load balancing, mTLS và retry.
6. eBPF cho Observability - profiling và auto-instrumentation
6.1. Continuous profiling với Parca/Pyroscope
Trước eBPF, continuous profiling ở scale production là chuyện khó: gắn profiler vào từng runtime (Java, Go, Python) vừa tốn CPU vừa xung đột với GC. eBPF cung cấp một đường vào duy nhất: gắn chương trình eBPF vào perf_event với tần suất lấy mẫu 19 hoặc 97 Hz, mỗi sample dùng bpf_get_stackid để lấy stack trace kernel và user, lưu vào BPF_MAP_TYPE_STACK_TRACE. Overhead điển hình dưới 1% CPU trên cả host, không cần thay đổi ứng dụng, hoạt động với mọi ngôn ngữ.
Thách thức còn lại là symbolization: stack trace chỉ có địa chỉ, phải được giải mã ngược về tên hàm. Đối với binary native, Parca/Pyroscope dùng BuildID + debug info (debuginfod). Đối với runtime JIT như JVM, .NET và V8, cần perf map hoặc agent runtime phát ra mapping. DWARF unwinding thuần eBPF (frame-pointer-less unwind) đã được hoàn thiện trong Parca Agent 0.20+, giảm đáng kể yêu cầu về frame pointer trong binary production.
6.2. Auto-instrumentation HTTP/gRPC không cần sửa code
Nếu muốn thấy một span trace cho mọi request HTTP mà ứng dụng Go/Node/Python/Java đang phục vụ, phương án cổ điển là import OpenTelemetry SDK vào code và build lại. Phương án eBPF mới hơn: gắn uprobe vào các hàm đã biết của runtime (Go: net/http.(*Server).Serve, Node: TLSWrap::OnRead, Python: PyEval_EvalFrameEx), đọc cấu trúc hàm từ uprobe context, và phát span trực tiếp sang collector.
flowchart LR
P[Process Go/Node/Java] --> U[uprobe / USDT]
K[Kernel tracepoint tcp] --> U
U --> R[Ring buffer]
R --> A[Userspace agent]
A --> B[Build spans OTLP]
B --> O[OpenTelemetry Collector]
O --> X[Traces / Metrics / Logs]
Hình 3: Auto-instrumentation eBPF - từ uprobe tới OTLP không cần sửa ứng dụng
Các dự án thực tế đi theo hướng này gồm: OpenTelemetry eBPF profiler (dự án CNCF đã ổn định), Pixie (New Relic), Grafana Beyla, Odigos, và Coroot. Điểm chung của chúng là một binary agent duy nhất chạy trên mỗi node và sinh ra tín hiệu observability cho mọi ngôn ngữ, thay cho việc tích hợp SDK vào từng service.
Giới hạn cần biết trước khi gỡ toàn bộ SDK
Auto-instrumentation eBPF cho bạn một khối lượng lớn dữ liệu "miễn phí", nhưng có những thứ nó không thể thấy: business context (tenant id, customer id), correlation thủ công giữa các service khác kernel, các sampling quyết định theo domain. Chiến lược dài hạn là kết hợp: auto-instrumentation làm baseline, SDK làm rich context ở các điểm quan trọng.
7. eBPF cho Runtime Security - Tetragon, Falco và LSM BPF
Năm 2020 kernel thêm BPF LSM, cho phép một chương trình eBPF cắm vào các hook an ninh của Linux (file_open, bprm_check_security, socket_connect, v.v.) và trả về 0 hoặc lỗi. Điều này làm cho eBPF trở thành công cụ runtime security khả thi thay vì chỉ là công cụ quan sát.
7.1. Quan sát + Enforce trong một đường
Tetragon của Isovalent là đại diện tiêu biểu: mỗi policy là một CRD mô tả quy luật, ví dụ "không một tiến trình nào trong pod có label tier=frontend được phép mở socket đến subnet 10.0.5.0/24". Tetragon sinh ra chương trình eBPF thực thi quy luật này ở ngay hook kernel, phát event khi xảy ra vi phạm và tùy chọn kill process ngay trong kernel trước khi syscall trả về userspace. Phản ứng ở microseconds - thay vì hàng chục millisecond của các agent userspace phải đi qua Netlink.
7.2. So sánh nhanh với mô hình cổ điển
| Khía cạnh | Agent userspace truyền thống | Tetragon / eBPF LSM |
|---|---|---|
| Điểm đặt | Sau khi syscall hoàn thành | Ở chính hook kernel |
| Độ trễ phát hiện | 10-100 ms | <100 µs |
| Khả năng chặn | Không (chỉ phát hiện) | Có (trả lỗi từ kernel) |
| Overhead | 3-10% CPU node điển hình | <1-2% CPU node điển hình |
| Phụ thuộc | Một daemon mỗi node | Một daemon + BTF kernel |
8. CO-RE - điểm đổi đời cho eBPF portable
Cho đến 2018, muốn phân phối một chương trình eBPF, bạn phải biên dịch nó trên đúng kernel mà node production đang chạy, vì offset của các struct kernel thay đổi giữa các phiên bản. Cách làm khi đó là BCC: mang clang, LLVM và kernel headers xuống mỗi node và biên dịch runtime. Rất chậm, rất cồng kềnh, và rất khó vận hành.
Compile Once - Run Everywhere (CO-RE) xoay ngược bài toán bằng hai thành phần:
- BTF - BPF Type Format. Kernel (từ 5.4+) tự mô tả kiểu dữ liệu của mình qua BTF và expose ở
/sys/kernel/btf/vmlinux. Bất kỳ ai cũng có thể đọc BTF để biết layout struct của kernel đang chạy. - CO-RE relocations. Khi biên dịch, libbpf thay vì hard-code offset sẽ chèn relocation placeholder. Lúc load, libbpf đọc BTF kernel đích, tính ra offset thực và patch vào bytecode trước khi đưa cho verifier.
Kết quả: một ELF file duy nhất chạy được trên hầu hết các kernel 4.18+. Các binary production như Cilium, Tetragon, Parca Agent, Beyla đều là CO-RE thuần túy - không mang theo clang, không đọc kernel headers.
9. Kiến trúc tham chiếu: Observability + Networking + Security trên cùng node
Một hệ thống production điển hình năm 2026 thường chạy nhiều thành phần eBPF song song: CNI plugin (Cilium), profiler (Parca), auto-instrumentation (Beyla/OTel eBPF profiler), và runtime security (Tetragon). Cả bốn đều cắm vào kernel của cùng một node. Để chúng hoạt động ổn định, cần thiết kế có ý thức:
flowchart TB
subgraph Node[Node Linux - Kernel 6.x with BTF]
direction TB
subgraph Programs[eBPF programs]
C1[Cilium
tc/xdp/cgroup]
C2[Parca Agent
perf_event]
C3[OTel eBPF
uprobe/kprobe]
C4[Tetragon
LSM/tracepoint]
end
Programs --> M[Shared Kernel]
end
C1 --> MN[Metrics + Hubble flows]
C2 --> PR[Profile samples]
C3 --> OT[OTLP traces]
C4 --> EV[Security events]
MN --> OTC[OpenTelemetry Collector]
PR --> OTC
OT --> OTC
EV --> SIEM[SIEM / Audit]
OTC --> BE[Grafana / Tempo / Loki / Prometheus]
Hình 4: Kiến trúc tham chiếu - bốn "người dùng" eBPF cùng chạy trên một node production
9.1. Các lưu ý thiết kế khi chạy nhiều eBPF trên cùng kernel
- Kernel BTF là bắt buộc. Tất cả các binary trên nên là CO-RE. Distro không bật
CONFIG_DEBUG_INFO_BTFsẽ phải dùng BTFhub hoặc tự sinh BTF; đây là rào cản thật khi vận hành các legacy kernel. - Chia sẻ hook an toàn. Với fentry/fexit cùng hàm, các agent không xung đột; với kprobe cùng địa chỉ, kernel serialize nên vẫn chạy được nhưng overhead cộng dồn. Tránh hai agent cùng kprobe hàm hot path.
- Ringbuf nên được ưu tiên hơn perf buffer. Ringbuf cho mỗi agent không cần một buffer per-CPU, tiết kiệm bộ nhớ nhiều khi node có 64-128 core.
- Giới hạn khối lượng map. Tổng khối lượng map của tất cả agent bị tính vào
RLIMIT_MEMLOCK. Kernel 5.11+ mặc định không còn phụ thuộc rlim này nhưng một số distro vẫn giữ, cần audit. - Giữ kernel up-to-date. Hầu hết lỗi eBPF nghiêm trọng trong vài năm qua đều được sửa ở kernel mainline. Rollout một baseline 6.1 LTS trở lên cho node Linux là quyết định vận hành đúng năm 2026.
10. So sánh eBPF với các cách tiếp cận truyền thống
| Vấn đề | Cách cũ | Cách eBPF | Hệ quả |
|---|---|---|---|
| Network policy Kubernetes | iptables/kube-proxy | Cilium tc/XDP | Giảm độ trễ p99, xóa chuỗi rule tuyến tính |
| Service mesh L7 | Envoy sidecar mỗi pod | Sockmap + envoy ambient | Giảm 30-50% CPU overhead mesh |
| APM tracing | SDK trong code | Auto-instrument eBPF | Không phải build lại ứng dụng |
| Continuous profiling | Profiler theo runtime | perf_event eBPF | Cross-language, <1% overhead |
| Runtime security | Audit daemon + Netlink | Tetragon LSM BPF | Enforce trong kernel, độ trễ microsecond |
| L4 load balancer | LVS, IPVS | Katran, Cilium LB | Triệu cps/core trên commodity NIC |
| DDoS mitigation | iptables drop | XDP drop | Drop xảy ra trước SKB allocation |
| DNS policy | CoreDNS plugin | eBPF DNS redirect/ECS rewrite | Policy L7 không tốn sidecar |
11. Thực chiến - viết và nạp một chương trình eBPF với libbpf CO-RE
Để cảm nhận được vòng đời thực tế, dưới đây là khung xương một chương trình eBPF "theo dõi các lần open file" thuần libbpf CO-RE. Đây không phải là một chương trình production - mục đích là minh họa chuỗi biên dịch và nạp vào kernel.
// trace_open.bpf.c
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
struct event {
__u32 pid;
char comm[16];
char filename[128];
};
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 20);
} events SEC(".maps");
SEC("fentry/do_sys_openat2")
int BPF_PROG(trace_openat, int dfd, const char *filename)
{
struct event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (!e) return 0;
e->pid = bpf_get_current_pid_tgid() >> 32;
bpf_get_current_comm(&e->comm, sizeof(e->comm));
bpf_probe_read_user_str(&e->filename, sizeof(e->filename), filename);
bpf_ringbuf_submit(e, 0);
return 0;
}
char LICENSE[] SEC("license") = "GPL";
Biên dịch bằng clang với target bpf, nhúng BTF, rồi sinh skeleton bằng bpftool gen skeleton. Phía userspace dùng skeleton để open, load, attach và poll ringbuf:
// loader.c (rút gọn)
struct trace_open_bpf *skel = trace_open_bpf__open_and_load();
trace_open_bpf__attach(skel);
struct ring_buffer *rb = ring_buffer__new(
bpf_map__fd(skel->maps.events), handle_event, NULL, NULL);
while (ring_buffer__poll(rb, 100) >= 0) { /* ... */ }
Không cần clang trên node đích. Không cần kernel headers. Binary này chạy được từ RHEL 8 tới Ubuntu 24.04 tới Debian 12 - miễn là kernel có BTF và hàm do_sys_openat2 (tức kernel 5.6+).
12. Giới hạn và những điểm cần tỉnh táo
eBPF không phải viên đạn bạc
Verifier nghiêm ngặt làm giảm rủi ro nhưng cũng làm giảm độ biểu đạt. Không phải thuật toán nào cũng viết được trong eBPF. Nhiều chương trình "đơn giản trên giấy" bị verifier từ chối vì không chứng minh được cận trên của loop hoặc vì quá phức tạp. Ngoài ra eBPF không phải là môi trường chống malicious actor - quyền nạp eBPF về bản chất là quyền root: CAP_BPF, CAP_PERFMON và CAP_SYS_ADMIN trong nhiều trường hợp.
- Hạn chế instruction count. Mặc định 1M state-expansion. Chương trình lớn phải chia ra qua tail call hoặc BPF-to-BPF function call. Debug verifier là một kỹ năng riêng.
- Stack 512 byte. Tất cả biến local của một chương trình eBPF chia sẻ một stack 512 byte. Cấu trúc lớn phải cất vào per-CPU array map.
- Kernel ABI drift. CO-RE giải quyết phần lớn nhưng những thay đổi lớn của struct kernel vẫn có thể phá script. Luôn test trên ít nhất 2-3 phiên bản kernel LTS mục tiêu.
- Chi phí context switching nhỏ nhưng có thật. Mỗi hook là một chi phí cố định - từ 50 ns đến vài trăm ns tùy loại. Gắn quá nhiều kprobe lên hot path có thể làm chậm hệ thống đo được.
- Khó troubleshoot khi sai. Khi một chương trình bị reject, thông báo lỗi verifier thường dài hàng trăm dòng. Các công cụ như
bpftool prog profile,bpftool prog trace, và plugin VS Code của libbpf mới bắt đầu làm trải nghiệm này dễ chịu hơn.
13. Checklist triển khai eBPF production 2026
- Chuẩn hóa kernel baseline. Ép tất cả node Linux production ở tối thiểu kernel 6.1 LTS, đủ cho toàn bộ hook hiện đại (fentry/fexit, ringbuf, LSM BPF, kfuncs).
- Bật BTF kernel. Kiểm tra
/sys/kernel/btf/vmlinuxtồn tại trên mọi node. Đây là điều kiện tiên quyết của CO-RE. - Chọn CNI eBPF làm mặc định. Với Kubernetes, Cilium (hoặc Calico eBPF dataplane) trở thành baseline; kube-proxy iptables chỉ dùng cho legacy.
- Continuous profiling toàn cụm. Một agent Parca/Pyroscope trên mỗi node, kết hợp với BuildID registry để symbolize sau.
- Auto-instrumentation song song với SDK. Đừng bỏ SDK OpenTelemetry của service quan trọng. Dùng eBPF profiler cho coverage rộng, SDK cho context chiều sâu.
- Runtime security dựa trên policy-as-code. Tetragon/Falco với policy được version trong Git, audit theo CRD, và có chế độ dry-run trước khi bật enforce.
- Giám sát chính các agent eBPF. Expose các metric
bpf_prog_run_time_ns,map_element_count,dropped_eventsquabpf_stats_enabled. Agent eBPF cũng có thể trở thành nguồn overhead nếu chương trình lỗi thời. - Tách quyền CAP_BPF. Với kernel 5.8+, tách CAP_BPF khỏi CAP_SYS_ADMIN. Các agent chỉ cần CAP_BPF + CAP_PERFMON thay vì full root.
- Có kế hoạch rollback. Mỗi rollout chương trình eBPF mới phải có cơ chế detach nhanh. Bật/tắt theo feature flag ở tầng agent, không cần reboot.
- Đầu tư kỹ năng đội vận hành. Ít nhất hai kỹ sư trong đội SRE cần đọc được output của
bpftool,perf, và hiểu ra "verifier complained vì sao".
14. Kết luận - eBPF là tầng hạ tầng mới của Linux production
Năm 2014 eBPF trông giống như một tính năng nhỏ được thêm vào kernel để tăng tốc tcpdump. Mười hai năm sau, nó là một trong những thay đổi kiến trúc lớn nhất mà Linux đã đón nhận kể từ khi có namespaces và cgroups. Không cần khởi động lại, không cần module, không cần thay distro - eBPF cho phép đội kỹ sư mở rộng kernel theo nhu cầu của ứng dụng của họ, với một mức độ an toàn được xác lập ở compile-time.
Điều quan trọng với người xây hệ thống không phải là "biết viết một chương trình eBPF". Quan trọng hơn là hiểu rằng bên dưới các sản phẩm bạn đang chọn ở năm 2026 - CNI, APM, profiler, runtime security - có một tầng hạ tầng chung đang dịch chuyển về eBPF. Quyết định chọn Cilium thay vì kube-proxy, chọn Parca thay vì profiler theo ngôn ngữ, chọn Tetragon thay vì audit log cổ điển không còn là quyết định kỹ thuật đơn lẻ - chúng là các mảnh của cùng một kiến trúc nền tảng. Và kiến trúc đó có tên là eBPF.
Người kỹ sư hệ thống không cần viết eBPF hàng ngày, nhưng cần biết nó ở đâu, biết điểm mạnh và điểm yếu, biết chẩn đoán khi nó sai, và biết khi nào nó là lựa chọn đúng. Đó là kiến thức nền của thập kỷ này.
15. Nguồn tham khảo
- ebpf.io - What is eBPF? An Introduction and Deep Dive into the eBPF Technology
- kernel.org - BPF Documentation
- Cilium - CNI Benchmark: Understanding the Performance Characteristics
- Andrii Nakryiko - BPF CO-RE reference guide
- Brendan Gregg - Learn eBPF Tracing
- Parca - Continuous Profiling Overview
- Tetragon - Runtime Security Observability and Enforcement
- OpenTelemetry - Profiling signal and eBPF profiler
- Grafana Beyla - eBPF-based auto-instrumentation
- Meta Engineering - Open-sourcing Katran
PostgreSQL 18 Deep Dive 2026 - Asynchronous I/O, Skip Scan, Virtual Generated Columns, UUIDv7 và OAuth 2.0: Cuộc Cách Mạng Hiệu Năng của OLTP Database Open-Source
Apache Iceberg & Open Lakehouse 2026 - Kiến trúc Catalog, Metadata, Time Travel và Query Engines cho Data Platform Production
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.