Khi top và htop hoàn toàn bất lực trước sự cố
Cảnh tượng này chắc không ít anh em vận hành từng nếm trải: Slack báo động đỏ, p99 latency của API vọt lên 2.500ms, CPU iowait chạm mức 70%. Bạn vội SSH vào node, gõ ngay top, htop, rồi iotop. Nhưng quái lạ: thông số trông vẫn rất bình thường. Tiến trình Postgres chỉ ăn 12% CPU, đĩa đọc ghi tà tà 25 MB/s. Bạn hoàn toàn mất dấu: PID nào, query nào, hay inode cụ thể nào đang ghì hệ thống xuống?
Phản xạ tự nhiên lúc bí bách là lôi strace ra soi system call. Cách đây vài tháng, một bạn junior team mình từng gõ lệnh này thẳng trên con DB production đang gánh 15.000 QPS:
# ĐỪNG BAO GIỜ gõ lệnh này trên production bận rộn!
sudo strace -p $(pgrep -f postgres) -T -tt
Kết quả? Database drop connection hàng loạt sau đúng 3 giây. Cơ chế ptrace buộc kernel dừng thread ở mỗi lượt syscall entry và exit. Khi app xử lý hàng chục nghìn I/O mỗi giây, overhead từ strace kéo tụt throughput tới 40–80 lần. Bạn vừa tự tay biến một ca chập chờn thành sự cố sập nguồn toàn hệ thống.
Khoảng mù chết người giữa User Space và Kernel Space
Các công cụ đo lường kinh điển thất bại vì những rào cản kiến trúc cố hữu:
- Độ phân giải quá thô:
top,vmstathaysarchỉ đọc số liệu tổng hợp định kỳ từ/procvà/sys. Nếu xảy ra một đợt micro-burst—I/O nghẽn cục bộ trong 15ms rồi dứt—chu kỳ lấy mẫu 1 giây sẽ san phẳng con số này. Bạn nhìn đồ thị thấy êm ru, nhưng khách hàng đã nếm trọn timeout. - Mất dấu ngữ cảnh:
iostatbáo ổ NVMe/dev/nvme0n1bận 98%, nhưng nó không thể chỉ điểm thread nào gửi request, file descriptor nào đang kẹt, hay block dữ liệu nằm ở offset nào. - Can thiệp thô bạo: Kỹ thuật trace truyền thống tạo ra quá nhiều context switch, đẩy latency lên cao và không thể chạy an toàn trên tải thực tế.
Đây chính là bài toán mà eBPF (Extended Berkeley Packet Filter) giải quyết triệt để. Fedora là môi trường thử nghiệm lý tưởng vì luôn ship bản kernel Linux mới nhất. Thay vì copy dữ liệu liên tục giữa kernel và user space, eBPF nạp các đoạn bytecode đã được kernel verifier chứng thực an toàn vào thẳng nhân hệ điều hành. Nhờ gắn trực tiếp vào kprobe, tracepoint hoặc uprobe, overhead đo đạc thường được ghì ở mức dưới 1.5%.
3 hướng tiếp cận eBPF trên Fedora: Từ mì ăn liền đến đo ni đóng giày
Tùy theo áp lực thời gian và mức độ phức tạp của ca trực, bạn có ba lựa chọn:
Cách 1: Khai thác kho công cụ BCC (BPF Compiler Collection) có sẵn
BCC tổng hợp hàng chục script Python/C chuẩn chỉ do Brendan Gregg và cộng đồng tối ưu. Bạn không cần viết code vẫn xử lý được 80% sự cố phổ biến.
Cài đặt gói công cụ trên Fedora:
sudo dnf install -y bcc-tools kernel-devel-$(uname -r) kernel-headers-$(uname -r)
Mẹo thực tế: Toàn bộ file thực thi nằm tại /usr/share/bcc/tools/ chứ không nằm ở /usr/bin để tránh xung đột tên lệnh. Bạn nên thêm thư mục này vào PATH hoặc gọi trực tiếp bằng đường dẫn tuyệt đối.
Bắt tận tay ổ đĩa nghẽn bằng biolatency-bpfcc:
# Đo độ trễ block I/O theo millisecond và gom thành biểu đồ phân phối
sudo /usr/share/bcc/tools/biolatency-bpfcc -m 5
Thay vì nhìn con số trung bình vô thưởng vô phạt, bạn sẽ thấy ngay phân bố latency: có bao nhiêu I/O xử lý dưới 1ms, và có bao nhiêu request bị kẹt ở ngưỡng nguy hiểm trên 512ms do hàng đợi đĩa quá tải.
Tóm sống tiến trình ngắn ngày với execsnoop-bpfcc:
# Bắt tức thì mọi tiến trình mới được fork/exec trong hệ thống
sudo /usr/share/bcc/tools/execsnoop-bpfcc -T
Tuyệt chiêu này cực kỳ đắc lực khi CPU thỉnh thoảng spike 100% rồi biến mất. Lúc đó top chưa kịp refresh chu kỳ 1 giây thì script bash con của cron job đã chạy xong và thoát.
Cách 2: Tự code eBPF native với C và libbpf (CO-RE)
Phương án này dùng C thuần, biên dịch bằng Clang ra file ELF bytecode gọn nhẹ rồi nạp qua libbpf. Đây là tiêu chuẩn vàng cho các daemon giám sát dài hạn như Datadog hay Cilium vì tối ưu bộ nhớ tuyệt đối và không cần cài compiler nặng nề trên node production.
Đổi lại, cách này đòi hỏi thời gian chuẩn bị. Bạn phải tự tay quản lý BPF ring buffer, BPF map và viết code user space để đọc dữ liệu. Khi hệ thống đang gặp sự cố, bạn không thể ngồi hàng giờ viết và debug code C chỉ để kiểm tra một hàm kernel.
Cách 3: Chẩn đoán thần tốc bằng oneliner với bpftrace
Nếu BCC là bộ cờ-lê đúc sẵn, còn libbpf là xưởng cơ khí, thì bpftrace chính là con dao bấm đa năng. Cú pháp lai giữa awk và C giúp bạn viết các lệnh một dòng cực mạnh ngay trên terminal.
Cài đặt gói trên Fedora:
sudo dnf install -y bpftrace
Bắt quả tang tiến trình nào đang mở file trong hệ thống:
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("[%s] (PID %d) mở: %s\n", comm, pid, str(args.filename)); }'
Xếp hạng tiến trình đọc đĩa nhiều nhất sau mỗi 5 giây:
sudo bpftrace -e 'kprobe:vfs_read { @[comm] = count(); } interval:s:5 { print(@); clear(@); }'
Chỉ một dòng lệnh ngắn, bạn đếm trúng đích số lần gọi hàm vfs_read, nhóm theo tên tiến trình và in bảng tổng kết. Nhanh, chuẩn và không tốn tài nguyên.
Quy trình cứu hỏa thực chiến: Phối hợp nhịp nhàng giữa BCC và bpftrace
Qua nhiều ca trực đêm xử lý sự cố, mình đúc kết quy trình 2 nhịp chuẩn xác nhất:
-
Nhịp 1 – Dùng BCC khoanh vùng vĩ mô:
Đừng vội viết script khi chưa rõ điểm nghẽn. Hãy chạy ngay các công cụ kiểm tra nhanh:
- Nghi ngờ I/O: Chạy
biolatency-bpfccxem độ trễ vàbiosnoop-bpfccđể tìm PID gây thắt nút cổ chai. - Nghi ngờ Network: Dùng
tcpconnect-bpfcchoặctcptracer-bpfccđể đo độ trễ bắt tay TCP (handshake latency). - Nghi ngờ Memory: Chạy
memleak-bpfcc -p <PID>để tóm các khối nhớ cấp phát bằngmallocmà không gọifree.
- Nghi ngờ I/O: Chạy
-
Nhịp 2 – Dùng bpftrace giải phẫu vi mô:
Khi BCC đã chỉ điểm được PID hoặc subsystem nghi vấn, hãy dùng
bpftraceđể soi kính hiển vi vào đúng điểm đó. Ví dụ, nếu nghi ngờ worker Nginx bị khựng khi đọc disk cache, hãy đo riêng phân phối độ trễ của syscallreadtrên worker đó:
# Đo latency của syscall read riêng cho tiến trình nginx (đơn vị: microsecond)
sudo bpftrace -e '
tracepoint:syscalls:sys_enter_read /comm == "nginx"/ {
@start[tid] = nsecs;
}
tracepoint:syscalls:sys_exit_read /@start[tid]/ {
@duration_us = hist((nsecs - @start[tid]) / 1000);
delete(@start[tid]);
}
'
3 bài học xương máu khi chạy eBPF trên Fedora
- Khớp phiên bản Kernel Headers: Fedora đẩy bản cập nhật kernel rất thường xuyên. Nếu bạn vừa chạy
dnf upgradenhưng chưa reboot máy, góikernel-develmới tải về sẽ mang số hiệu khác với kernel đang nạp trong RAM (uname -r). BCC và bpftrace sẽ văng lỗi biên dịch ngay lập tức. Hãy luôn đối chiếurpm -q kernel-develvớiuname -rtrước khi debug. - Rào cản SELinux: Fedora bật SELinux ở chế độ Enforcing mặc định. Kể cả khi chạy quyền root hoặc cấp
CAP_BPF, SELinux policy vẫn có thể chặn trace một số daemon nhạy cảm. Khi lệnh bị từ chối truy cập (permission denied) vô cớ, hãy kiểm tra ngay bằngjournalctl -t setroubleshoothoặc soi file/var/log/audit/audit.log. - Thu hồi biến trong bpftrace: Khi viết script tracking, các map dạng
@start[tid]chiếm trực tiếp bộ nhớ non-swappable trong kernel RAM. Nếu trace luồng xử lý dày đặc mà quên gọidelete(@start[tid])sau khi tính xong độ trễ, kernel map sẽ phình to không kiểm soát và tiềm ẩn nguy cơ tràn bộ nhớ.

