uprobe, USDT và Soi Pod Từ Host

K
Kai··4 min read

Bài 6–7 gắn vào nhân — tracepoint, kprobe. Nhưng nhiều thứ đáng quan sát nằm trong userspace: một hàm thư viện, logic của ứng dụng. eBPF với được vào đó qua uprobeUSDT. Và vì container chỉ là tiến trình trên host, đúng kỹ thuật đó cho ta soi một pod từ phía host mà không cần cài gì vào pod — khép Part II bằng đúng điều khiến eBPF thành nền quan sát của Kubernetes.

uprobe: gắn vào hàm userspace

uprobe (user probe) gắn vào một hàm trong một chương trình hay thư viện userspace, y như kprobe gắn vào hàm nhân. Ví dụ thường gặp: trace getaddrinfo trong libc — hàm mà mọi chương trình dùng libc gọi để phân giải tên miền:

LIBC=/usr/lib/x86_64-linux-gnu/libc.so.6
sudo bpftrace -e "uprobe:$LIBC:getaddrinfo { printf(\"%s resolving %s\n\", comm, str(arg0)); }"
python3 resolving localhost
python3 resolving github.com

uprobe:<đường-dẫn-binary>:<tên-hàm> là probe; arg0 là tham số đầu của hàm — với getaddrinfo đó là con trỏ tên host, str() đọc ra. Bất kỳ tiến trình nào trên máy gọi getaddrinfo (qua libc) đều bị bắt — ở đây là python3 phân giải localhostgithub.com. Đây là quan sát userspace không cần sửa hay biên dịch lại chương trình: gắn vào binary có sẵn trên đĩa. (Có uretprobe cho lúc hàm trả về, như kretprobe.)

USDT: probe ứng dụng cài sẵn

uprobe gắn vào bất kỳ hàm nào, nhưng tên hàm có thể đổi giữa các phiên bản. USDT (User Statically-Defined Tracing) là các điểm probe ổn định mà tác giả ứng dụng cài thẳng vào code — như tracepoint nhưng ở userspace. Nhiều runtime lớn ship sẵn USDT (Node.js, Python, PostgreSQL, JVM, libc). Liệt kê USDT trong libc:

sudo bpftrace -l 'usdt:/usr/lib/x86_64-linux-gnu/libc.so.6:*'
usdt:/usr/lib/x86_64-linux-gnu/libc.so.6:libc:cond_broadcast
usdt:/usr/lib/x86_64-linux-gnu/libc.so.6:libc:cond_destroy
usdt:/usr/lib/x86_64-linux-gnu/libc.so.6:libc:cond_init
usdt:/usr/lib/x86_64-linux-gnu/libc.so.6:libc:cond_signal

Gắn USDT bằng usdt:<binary>:<nhà-cung-cấp>:<tên>. Khác uprobe ở chỗ USDT là hợp đồng tác giả cam kết giữ, nên ổn định hơn khi quan sát logic ứng dụng (vd query của database, GC của runtime).

Soi một pod từ host

Đây là điểm mấu chốt cho Kubernetes: một container chỉ là tiến trình trên host (cùng nhân, khác namespace). eBPF gắn vào nhân host nên thấy mọi tiến trình của mọi pod — lọc theo PID hoặc cgroup là soi được một pod cụ thể, không cần cài sidecar hay sửa pod. Tìm tiến trình của một pod (cilium-agent) rồi đếm syscall nó gọi:

cpid=$(pgrep -x cilium-agent | head -1)        # PID của tiến trình trong pod, nhìn từ host
sudo bpftrace -e "tracepoint:syscalls:sys_enter_* /pid == $cpid/ { @[probe] = count(); }"
@[...sys_enter_write]:      1897
@[...sys_enter_waitid]:      172
@[...sys_enter_socket]:       18
@[...sys_enter_tgkill]:       12
@[...sys_enter_setsockopt]:   10
@[...sys_enter_unlinkat]:      2

Lọc /pid == $cpid/ thu hẹp về đúng tiến trình của pod; @[probe] = count() đếm theo tên syscall (đọc được, không phải số). Ta thấy cilium-agent chủ yếu write (log/maps) và mở socket — quan sát từ ngoài, không đụng gì vào pod, không cài agent vào container. Mở rộng được: lọc theo cgroup (để bắt cả pod đa tiến trình), hay theo network namespace — chính là cách các công cụ như Pixie, Cilium Hubble, Parca quan sát workload Kubernetes mà không cần instrument từng app. Một điểm gắn ở nhân host thấy được mọi pod trên đó.

🧹 Dọn dẹp

bpftrace tự gỡ khi thoát; không có gì để dọn, node về 140 chương trình. Lệnh ở github.com/nghiadaulau/ebpf-from-scratch, thư mục 08-uprobe-usdt.

Tổng kết

eBPF với ra ngoài nhân: uprobe gắn vào hàm userspace của một binary/thư viện có sẵn (ta trace getaddrinfo trong libc, thấy python3 phân giải github.com — qua arg0 + str()), USDT gắn vào probe ổn định mà ứng dụng cài sẵn (libc, Node, Python, PostgreSQL...) qua usdt:<binary>:<vendor>:<tên>. Và vì container là tiến trình trên host chung nhân, eBPF ở nhân host soi được mọi pod: lọc /pid == .../ (hay theo cgroup) để xem syscall một pod gọi — ta thấy cilium-agent chủ yếu write/socket mà không cài gì vào pod. Đây là nền của quan sát Kubernetes hiện đại (Hubble, Pixie): một điểm gắn ở host, thấy toàn cụm.

Part II khép lại — ta đã quan sát hệ thống bằng bpftrace từ one-liner tới histogram tới soi pod, tất cả không viết C. Part III quay lại tự viết chương trình eBPF đầy đủ bằng libbpf + CO-RE (C) rồi nạp từ Go (cilium/ebpf) — khi cần một công cụ thật sự, không chỉ một dòng lệnh ad-hoc.

Related Posts