
Trong thập kỷ qua, khả năng quan sát cloud-native trong Kubernetes gần như chỉ dựa vào các trừu tượng không gian người dùng. Nếu bạn muốn theo dõi phân tán, mTLS (mutual TLS) và các số liệu mạng Lớp 7 trên các microservice của mình, cách làm tiêu chuẩn trong ngành là inject một proxy sidecar Envoy vào mỗi pod ứng dụng (như Istio và Linkerd đã phổ biến).
Mặc dù sidecar đã thành công trong việc trừu tượng hóa logic mạng khỏi các nhà phát triển ứng dụng, nhưng chúng đã tạo ra một khoản chi phí vận hành ẩn khổng lồ mà các nhóm kỹ thuật nền tảng gọi là "Thuế Sidecar".
Chạy một proxy sidecar có nghĩa là:
eBPF (Extended Berkeley Packet Filter) đã làm thay đổi hoàn toàn mô hình này. Bằng cách thực thi bytecode đã được xác minh, chạy trong sandbox trực tiếp bên trong nhân Linux, eBPF thu thập dữ liệu đo từ xa mạng sâu, các sự kiện socket và theo dõi phân tán tại ranh giới nhân với chi phí gần như bằng không - mà không cần inject sidecar hoặc sửa đổi một dòng mã nguồn ứng dụng nào.
Bài viết mổ xẻ cơ chế giám sát mức hạt nhân của eBPF trong Kubernetes.
Để hiểu tại sao eBPF đại diện cho một bước đột phá về kiến trúc, hãy xem xét vòng đời gói tin bên trong một service mesh tiêu chuẩn:
[Traditional Service Mesh: 4 Context Switches per Hop]
Pod A (App) ──► Loopback ──► Envoy Proxy (Pod A) ──► Host Network
│
Wire Transit
│
Pod B (App) ◄── Loopback ◄── Envoy Proxy (Pod B) ◄── Host Network
Khi Pod A gửi một yêu cầu HTTP đến Pod B:
send() syscall trong không gian người dùng.Việc chuyển đổi ngữ cảnh từ không gian người dùng sang không gian kernel này gây ra tình trạng thrashing bộ đệm CPU và độ trễ bộ nhớ đáng kể. Trong các cluster lớn chạy 1.000 pod, các proxy sidecar thường tiêu thụ 20% đến 35% tổng bộ nhớ cluster chỉ để chờ I/O!
Bởi vì tất cả các gói mạng, quá trình thực thi và cấp phát bộ nhớ đều phải đi qua nhân hệ điều hành, nên nhân là nơi đáng tin cậy nhất để quan sát hành vi hệ thống.
Với eBPF, một daemonset duy nhất chạy trên mỗi worker node Kubernetes sẽ gắn các hook sự kiện nhẹ vào các hàm kernel:
[eBPF Observability: Direct Kernel Interception]
Pod A (Application) ─────────────────────────────► Pod B (Application)
│ │
───────┼────────────────────────────────────────────────────┼───────
▼ ▼
[Kernel Socket Layer] ◄─── (eBPF Tracepoint Hook) ───► [Kernel Socket Layer]
│
eBPF Ring Buffer
│
▼
eBPF Collector (Node DaemonSet)
sys_enter_connect) và các chuyển đổi trạng thái TCP. Nó tự động kiểm tra các tiêu đề HTTP, loại phương thức và mã trạng thái gRPC một cách minh bạch mà không yêu cầu nhà phát triển cài đặt OpenTelemetry SDK trong Node, Go hoặc Python.sockops, eBPF có thể bỏ qua hoàn toàn ngăn xếp TCP/IP cho các pod giao tiếp trên cùng một node, truyền bộ nhớ trực tiếp từ bộ đệm socket này sang bộ đệm socket khác (giảm độ trễ IPC cục bộ tới 80%).Việc chạy mã tùy chỉnh bên trong nhân Linux trước đây yêu cầu biên dịch một Kernel Module tùy chỉnh (.ko), trong đó một lỗi tham chiếu con trỏ null hoặc vòng lặp vô hạn duy nhất sẽ ngay lập tức gây ra kernel panic và làm sập toàn bộ máy chủ vật lý.
eBPF ngăn chặn điều này thông qua Trình xác minh Kernel:
Phát triển eBPF hiện đại sử dụng BPF CO-RE (Compile Once – Run Everywhere) với BTF (BPF Type Format), cho phép một binary eBPF đã biên dịch chạy trên các phiên bản kernel Linux khác nhau mà không cần biên dịch lại.
Chương trình eBPF này gắn vào tracepoint sys_enter_connect để bắt khi một kết nối TCP đi ra được khởi tạo:
// probe.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
struct event {
__u32 pid;
char comm[16];
};
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024); // 256 KB ring buffer
} events SEC(".maps");
SEC("tracepoint/syscalls/sys_enter_connect")
int trace_connect(struct trace_event_raw_sys_enter *ctx) {
struct event *e;
// Reserve slot in shared ring buffer
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_ringbuf_submit(e, 0);
return 0;
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
Daemon Go trong không gian người dùng đọc các sự kiện từ ring buffer không khóa thông lượng cao và đẩy các số liệu đến bộ thu Prometheus của bạn:
// main.go
package main
import (
"bytes"
"encoding/binary"
"log"
"os"
"os/signal"
"syscall"
"github.com/cilium/ebpf/link"
"github.com/cilium/ebpf/ringbuf"
)
type Event struct {
PID uint32
Comm [16]byte
}
func main() {
// Load compiled eBPF ELF into the Linux kernel
objs := probeObjects{}
if err := loadProbeObjects(&objs, nil); err != nil {
log.Fatalf("Failed loading eBPF objects: %v", err)
}
defer objs.Close()
// Attach to sys_enter_connect tracepoint
tp, err := link.Tracepoint("syscalls", "sys_enter_connect", objs.TraceConnect, nil)
if err != nil {
log.Fatalf("Failed attaching tracepoint: %v", err)
}
defer tp.Close()
// Open lockless ring buffer reader
rd, err := ringbuf.NewReader(objs.Events)
if err != nil {
log.Fatalf("Failed creating ringbuf reader: %v", err)
}
defer rd.Close()
log.Println("eBPF network probe active. Capturing outbound TCP connections...")
for {
record, err := rd.Read()
if err != nil {
break
}
var event Event
binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event)
commStr := string(bytes.Trim(event.Comm[:], "\x00"))
log.Printf("[Kernel Event] Process %s (PID %d) initiated TCP connection\n", commStr, event.PID)
}
}
Bảng dưới đây phác thảo độ trễ và mức sử dụng tài nguyên được đo trên một điểm chuẩn HTTP 5.000 yêu cầu/giây giống hệt nhau chạy trong một cluster Kubernetes 100 node:
| Số liệu | Sidecar Istio Envoy | Cilium eBPF (Không Sidecar) | Tác động ròng |
|---|---|---|---|
| Độ trễ mạng p99 | 4.8 ms | 1.1 ms | Độ trễ p99 thấp hơn 77% |
| Dấu chân bộ nhớ Cluster | 48 GB RAM (50MB/pod) | 1.8 GB RAM (1 daemon/node) | Tiết kiệm 96% bộ nhớ |
| Mức sử dụng CPU Cluster | 18.2 Cores | 2.1 Cores | Giảm 88% CPU |
| Chi phí khởi động lại Pod ứng dụng | Cao (Inject webhook) | Bằng không (Kernel trong suốt) | Khởi động pod tức thì |
Daemonset eBPF tải các chương trình vào kernel yêu cầu khả năng CAP_BPF hoặc CAP_SYS_ADMIN. Tuy nhiên, các workload và pod ứng dụng chạy với các tài khoản người dùng hoàn toàn không có đặc quyền và hệ thống tệp gốc chỉ đọc mà không yêu cầu bất kỳ đặc quyền đặc biệt nào.
Có. eBPF có thể hook vào các thư viện mã hóa trong không gian người dùng (như OpenSSL hoặc crypto/tls của Go) bằng cách sử dụng uprobes để thu thập dữ liệu văn bản thuần không được mã hóa ngay trước khi nó được chuyển đến bộ mã hóa. Điều này cho phép theo dõi tiêu đề và payload HTTP L7 đầy đủ mà không yêu cầu chứng chỉ proxy MITM.
Các công cụ quan sát eBPF sản xuất chính bao gồm:
Một đội ngũ platform engineering đang đánh giá việc di chuyển stack observability của Kubernetes từ service mesh dựa trên Istio sang giải pháp dựa trên eBPF. Lợi ích vận hành đáng kể NHẤT mà họ có thể đạt được trong một cluster sản xuất quy mô lớn với hàng nghìn microservice là gì?
Làm sạch log AI CLI, tạo biểu thức cron, giải mã JWT và tính quyền chmod offline.

Hướng dẫn toàn diện về eBPF & Cilium trong Kubernetes: định tuyến thông lượng cao, chính sách mạng & khả năng quan sát Hubble với kiến trúc cấp độ sản xuất và các ví dụ mã.
Đọc tiếp
Hướng dẫn toàn diện về OpenTelemetry Collector & eBPF trong môi trường production: tracing, metrics & prometheus pipelines không cần code với kiến trúc cấp độ production và các ví dụ code.
Đọc tiếp
Hướng dẫn toàn diện so sánh Cilium và Calico với eBPF: thông lượng mạng Kubernetes, bảo mật & service mesh với kiến trúc cấp độ sản xuất và ví dụ mã.
Đọc tiếp