基于 eBPF 的云原生网络可观测性实战:从零搭建到生产级排障
结论先行: eBPF 让云原生网络可观测性从“黑盒猜测”变成了“内核级透视”,但落地时有三个坑你必须提前知道:内核版本、权限模型、以及数据风暴。

为什么放弃传统方案?
过去排查 K8s 网络问题,我的流程是:kubectl exec 进 Pod → tcpdump → 抓包 → 本地分析。痛点很明显:
- 每个节点都要装 Agent,资源开销大
- 无法追踪跨节点、跨 Service Mesh 的请求链路
- 容器重启后,旧数据丢失
eBPF 方案直接在内核层挂载钩子,对应用零侵入,且能拿到完整上下文。我选择了 Cilium 作为载体(它内置了 Hubble 可观测性组件),因为社区最活跃,文档最全。
第一步:环境准备(踩坑实录)
我的环境: 3 节点 K8s 集群(1 Master + 2 Worker),Ubuntu 22.04,内核 5.15。
注意: eBPF 需要内核 >= 4.19,且开启
CONFIG_DEBUG_INFO_BTF。5.10+ 内核最省心。
踩坑 1:内核版本检测
uname -r
# 5.15.0-86-generic
如果你的内核低于 4.19,必须升级。我试过在 4.15 内核上强装 Cilium,结果 eBPF 程序加载失败,报 invalid argument。
踩坑 2:权限问题
eBPF 需要 CAP_BPF 或 root 权限。在 Docker 容器中运行 Cilium Agent 时,必须挂载 /sys/fs/bpf 和 /sys/kernel/debug:
# cilium-agent DaemonSet 的关键配置
securityContext:
capabilities:
add:
- SYS_ADMIN
- NET_ADMIN
- SYS_RESOURCE
volumeMounts:
- name: bpf-maps
mountPath: /sys/fs/bpf
mountPropagation: Bidirectional

如果你用 Helm 安装,默认已经处理了,但自建集群时容易漏。
第二步:安装 Cilium + Hubble
我直接用 Helm 安装,指定 eBPF 模式:
helm repo add cilium https://helm.cilium.io/
helm install cilium cilium/cilium \
--namespace kube-system \
--set bpf.masquerade=true \
--set hubble.enabled=true \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true
安装完检查状态:
kubectl -n kube-system get pods -l k8s-app=cilium
# 全部 Running 后,验证 eBPF 是否生效
cilium status | grep "BPF"
# BPF: OK
关键验证命令: cilium status 会显示 BPF 和 Kubernetes 状态,如果出现 Disabled,大概率是内核或权限问题。
第三步:开启网络流量追踪
Hubble 默认只采集部分指标,要看到完整流量,需要开启 monitor-aggregation:
kubectl -n kube-system edit configmap cilium-config
# 添加或修改:
monitor-aggregation: none # 关闭聚合,捕获所有包
注意: 生产环境建议用
medium或low,否则流量大的集群会写爆 BPF Map。
然后启动 Hubble UI:
cilium hubble enable
cilium hubble port-forward
# 访问 http://localhost:12000
第四步:实战排障——Pod 间通信延迟
场景
一个微服务 order-svc 调用 payment-svc,偶尔出现 3-5 秒延迟。传统 kubectl logs 和 tcpdump 都抓不到规律。
用 eBPF 抓现场
- 找到 Pod 的 IP
kubectl get pod -l app=order-svc -o wide
# IP: 10.1.0.15
-
在 Hubble UI 中过滤
输入source=10.1.0.15,看到目标 IP10.1.0.22(payment-svc)。 -
用 CLI 追踪具体连接
hubble observe --from-pod default/order-svc-xxx --to-pod default/payment-svc-yyy --verdict DROPPED
输出显示大量 TCP RST 包,且重传率达到 15%。
- 深入内核级指标
cilium bpf metrics list | grep "drop"
# drop_count: 142
# reason: CT_MAP_INSERT_FAILED
真相浮出水面: Cilium 的连接跟踪表(CT Map)满了,导致新连接被丢弃,触发 TCP 重传和延迟。
解决方案
增大 CT Map 大小:
helm upgrade cilium cilium/cilium \
--namespace kube-system \
--set bpf.ctMapEntriesMax=524288 # 默认 65536
调整后延迟从 3s 降到 50ms。
第五步:高级技巧——自定义 eBPF 指标
Cilium 内置了丰富的 Prometheus 指标,但有时你需要自定义。我写了一个简单的 eBPF 程序来统计特定 Service 的 RTT:
// rtt_tracker.c (简化版)
#include <bpf/bpf_helpers.h>
struct rtt_key {
__u32 src_ip;
__u32 dst_ip;
};
struct rtt_value {
__u64 total_rtt_ns;
__u64 count;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, struct rtt_key);
__type(value, struct rtt_value);
} rtt_map SEC(".maps");
SEC("kprobe/tcp_rcv_established")
int trace_rtt(struct pt_regs *ctx) {
// 从 skb 中提取 src/dst IP
// 计算 RTT 并更新 map
return 0;
}
然后通过 Cilium 的 bpf-lb 模式加载,暴露为 Prometheus 指标。
不要害怕写 eBPF 代码——对于 1-3 年经验的开发者,从复制社区代码开始,改几行即可。
踩坑总结(诚实记录)
| 坑 | 原因 | 解决 |
|---|---|---|
| eBPF 程序加载失败 | 内核版本低 | 升级到 5.10+ |
| Hubble 看不到流量 | monitor-aggregation 默认 high | 改为 none 或 medium |
| 节点重启后 BPF Map 丢失 | 未挂载持久化卷 | 在 DaemonSet 中配置 hostPath |
| 流量大时 CPU 飙升 | eBPF 钩子过多 | 减少 tracepoint,改用 kprobe |
延伸思考:下一步怎么玩?
- 集成到 Grafana:Cilium 暴露了
cilium_bpf_map_pressure、cilium_drop_count等指标,配合 Loki 日志可以构建全链路可观测性。 - 服务网格场景:eBPF 可以替代 Envoy 做 Sidecar 流量拦截,性能提升 40%+(参考 Cilium Service Mesh)。
- 安全审计:用 eBPF 监控
connect()系统调用,实时检测异常外连——这是云原生安全的起点。
最后说一句:eBPF 不是银弹,它解决的是“内核级数据获取”问题,但数据分析、告警、可视化依然需要传统工具配合。先跑通 Hubble,再上自定义程序,最后才考虑写 eBPF 代码——这个顺序能让你少踩 80% 的坑。

评论已关闭!