Hubble Bên Trong: Từ Sự Kiện eBPF Tới Luồng Mạng Toàn Cụm

K
Kai··5 min read

Bài 17-18 quan sát hiệu năng một node. Bài này khép Part VI bằng quan sát mạng toàn cụm: Hubble — thứ cho ta thấy mọi kết nối giữa các pod, theo tên service, kèm verdict policy, mà không cài gì vào pod. Nó là tầng trên cùng của chuỗi "eBPF làm nền observability". Ta mổ cách nó hoạt động, từ datapath lên tới luồng có nhãn.

Ba tầng: datapath → perf ring → giàu hóa

Hubble không phải một agent riêng cắm vào pod. Nó là tầng trên cùng của một chuỗi ba bước, mà hai bước dưới ta đã gặp:

   [1] datapath eBPF (74 sched_cls, Bài 12) xử lý mỗi gói
            │  bpf_perf_event_output()  -- đẩy sự kiện trace/drop/policy
            ▼
   [2] cilium_events  (BPF map perf_event_array, một khe mỗi CPU)
            │  cilium-agent mở perf reader, đọc sự kiện thô
            ▼  (cilium monitor cho thấy tầng này: identity dạng SỐ)
   [3] Hubble: giàu hóa identity -> tên pod/service + verdict
            ▼
   luồng đọc được: "host -> kube-system/coredns:8080 FORWARDED"

Tầng 2: perf ring cilium_events

Map mà datapath đẩy sự kiện vào chính là một perf_event_array đã gặp ở Bài 12:

sudo bpftool map show | grep cilium_events
23: perf_event_array  name cilium_events  max_entries 2     <- một khe mỗi CPU (node 2 CPU)

Các chương trình sched_cls của Cilium (cil_from_container, cil_to_netdev...) gọi helper bpf_perf_event_output() để ghi một bản ghi sự kiện vào cilium_events mỗi khi có việc đáng báo (gói được forward, bị drop, một verdict policy). perf_event_arraymột khe cho mỗi CPU (max_entries 2 = 2 lõi) — mỗi CPU đẩy vào khe riêng, tránh tranh chấp trên đường nóng. Đây là họ hàng của ring buffer (Bài 9): cơ chế đẩy sự kiện từ nhân lên userspace, hiệu quả.

Tầng 2 đọc thô: cilium monitor

cilium monitor mở đúng perf reader trên cilium_events và in sự kiện thô — đúng thứ datapath đẩy lên, chưa giàu hóa:

sudo cilium monitor
-> endpoint 645  identity 16777217->18203 state reply  ifindex lxc9d33...  10.0.1.11:6443 -> 10.200.0.140:59902 tcp ACK
-> endpoint 1797 identity host->35393    state new    ifindex lxca0f2...  10.200.0.64:36830 -> 10.200.0.180:9808 tcp SYN
-> stack         identity 35393->host    state reply  ifindex 0           10.200.0.180:9808 -> 10.200.0.64:36830 tcp SYN,ACK

Đọc ra cấu trúc một sự kiện datapath: hướng (-> endpoint gửi vào một pod, -> stack lên host stack, -> network ra card mạng), identity nguồn→đích (số!), state (new/established/reply — từ conntrack Bài 12), ifindex (veth pod nào), và gói thật (IP:port, cờ TCP). Chú ý: danh tính ở đây là số18203, 35393, 16777217. Đó là security identity của Cilium (Bài 12), chưa phải tên người đọc được. Tầng này là sự kiện eBPF trần.

Tầng 3: Hubble giàu hóa thành tên

hubble observe đọc cùng dòng sự kiện đó nhưng giàu hóa identity số thành tên pod/service:

hubble observe --last 12 -o compact
10.200.0.64:54764 (host) -> kube-system/coredns-87bb947d6-v29lc:8080 (ID:18203) to-endpoint FORWARDED (TCP Flags: SYN)
10.200.0.64:54764 (host) <- kube-system/coredns-87bb947d6-v29lc:8080 (ID:18203) to-stack    FORWARDED (TCP Flags: SYN,ACK)
10.200.0.64:55954 (host) -> kube-system/ebs-csi-node-qq4nk:9808     (ID:35393) to-endpoint FORWARDED (TCP Flags: ACK,FIN)
10.0.1.20:47106   (host) -> 10.0.1.10:6443                          (world)    to-network  FORWARDED (TCP Flags: ACK,PSH)

Cùng các flow của cilium monitor, nhưng giờ identity 18203 hiện thành kube-system/coredns-87bb947d6-v29lc, 35393 thành ebs-csi-node-qq4nk, 16777217 thành world (ngoài cụm). Verdict FORWARDED (hoặc DROPPED nếu policy chặn) rõ ràng. Nguồn của phép giàu hóa là kho identity→nhãn (Bài 12):

cilium identity get 18203
18203   k8s:k8s-app=kube-dns
        k8s:io.kubernetes.pod.namespace=kube-system
        k8s:io.cilium.k8s.policy.serviceaccount=coredns

Hubble tra số 18203 ra bộ nhãn k8s này, ghép với endpoint/pod tương ứng, thành tên đọc được. Toàn bộ thông tin gốc — ai nói với ai, verdict gì — đã có sẵn trong sự kiện eBPF; Hubble chỉ dịch số sang tên.

Vì sao mô hình này mạnh

Không pod nào bị cài sidecar, không ứng dụng nào bị sửa. Datapath eBPF vốn đã xử lý mọi gói của mọi pod (Bài 12) — thêm một bpf_perf_event_output là có ngay luồng sự kiện đầy đủ, gắn nhãn theo identity mà Cilium vốn dùng để áp policy. Đó là lý do quan sát mạng kiểu Hubble gần như miễn phí về mặt kiến trúc: nó tái dùng đúng datapath đang định tuyến và áp policy. So với mô hình cũ (mỗi node một agent bắt gói bằng libpcap, hay sidecar trong mỗi pod), eBPF cho cùng tầm nhìn từ một điểm gắn ở nhân, ít chi phí hơn nhiều.

🧹 Dọn dẹp

Bài này chỉ đọc sự kiện đang chảy (cilium monitor, hubble observe, cilium identity get) — không gắn hay sửa gì. Node vẫn 140 chương trình. Lệnh ở github.com/nghiadaulau/ebpf-from-scratch, thư mục 19-hubble.

Tổng kết

Hubble dựng bức tranh luồng mạng toàn cụm từ một chuỗi ba tầng, tái dùng datapath eBPF có sẵn. [1] Các chương trình sched_cls của Cilium (Bài 12) gọi bpf_perf_event_output() đẩy sự kiện trace/drop/policy vào [2] cilium_events, một perf_event_array một-khe-mỗi-CPU (họ hàng ring buffer Bài 9). [3] cilium-agent đọc perf ring đó — cilium monitor cho thấy sự kiện thô: hướng (endpoint/stack/network), identity nguồn→đích dạng số, state (từ conntrack), ifindex, gói thật. Hubble giàu hóa: tra security identity (18203) ra nhãn k8s (k8s-app=kube-dns) từ kho identity→nhãn rồi thành tên pod (kube-system/coredns-...), kèm verdict FORWARDED/DROPPED. Toàn bộ dữ liệu đã có trong sự kiện eBPF; Hubble chỉ dịch số sang tên. Vì datapath vốn đã thấy mọi gói, quan sát kiểu này gần như miễn phí — không sidecar, không sửa app, một điểm gắn ở nhân thấy toàn cụm.

Part VI khép lại — ta đã quan sát hiệu năng từ on-CPU profiling, off-CPU latency, tới luồng mạng toàn cụm. Part VII là phần cuối: một case-study Cilium end-to-end — ghép mọi mảnh của series (verifier, maps, XDP, tc, tail call, perf ring, identity) thành bức tranh hoàn chỉnh một gói pod đi từ container này sang container kia, rồi tổng kết hành trình eBPF từ số không.

Related Posts

Kiểu Tetragon: Từ Quan Sát Đến Cưỡng Chế...
SecurityKubernetes

Kiểu Tetragon: Từ Quan Sát Đến Cưỡng Chế Bằng bpf_send_signal

Tetragon là công cụ an ninh runtime của hệ Cilium: nó quan sát bằng kprobe/tracepoint (đúng những hook Part II dùng) rồi cưỡng chế ngay trong nhân. Cơ chế cưỡng chế thật của nó là hai helper — bpf_send_signal gửi SIGKILL giết tiến trình, và bpf_override_return ghi đè giá trị trả về syscall. Bài này tự dựng đúng cơ chế đó: một tracepoint exec gọi bpf_send_signal(SIGKILL) để giết một tiến trình ngay khi nó chạy — binary cấm nhận exit 137, binary thường vẫn chạy. Không cần LSM hay reboot.

K
KaiMay 24, 2026
LSM BPF: Cưỡng Chế An Ninh Ngay Trong Nh...
SecurityLinux

LSM BPF: Cưỡng Chế An Ninh Ngay Trong Nhân

Tới giờ eBPF của ta chỉ quan sát. LSM BPF thì cưỡng chế: gắn vào các điểm kiểm soát an ninh của nhân (Linux Security Modules) mà SELinux, AppArmor dùng, và một chương trình trả về 0 cho qua hay -EPERM để chặn. Bài này viết một chương trình LSM chặn mở file, và gặp một bài học thật: lần đầu nó nạp và gắn được nhưng không chặn gì — vì bpf chưa nằm trong danh sách LSM hoạt động. Bật bpf bằng tham số boot rồi reboot, cùng chương trình đó chặn thật — cat và python đều nhận Operation not permitted.

K
KaiMay 24, 2026