应用发布任务引擎
罗扬
【摘要】
本文介绍在应用发布自动化v3版本中,使用的任务引擎。
【正文】
应用是什么:为了业务拆出来的不同职责的工程,或者技术点来说一组可以提供完整业务功能的代码工程。举个例子:英雄联盟。
模块是什么:可以提供完整的基本功能的代码工程,比如说:英雄联盟中的登录模块,匹配模块,地图模块等。
应用发布最简单的场景是:在一台主机上,把新的代码替换旧的代码。我们把这个操作称之为“原子任务”,然后其中组成这个任务的操作,比如说:分发程序包,解压程序包,停止服务,备份旧代码,替换代码,重启服务,我们把这些操作称之为“原子操作”,很显然原子任务是由原子操作组成的,并且原子操作之间存在着复杂的执行关系。
介绍完几个名词,我们可以看看企业中比较简单的发布场景:对英雄联盟进行一次应用发布,本次只对登录模块进行更新。那么本次发布操作实际为——登录模块的所有主机的原子任务的集合,根据不同的发布策略(蓝绿发布,滚动发布等),我们可能对这些原子任务进行分批发布,批次之间可能存在执行关系(并行或者串行发布),批次内部的原子任务也可能存在执行关系。
我们把发布场景再设计复杂一些,多个模块发布:一般情况下,我们会将模块进行分批处理(比如说核心模块和非核心模块。比如说第二批模块依赖第一批模块),不同批次的模块之间也存在着执行关系和依赖关系(前置依赖),如图1-1
图1-1
我们平常会有这样一些需求,我们有很多的任务需要定时或者重复执行,任务之间存在着执行关系或者依赖关系,还可能需要把这些任务去组成一些更大的任务,更大的任务之间也可能存在着依赖关系和执行关系(串行,并行)。我们通过可视化编排把这些任务按照一定的规则组装在一起,形成一个可重复执行的模板。然后按照编排好的规则进行任务的执行,并在执行过程中可以对流程进行控制(暂停,停止等),如图1-2:
图1-2
上面介绍应用发布时,可以知道应用发布存在着两个流程的编排,一个是原子操作之间需要按照实际情况进行编排组合,一个是原子任务之间存在着复杂的依赖、执行关系。前者是执行域,关注的是具体技术的操作,后者是管理域,关注的是整个发布任务的组成关系、关联关系和执行情况。
我们将两者进行解耦,原子操作放在蓝鲸平台中标准运维中,而原子任务放在应用发布自动化v3中。
而在标准运维中,有可重复执行的流程模板,模板在执行后的生成任务这样一个逻辑,并且流程模板中可以设置参数,在实例化时再定义参数,通过这样一个逻辑我们可以将多个任务抽象成一个模板,传入不同的参数从而实例化成不同的任务。
那么应用发布自动化v3中,原子任务和原子操作的关系如图1-3:
图1-3
解决了原子操作的复杂关系,其实在发布的场景中,对于原子任务的编排也是有着复杂的关系,我们按照图1-1的场景进行了抽象,从上而下:
1,
2,
3,
4,
那么在应用发布自动化v3中,应用发布的场景就变成了图1-4:
图1-4
模板的生成本质上就是前端提供可视化编排,生成后台任务引擎所需要的数据,这里就不多讲了,根据不同人对前端的要求不同,场景不同,有不同的实现,举一个例子:如图2-1,图2-2
图2-1
图2-2
后台任务引擎的难点有三:
1,
2,
3,
那么在后台任务引擎的逻辑中,我们将上述难点进行了抽象分离,专门有一个逻辑进行主机任务的调度获取,一个逻辑进行执行,一个逻辑进行状态的同步,一个逻辑进行任务的控制。
图2-3
如图2-3:我们制作一个队列来控制并发量,run_queue为当前执行的队列,当它的长度小于我们指定的并发量时,就会调用get_run_task函数去获取当前可以执行的主机任务。后续的操作就是把run_queue里面所有的任务进行一次执行。
图2-4
如图2-4,get_run_task函数,他会获取这个步骤下可以执行的节点,node_start函数将会获取这个节点下可执行的批次,以此类推,返回会当前状态下,可以执行的主机任务列表。
图2-5
图2-6
如图2-5,图2-6,host_run函数,执行每一个主机任务,用于调度标准运维,在标准运维中我们需要通过三个接口才能执行一个任务,一,根据模板,传入参数,创建任务(_create_bkops),二,启动任务(_run_bkops),三,获取任务状态(_get_bkops_state_cell),因为我们无法知道任务何时结束,所以我们采用轮询的方式查看状态,那么任务没有执行完成,我们也不需要因为它阻塞后续的执行。通过index的方式告知host_run函数,这个任务执行到哪一步,这样,我们就可以将并发的压力全部放在标准运维中,应用发布自动化v3上只需要用循环的方式即可。
图2-7
如图2-7,当一个主机任务结束的时候,我们需要同步一下关于这个主机任务上游的环节的状态,这里我们会把结束的主机任务存下来,对他上游的批次,节点,步骤的状态进行一次同步更新。
在任务执行中,我们需要对任务进行操控,本质上就是对于任务引擎和主机任务对应的标准运维任务进行操控,举一个暂停的例子:
图2-8
如图2-8,我们在暂停任务时,首先将任务和当前运行的主机任务的上游环节的状态全部置成暂停(在任务执行中,会有大量的埋点判断,当判断到任务状态为暂停,异常,错误,停止时,任务进程会退出执行),然后将所有进行中的主机任务拿到,将他们对应的标准运维任务通过接口操控标准运维让其暂停(这里需要考虑如果任务已经结束了的情况,毕竟标准运维任务的状态和应用发布自动化v3中存的状态存在着时间差),如果暂停操作成功,将主机任务的状态置成暂停即可。
通过这种思路,我们可以对任务进行暂停,恢复,停止,跳过等操作。