4. 流量采集与负载均衡
当监测链路从 1 Gbps 走向 10 Gbps、40 Gbps 时,Zeek 面临的第一道瓶颈往往不是协议解析逻辑,而是数据包如何从网卡高效进入用户态。默认情况下,Linux 内核协议栈会为每个数据包分配 sk_buff、走中断与软中断、做路由与过滤判断,再通过 socket 队列交给 Zeek。这条路径通用但开销大,单核往往在 1~2 Gbps 左右就出现丢包。要扩展吞吐,需要同时解决两件事:单机采集路径的零拷贝化,以及多进程/多主机的负载均衡。
三种采集方式的定位差异
AF_PACKET 是 Linux 原生套接字接口,Zeek 通过 AF_PACKET 直接读取网卡数据,配合 PACKET_MMAP 环形缓冲区可显著减少内存拷贝和系统调用次数。它的优势是零依赖、部署简单、与内核版本兼容性好,适合 1~10 Gbps 的常规监测点。局限在于数据仍要经过内核网络栈的一部分处理,单流性能受限于单核,且多队列需要配合 RSS 与 fanout 才能分摊。
PF_RING 是带内核模块的加速方案,通过 pf_ring 模块提供更深的环形缓冲和 DNA(Direct NIC Access)模式,可将网卡 DMA 区域直接映射到用户态,绕过部分内核开销。相比 AF_PACKET,它在 10 Gbps 场景下 CPU 占用更低、突发吸收能力更强,但需要编译安装内核模块,与内核升级存在耦合,运维成本更高。
DPDK 走的是完全旁路内核的路线:通过 UIO/VFIO 把网卡交给用户态轮询驱动,配合大页内存和 CPU 绑核,实现真正的零拷贝与无中断轮询。它能在单机达到 40 Gbps 以上,但代价是独占网卡与 CPU 核,无法与内核共享同一张网卡,且需要专门的绑定脚本与驱动适配。DPDK 通常不直接对接 Zeek 原生采集,而是由 Suricata、自定义采集器或专用分流器使用,再把流量以某种形式交给 Zeek。
| 维度 | AF_PACKET | PF_RING | DPDK |
|---|---|---|---|
| 内核依赖 | 原生,无额外模块 | 需 pf_ring 模块 | 需 UIO/VFIO,旁路内核 |
| 拷贝/中断 | mmap 减少拷贝,仍有中断 | mmap + 可选 DNA | 零拷贝、纯轮询 |
| 典型单机吞吐 | 1~10 Gbps | 10~20 Gbps | 40 Gbps+ |
| CPU 占用 | 中 | 较低 | 低但需独占核 |
| 部署复杂度 | 低 | 中 | 高 |
| 适用场景 | 常规监测点 | 中高吞吐、可维护内核 | 骨干/数据中心高速链路 |
选择原则可以概括为:能不改内核就不改,能不上 DPDK 就不上。多数企业监测点用 AF_PACKET + 多队列 + Zeek 集群即可覆盖;只有当单机持续超过 10 Gbps 且 CPU 已绑满时,才考虑 PF_RING 或 DPDK 前置分流。
用 Zeek 集群扩展监测吞吐
单进程 Zeek 只能利用一个 CPU 核,即使采集路径再快,解析也会成为瓶颈。Zeek 原生支持集群模式,典型拓扑由三类节点组成:Manager 负责接收各 Worker 上报的日志、维护全局状态、下发脚本策略;Proxy 负责分担 Manager 的日志聚合压力,并对外提供查询接口;Worker 是真正抓包和解析的进程,每个 Worker 绑定一个 CPU 核与一个采集队列。
要让集群正确工作,负载均衡器必须保证同一会话的所有数据包进入同一个 Worker。如果一条 TCP 连接的前半段被分到 Worker 1、后半段被分到 Worker 2,两个 Worker 都只能看到半条流,连接状态、协议识别和文件还原都会出错。因此分流算法通常采用五元组哈希(源 IP、目的 IP、源端口、目的端口、协议),并保持哈希表稳定,避免 Worker 数量变化时大量会话重新映射。
Zeek 自带的集群通信基于 Broker,配置时需要在 node.cfg 中声明各节点角色与监听接口。以下是一个双 Worker 的示例配置:
[manager]
type=manager
host=10.0.0.10
[proxy-1]
type=proxy
host=10.0.0.10
[worker-1]
type=worker
host=10.0.0.11
interface=af_packet::eth0
lb_method=custom
lb_procs=4
pin_cpus=0,1,2,3
[worker-2]
type=worker
host=10.0.0.12
interface=af_packet::eth0
lb_method=custom
lb_procs=4
pin_cpus=0,1,2,3
其中 lb_procs=4 表示在该 Worker 主机上启动 4 个 Zeek 进程,pin_cpus 把它们分别绑定到 0~3 号核,避免进程在核间漂移导致缓存失效。af_packet::eth0 指定使用 AF_PACKET 采集。若改用 PF_RING,接口写法为 pf_ring::eth0,并需确保 pf_ring 模块已加载。
分流一致性与丢包判断
部署完成后,判断集群是否健康主要看两个指标:各 Worker 的流量是否均衡,以及是否存在内核丢包。可以用 zeekctl status 查看进程状态,用 cat /proc/net/pf_ring/* 或 ethtool -S eth0 观察队列丢包计数。如果某个 Worker 的 pkts_dropped 持续增长,说明该核处理不过来,需要增加 Worker 数量或改用更快的采集方式。
zeek -C -r sample.pcap 对比单机解析结果,或在集群中查询同一连接的 conn.log 是否只出现在一个 Worker 的日志里。若同一 uid 出现在多个 Worker,说明哈希分流不稳定。
综合来看,采集方式的选择是吞吐、成本与运维复杂度的权衡:AF_PACKET 适合大多数常规监测点,PF_RING 适合可维护内核的中高吞吐场景,DPDK 适合骨干级高速链路。而无论选哪种采集方式,只要单机吞吐接近上限,就应通过 Zeek 集群 + 五元组哈希负载均衡把流量分摊到多个 Worker,并用 Manager/Proxy 完成日志聚合与全局分析,从而在保证会话完整性的前提下线性扩展监测能力。