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_PACKETPF_RINGDPDK
内核依赖原生,无额外模块需 pf_ring 模块需 UIO/VFIO,旁路内核
拷贝/中断mmap 减少拷贝,仍有中断mmap + 可选 DNA零拷贝、纯轮询
典型单机吞吐1~10 Gbps10~20 Gbps40 Gbps+
CPU 占用中较低低但需独占核
部署复杂度低中高
适用场景常规监测点中高吞吐、可维护内核骨干/数据中心高速链路

选择原则可以概括为:能不改内核就不改,能不上 DPDK 就不上。多数企业监测点用 AF_PACKET + 多队列 + Zeek 集群即可覆盖;只有当单机持续超过 10 Gbps 且 CPU 已绑满时,才考虑 PF_RING 或 DPDK 前置分流。

采集路径与 Zeek 集群负载均衡 物理网卡 RSS 多队列 AF_PACKET PACKET_MMAP 环形缓冲 1~10 Gbps · 零依赖 PF_RING pf_ring 模块 / DNA 10~20 Gbps · 需内核模块 DPDK UIO/VFIO 轮询 · 大页 40 Gbps+ · 独占网卡 负载均衡器 五元组哈希分流 Flow 一致性保证 Zeek 自带 LB / PF_RING 集群模式 Zeek Worker 1 CPU 核 0 Zeek Worker 2 CPU 核 1 Zeek Worker N CPU 核 N-1 Zeek Manager 日志聚合 / 策略下发 关键点:① 采集路径决定单机上限;② 负载均衡必须保证同一会话(五元组)落到同一 Worker,否则会话状态会分裂; ③ Zeek 集群通过 Manager + Proxy + Worker 分层,Worker 只负责解析,Manager 负责日志聚合与全局策略。

用 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 数量或改用更快的采集方式。

分流一致性验证方法:在 Worker 上执行 zeek -C -r sample.pcap 对比单机解析结果,或在集群中查询同一连接的 conn.log 是否只出现在一个 Worker 的日志里。若同一 uid 出现在多个 Worker,说明哈希分流不稳定。
使用 DPDK 时,网卡会被用户态驱动独占,内核将无法再看到该网卡,SSH 管理连接若走同一张卡会立即中断。务必保留一张独立管理网卡,并在绑定前确认驱动与 CPU 隔离配置。

综合来看,采集方式的选择是吞吐、成本与运维复杂度的权衡:AF_PACKET 适合大多数常规监测点,PF_RING 适合可维护内核的中高吞吐场景,DPDK 适合骨干级高速链路。而无论选哪种采集方式,只要单机吞吐接近上限,就应通过 Zeek 集群 + 五元组哈希负载均衡把流量分摊到多个 Worker,并用 Manager/Proxy 完成日志聚合与全局分析,从而在保证会话完整性的前提下线性扩展监测能力。