ZeekCtl源码分析
deploy流程
一. check
- 创建
{prefix}/spool/tmp/check-config-XXXXX
目录 - 将install命令应生成的zeek脚本放入临时目录
- 运行
check-config
脚本,启动所有node(不收包,跑完zeek脚本后正常退出) - 检查node的exitcode是否为0
二. install
- 删除以前安装的策略文件以避免混淆
- 在
{prefix}/spool/installed-scripts-do-not-touch
下创建size
和auto
目录,分别存放站点策略和配置信息 - 安装本地站点策略(将
{prefix}/share/zeek/site/local.zeek
拷贝到策略文件目录) - 创建包含集群布局的Zeek脚本
cluster-layout.zeek
- 创建包含本地网络列表的Zeek脚本
local-networks.zeek
- 创建包含zeekctl配置的Zeek脚本
zeekctl-config.zeek
- 将logger节点的工作目录软连接到
{prefix}/logs/current
三. stop
- 检查节点是否意外崩溃,如果是则生成崩溃报告
- 调用
stop
脚本对节点发送SIGTERM信号 - 再次检查节点是否正常终止(默认timeout 60秒)
- 如果还在运行,就使用SIGKILL信号,强行终止
- 运行
post-terminate
脚本为节点进行清理任务
Zeek终止后的清理任务: 1. 将节点的工作目录移动到tmp目录并创建新的工作目录。如果节点崩溃,则创建崩溃报告 2. 尝试归档所有日志(如果失败,则发送电子邮件),最后(如果节点没有崩溃),如果所有日志都成功归档,则删除tmp目录
四. start
- 忽略仍在运行的节点
- 检查节点是否意外崩溃,如果是则生成崩溃报告
- 创建节点工作目录
{prefix}/spool/XXXX
- 运行
start
脚本启动节点 - 检查进程是否确实启动了
start
脚本: 1. 检查当前用户是否有工作进程读写权限 2. 不挂起并在后台运行run-zeek
脚本启动zeek,并将标准输出和标准错误分别重定向到stdout.log
和stderr.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流程
- zeekctl调用
_send_event_init
发送Control::net_stats_request
事件 - worker节点触发
Control::net_stats_request
事件,计算出网络状态 - worker节点触发
Control::net_stats_response
事件,将消息传回zeekctl - zeekctl调用
_send_event_wait
取到消息
_send_event_init
实现细节: 1. 创建端点 2. 创建数据订阅者和状态订阅者 3. 建立连接 4. 等待连接建立(通过状态订阅者查看连接状态) 5. 构建Event并通过数据订阅者publish给Zeek
多个zeekctl之间如何竞争资源?
文件实现的多进程锁
zeekctl使用函数装饰器让命令在执行前上锁,执行后解锁
上锁流程
- 获取当前zeekctl的PID
- 创建
{prefix}/spool/lock.XXXX
文件,并写入PID - 尝试将
lock.XXXX
硬链接到lock
- 对比链接前后inode链接数,判断是否链接成功
- 如果链接成功,说明当前没有其他zeekctl进程上锁,成功上锁
- 如果链接失败,说明当前已有其他zeekctl进程上锁,上锁失败
上锁失败后的流程
- 如果已上锁的进程存活,则等待30秒,每隔1秒尝试获取锁
- 如果已上锁的进程已死亡,则直接删除锁文件
解锁流程
- 删除
lock
文件
除了
config
、nodes
、exec
命令外,其他所有命令都会上锁
deploy后,Zeek集群如何建立?
以下脚本均位于
base/frameworks/cluster/
目录下
- 加载
main.zeek
:定义各种集群事件(如在其他节点成功连接上自己时,会发送hello事件给对方)、节点topic名、record类型定义 - 加载
pools.zeek
:定义用于管理群集节点池的接口。使用pools可以方便的在集群内的节点之间分发工作或数据 - 加载zeekctl在install阶段创建的包含集群布局的Zeek脚本
cluster-layout.zeek
- 判断自己是否在集群里,如果在,就加载
setup-connections.zeek
脚本,将自己连接到集群 - 根据自己的节点类型,加载不同的
./nodes/xxxx.zeek
脚本(如果集群中没有logger,那么manager也会加载logger的脚本,兼职logger) - 加载
broker-stores.zeek
:初始化分布式键值存储接口(可以数据持久化)
Zeek集群的布局
每个worker都连接到所有proxy。
所有节点连接到所有logger和manager。
