基于Redis的扫描器任务调度设计方案

本文介绍了一种使用Redis实现的任务调度设计方案,适用于扫描器项目。该方案通过Redis的列表数据结构实现任务队列,结合MySQL和API接口实现任务的分发、优先级控制、状态确认及指定Agent扫描等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 最近在研究扫描器任务调度相关的东西,几番折腾后有些个人心得,心想需记录下来,避免遗忘,也当作是一种积累。之前我开发过几款扫描器(甲方内部使用),架构一直沿用:mysql(DB)+ rabbitmq(QUEUE)+ celery(调度)+ supervisor(进程监控)+ python(编程语言)。在经过一段时间的使用测试后,深感rabbitmq作为队列其架构还是过于厚重,不太适合扫描器一类的项目。当然并不是说rabbitmq就完全不适合作为扫描器的队列,而是我们应当寻求更简便的方案。

  在研究了几款开源队列产品后,我决定在最新版的扫描器中放弃沿用之前的架构,转而使用redis作为队列存储。原因主要有几点:1、redis使用简单 2、redis比较轻量 3、redis可以满足功能需求。熟悉redis的朋友可能知道它是一款开源的key-value存储系统,常常用作数据缓存服务,那么如何使它在扫描器架构中提供一个队列服务呢?


  首先需要思考一下,扫描器架构中的队列服务或者说任务调度服务,需要满足哪些功能?第一:控制扫描QPS,第二:任务优先级控制,第三:任务状态ACK确认(丢失重扫),第四:任务指定Agent扫描。

  简单解释一下,如果您的扫描器针对甲方,那么控制扫描QPS是非常重要的,因为有些业务有qps要求,胡乱扫描可能会被怼。控制任务优先级就不用多说了,比如针对某个最新漏洞的应急扫描,此任务必须优先,因为需要赶在漏洞被利用之前发现并修复(时间就是kpi)。任务状态ACK也好理解,比如某个节点在扫描过程中异常,导致扫描数据丢失,那么需要将此任务重新放回队列扫描。任务指定扫描agent需求,这个可能比较模糊我解释一下,在甲方公司,资产覆盖办公网、生产网、外网边界等,各个机房的网络还可能不通,因此扫描Agent需要覆盖到各类环境,比如办公服务器、生产网服务器,甚至各个机房服务器都需要部署。那么在添加一个扫描任务时,就需要指定这个任务需要在哪个网络环境下的节点进行扫描,因此任务指定Agent扫描需求也是必须要实现的。

  需求设计好了,那么如何实现呢?我们知道redis是一款key-value形式的存储系统,如果需要提供像rabbitmq那样的队列服务,就需要使用redis的一个特殊数据结构–列表。redis中的列表可以实现任务的插入、取出,可简单实现队列存储功能,其次列表可以左、右插入,也可以左、右取出,据此可以实现任务优先级控制。那么如何实现控制QPS、实现任务状态ACK、实现指定Agent扫描?这些功能比较复杂,不单单是靠redis就可以实现的,因此接下来我要重点谈谈如何实现以上这三个功能。

任务调度架构

任务调度部分整体架构,包含:Mysql、控制中心、REDIS、API、Agent

上图中的描述写得比较粗糙,可以直接往下看。

重点内容!!!:一个agent在redis中对应唯一一个agent列表,用来存放任务公共信息;一个task在redis中对应唯一一个task列表用来存在任务扫描数据,以及对应唯一一个task ack列表,用来存放将要ack确认的数据(包含时间戳)。

MYSQL+控制中心+REDIS

来看下MYSQL+控制中心+REDIS这一部分:

说明:这一部分实现的功能主要是:1)循环监听数据库中最新添加的任务,将其插入到指定的redis列表中;2)循环监听redis task列表,若task列表以及task_ack列表都为空,则删除agent列表中对应的任务公共信息;3)遍历task_ack列表,获取数据(包含时间戳),判断是否ack超时,若超时则将该数据重新插入task列表(这里可以设置超时次数阈值,超过上限则丢弃数据)。

执行流程

控制中心将最新任务插入redis过程:

  • 控制中心遍历mysql的task表,获取最新的任务信息,该任务信息包含任务ID以及任务环境等参数,可用其对应agent表中的一批Agent(比如任务网络环境为办公网,则该任务可分配给所有办公网agent;若任务指定了agent个数,则可选择分配给部分agent,这也就是实现了控制QPS)。
  • 控制中心获取任务数据以及对应的agent数据后,将任务数据中的公共信息插入redis中对应的agent列表(一个任务公共信息可能插入多个agent列表,由此来控制此任务由哪些agent扫描);将任务扫描数据插入redis中对应的task列表。

控制中心将任务从redis中删除过程:

  • 控制中心遍历redis中的task表,若task表为空,则判断对应的task_ack表状态,若也为空,则删除所有agent列表中包含该任务的公共信息。

REDIS+API+AGENT

来看下REDIS+API+AGENT这一部分架构:

 

说明:这一部分实现的功能主要是:1)agent通过api获取对应的任务数据,任务数据包含:1、任务公共信息2、任务扫描数据;2)agent通过api进行任务ack确认。

执行流程

Agent_01请求API获取任务信息过程:

  • agent请求API的时候,带上了自己的agent_id,比如:01
  • api从REDIS中的agent_01_list列表中获取一个任务的公共信息(注意这里是获取任务公共信息,并不是pop,即数据不会从列表中删除)
  • api从任务公共信息中获取该任务的任务ID,比如:001
  • api通过任务ID从对应的任务列表task_001_list中pop出扫描数据,与此同时将该扫描数据插入任务ack列表task_ack_001_list中。

Agent_01请求API进行任务ACK确认过程:

  • agent请求API的时候,带上了任务ID,比如:001;以及任务扫描数据,比如:thief.one
  • api从对应的任务ack列表task_ack_001中删除该扫描数据,thief.one。

实现控制QPS

  回顾一下前文中描述的,控制中心将任务信息插入到redis列表中的过程,即可以根据任务参数选择对应的agent数量,每个agent的扫描qps相对是固定的,因此用控制单个任务的agent数量,来控制此任务的扫描QPS。

实现任务状态ACK

  回顾一下前文中描述的,agent请求api获取任务信息,以及进行任务ack确认过程,即api从任务列表中取出(pop)一个数据的同时,将该数据插入任务ack列表中。当任务扫描完成后,再通过api将此数据从任务ack列表中删除。控制中心认为任务列表以及任务ack列表都为空时,此任务才算正真扫描完,事实也应该如此。

实现指定Agent扫描

  回顾一下前文中描述的,控制中心将任务信息插入到redis列表中的过程,任务在添加到mysql中时可以附带很多参数,比如网络环境、qps数量、扫描插件等等,那么网络环境参数就决定了此任务只能分配给对应网络环境的agent进行扫描。

后文

  前文介绍了一些基于redis实现的扫描器调度设计方案,当然这只是个人根据实践得出的方案,尚不够成熟,并且很多实现过程中的细节无法一一详细描述。只能说,很多时候需要靠实践中摸索出一条通往光明的道路。

 文章来源:http://thief.one/2018/08/15/1/

相关内容推荐:https://www.roncoo.com/article/index?title=redis

 

 

<think>我们正在设计一个自动化运维平台中的任务调度子系统。根据引用[3],任务调度系统包含核心业务流:任务管理系统、作业管理系统、任务调度、作业调度、作业执行、执行主机(Worker)以及执行目标主机及目标程序。我们将基于此展开设计。1.**整体架构设计**-任务调度子系统采用分层设计:-**管理层**:提供用户接口,管理任务和作业的元数据(任务管理系统、作业管理系统)。-**调度层**:负责任务的实例化、执行规划(任务调度器TaskScheduler)和作业的调度执行(作业调度器JobScheduler)。-**执行层**:由作业执行器(JobExecutor)和Worker节点组成,负责具体执行。-**目标层**:实际运行脚本或程序的目标主机。2.**核心组件详解**-**任务管理系统**:-功能:任务的定义、修改、删除、查询,以及任务依赖关系管理。-存储:任务元数据(如任务名称、类型、执行命令、依赖任务、调度周期等)存入数据库。-**作业管理系统**:-功能:作业(任务的一次实例化)的生命周期管理,包括状态跟踪(等待、运行、成功、失败)、日志记录等。-**任务调度器(TaskScheduler)**:-根据任务配置(如定时规则)实例化任务生成作业。-进行作业执行规划:确定作业执行的先后顺序(考虑任务依赖关系)。-**作业调度器(JobScheduler)**:-从作业队列中取出待执行作业,根据资源状况(如Worker负载)分配执行资源。-支持优先级调度、故障转移等策略。-**作业执行器(JobExecutor)**:-在Worker节点上运行,接收JobScheduler分配的作业。-调用目标主机上的脚本或程序,并监控执行状态。-**Worker节点**:-部署在计算节点上,负责实际执行作业。可横向扩展。-向调度层注册并定期上报心跳和资源状态。3.**关键流程**-**任务提交**:用户通过管理界面或API创建任务任务管理系统存储任务配置。-**任务实例化**:-TaskScheduler扫描任务配置,当满足触发条件(如定时到达)时,生成作业实例并推入作业队列。-对于有依赖的任务,需检查父任务状态(例如父任务成功后才触发)。-**作业调度**:-JobScheduler监听作业队列,根据调度策略(如优先级、资源匹配)选择作业。-将作业分配给负载较低的Worker节点(通过JobExecutor执行)。-**作业执行**:-JobExecutor在Worker节点上启动进程执行目标命令,并捕获输出和返回码。-实时上报执行状态给作业管理系统。-**状态同步**:作业管理系统更新作业状态,供用户查询和告警使用。4.**高可用与扩展性**-**去中心化设计**:Worker节点可动态加入,JobScheduler支持多实例部署(通过选举或分片实现负载均衡)。-**故障恢复**:-Worker失联时,JobScheduler将其上运行的作业重新分配。-任务调度器采用分布式锁保证实例化过程幂等。-**监控集成**:Worker节点上报资源使用率,结合引用[2]的监控系统,可定义规则自动处理故障(如作业失败时重试)。5.**技术选型建议**-调度框架:Celery(Python)或Quartz(Java),但需二次开发满足分布式调度需求。-存储:MySQL(任务/作业元数据),Redis(作业队列)。-通信:RPC(gRPC)或消息队列(RabbitMQ/Kafka)用于组件间通信。6.**高级特性**-**任务依赖**:使用DAG(有向无环图)描述任务依赖关系,调度时进行拓扑排序。-**超时控制**:作业执行设置超时,避免僵尸任务。-**资源隔离**:Worker节点支持容器化(Docker)运行作业。>示例:一个简单的任务调度流程>1.用户定义任务A(每天00:00执行)和任务B(依赖A成功)。>2.TaskScheduler在00:00生成A的作业实例。>3.JobScheduler分配Worker执行A作业。>4.A成功后,TaskScheduler生成B的作业实例。>5.JobScheduler分配Worker执行B作业。§§相关问题§§1.如何设计任务调度子系统的高可用架构?2.任务依赖关系在调度系统中如何实现?3.自动化运维平台中任务调度子系统的监控指标有哪些?</think>### 自动化运维平台任务调度子系统设计方案 #### 一、核心架构设计 任务调度子系统采用分层架构,主要分为以下模块: 1. **任务管理层** - 功能:提供任务定义、配置管理、版本控制等接口 - 技术实现:RESTful API + 数据库(MySQL/PostgreSQL) - 关键数据结构: $$ \text{Task} = \{ \text{id, name, type, command, params, dependencies, schedule\_policy} \} $$ 2. **调度引擎层** - **任务调度器(TaskScheduler)** - 解析任务DAG(有向无环图),处理依赖关系 - 基于时间/事件触发任务实例化 - 算法示例: ```python def schedule_tasks(task_dag): # 拓扑排序生成执行序列 sorted_tasks = topological_sort(task_dag) for task in sorted_tasks: if check_dependencies(task): # 检查前置条件 create_job_instance(task) # 生成作业实例 ``` - **作业调度器(JobScheduler)** - 采用资源感知调度算法(如DRF算法) - 动态分配Worker资源 - 状态机管理: $$ \text{JobState} \in \{ \text{PENDING, RUNNING, SUCCESS, FAILED, TIMEOUT} \} $$ 3. **执行层** - **Worker节点** - 部署监控代理(如Prometheus Exporter) - 支持容器化执行环境(Docker/Kubernetes) - **作业执行器(JobExecutor)** - 命令执行模板: ```bash ssh {target_host} "nohup {command} > {log_path} 2>&1 &" ``` #### 二、关键流程设计 1. **任务提交流程** ```mermaid graph TD A[用户提交任务] --> B[任务管理系统] B --> C[持久化任务配置] C --> D[TaskScheduler] D --> E[生成作业实例] E --> F[JobScheduler队列] ``` 2. **故障处理机制** - 基于引用[2]的IFTTT模型: ```python if check_failure(job): # 触发条件检测 execute_action(job, 'retry') # 执行预设动作 ``` - 支持三级容错策略: 1. 本地重试(≤3次) 2. Worker转移 3. 人工介入告警 #### 三、高可用设计 1. **分布式部署方案** - ZooKeeper实现调度器主从选举 - Redis分布式锁控制并发调度 $$ \text{LockKey} = \text{job}\_\text{}\{\text{id}\}\_\text{schedule} $$ 2. **数据持久化** - 元数据:关系型数据库(ACID事务保障) - 执行日志:Elasticsearch集群 - 检查点:定期持久化DAG状态 #### 四、监控体系设计 | 监控层级 | 核心指标 | 告警阈值 | |----------------|-------------------------|------------------| | 调度器 | 队列积压量 | >100任务/分钟 | | Worker节点 | CPU/MEM使用率 | >85%持续5分钟 | | 作业执行 | 失败率 | >5%/小时 | | 网络通信 | P99延迟 | >500ms | > 通过集成引用[2]的Action Channel实现自动化故障处理[^2] #### 五、典型技术栈 | 模块 | 推荐技术 | |--------------|-----------------------------------| | 调度引擎 | Celery/Airflow + Redis/RabbitMQ | | 执行环境 | Docker + Kubernetes CRD | | 状态存储 | PostgreSQL + Redis | | 监控 | Prometheus + Grafana | ### 设计验证案例 **场景:** 每日凌晨批量服务器巡检 1. 任务配置: ```json { "name": "nightly_inspection", "cron": "0 2 * * *", "command": "/scripts/inspection.sh", "timeout": 1800 } ``` 2. 执行过程: - TaskScheduler 02:00 生成作业 - JobScheduler 分配低负载Worker - JobExecutor 通过SSH在多目标机并行执行 - 结果写入Elasticsearch供可视化展示
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值