ZeekCtl源码分析

deploy流程

一. check

  1. 创建{prefix}/spool/tmp/check-config-XXXXX目录
  2. 将install命令应生成的zeek脚本放入临时目录
  3. 运行check-config脚本,启动所有node(不收包,跑完zeek脚本后正常退出)
  4. 检查node的exitcode是否为0

二. install

  1. 删除以前安装的策略文件以避免混淆
  2. {prefix}/spool/installed-scripts-do-not-touch下创建sizeauto目录,分别存放站点策略和配置信息
  3. 安装本地站点策略(将{prefix}/share/zeek/site/local.zeek拷贝到策略文件目录)
  4. 创建包含集群布局的Zeek脚本cluster-layout.zeek
  5. 创建包含本地网络列表的Zeek脚本local-networks.zeek
  6. 创建包含zeekctl配置的Zeek脚本zeekctl-config.zeek
  7. 将logger节点的工作目录软连接到{prefix}/logs/current

三. stop

  1. 检查节点是否意外崩溃,如果是则生成崩溃报告
  2. 调用stop脚本对节点发送SIGTERM信号
  3. 再次检查节点是否正常终止(默认timeout 60秒)
  4. 如果还在运行,就使用SIGKILL信号,强行终止
  5. 运行post-terminate脚本为节点进行清理任务

Zeek终止后的清理任务: 1. 将节点的工作目录移动到tmp目录并创建新的工作目录。如果节点崩溃,则创建崩溃报告 2. 尝试归档所有日志(如果失败,则发送电子邮件),最后(如果节点没有崩溃),如果所有日志都成功归档,则删除tmp目录

四. start

  1. 忽略仍在运行的节点
  2. 检查节点是否意外崩溃,如果是则生成崩溃报告
  3. 创建节点工作目录{prefix}/spool/XXXX
  4. 运行start脚本启动节点
  5. 检查进程是否确实启动了

start脚本: 1. 检查当前用户是否有工作进程读写权限 2. 不挂起并在后台运行run-zeek脚本启动zeek,并将标准输出和标准错误分别重定向到stdout.logstderr.log 3. 循环等待.pid文件,获取Zeek的PID并回写给zeekctl

run-zeek脚本: 1. 使用ulimit命令解锁各种资源限制,并写入stdout.log 2. 将环境变量写入.env_vars 3. 将Zeek命令行写入.cmdline 4. 将当前时间写入.startup 5. 不挂起并在后台启动zeek,如果有pin_cpu就使用taskset命令设置亲和性 6. 将Zeek的PID写入.pid 7. 等待zeek进程

Q & A

Q: 为啥check检查不到zeek监听端口冲突等问题?

A: 因为check-config启动时会设置环境变量ZEEKCTL_CHECK_CONFIG=1,zeek不会在群集中建立节点之间通信,自然不会监听端口,也就不会引发异常

netstats流程

  1. zeekctl调用_send_event_init发送Control::net_stats_request事件
  2. worker节点触发Control::net_stats_request事件,计算出网络状态
  3. worker节点触发Control::net_stats_response事件,将消息传回zeekctl
  4. zeekctl调用_send_event_wait取到消息

_send_event_init实现细节: 1. 创建端点 2. 创建数据订阅者和状态订阅者 3. 建立连接 4. 等待连接建立(通过状态订阅者查看连接状态) 5. 构建Event并通过数据订阅者publish给Zeek

多个zeekctl之间如何竞争资源?

文件实现的多进程锁

zeekctl使用函数装饰器让命令在执行前上锁,执行后解锁

上锁流程

  1. 获取当前zeekctl的PID
  2. 创建{prefix}/spool/lock.XXXX文件,并写入PID
  3. 尝试将lock.XXXX硬链接到lock
  4. 对比链接前后inode链接数,判断是否链接成功
  5. 如果链接成功,说明当前没有其他zeekctl进程上锁,成功上锁
  6. 如果链接失败,说明当前已有其他zeekctl进程上锁,上锁失败

上锁失败后的流程

  • 如果已上锁的进程存活,则等待30秒,每隔1秒尝试获取锁
  • 如果已上锁的进程已死亡,则直接删除锁文件

解锁流程

  • 删除lock文件

除了confignodesexec命令外,其他所有命令都会上锁

deploy后,Zeek集群如何建立?

以下脚本均位于base/frameworks/cluster/目录下

  1. 加载main.zeek:定义各种集群事件(如在其他节点成功连接上自己时,会发送hello事件给对方)、节点topic名、record类型定义
  2. 加载pools.zeek:定义用于管理群集节点池的接口。使用pools可以方便的在集群内的节点之间分发工作或数据
  3. 加载zeekctl在install阶段创建的包含集群布局的Zeek脚本cluster-layout.zeek
  4. 判断自己是否在集群里,如果在,就加载setup-connections.zeek脚本,将自己连接到集群
  5. 根据自己的节点类型,加载不同的./nodes/xxxx.zeek脚本(如果集群中没有logger,那么manager也会加载logger的脚本,兼职logger)
  6. 加载broker-stores.zeek:初始化分布式键值存储接口(可以数据持久化)

Zeek集群的布局

  • 每个worker都连接到所有proxy。

  • 所有节点连接到所有logger和manager。