什么是按编码解码的的方式构造任务流
以python脚本为例,我们都知道脚本不能脱离python解释器而独立运行,脚本里的代码为编码前的状态,经过解释器处理的机器代码为编码后的,能直接在物理机上运行。
类比于以上的示例,这里我们把初始定义的称为编码前的任务流,经过解析工具生成特定环境下可投递任务流称为编码后的任务流,相较于以往直接编写任务流具有以下几点突出的优势。
- 与流程无关
在编码解码的思想下,我们编写解码工具的核心便不是流程本身,而是如何定义流程的书写规则,所有流程共同遵守一套规则,1. 可以实现用工具解码后的流程为可投递流程; 2. 规则一定,便于流程负责人更关注流程的逻辑,生成结果的意义,而不是如何正常的创建流程; 3. 提供统一的文件格式输入,便可以正常的创建流程,与流程解耦合。
此情况下,解码工具的开发维护,和流程本身的开发维护就可以分开进行,分工产生效能。 - 与项目无关
首先我们需要定义项目的含义,项目是指使用特定流程对特定的对象进行有逻辑、有意义的数据分析,最终交付运行结果和出具运行报告的。
特定的项目在代码层面,表现为可变的参数输入,不同的项目我们通过读取配置文件中的信息即可实现流程对不同项目的使用。 - 与特定的集群类型解耦,按需求生成对应的任务流
不同的集群所接受的任务流定义文件是不一样的,一般情况下流程运行过程如下:
但是,这种模式中流程是根据具体的任务投递器和HPC架构进行设计的,很难对其他任务投递器和HPC架构进行兼容。流程 + 项目 → 流程资源定义文件 → 投递器 → HPC
我们可以重新设计以上的逻辑,加入共用的解码工具,实现流程的扩展:# 先生成适用环境的流程模板,在根据具体项目生成项目对应的运行流程 初始流程模板 + 共用解码工具 → 适用环境的流程模板 + 项目 → 流程资源定义文件 → 投递器 → HPC # 一步到位,根据项目、流程模板和共用解码工具生成运行流程 初始流程模板 + 共用解码工具 + 项目 → 流程资源定义文件 → 投递器 → HPC
- 流程模块化,资源定义更清晰
原来我们可能将功能写在python、perl、R或shell等脚本中,既不便于功能升级,也及其依赖解释器。
业务代码(底层代码)使用python、perl等语言编写无法更改,但建议业务代码的串联工具推荐使用makefile作为技术进行使用,1. 模块化,在makefile中定义不同的target即可定义任何自己需求的模块,2. 扩展性高, 可以直接读取配置文件获取变量,也可以通过参数定义的方式获取变量。 - 运行代码与运行结果关联
这里提倡的方式是运行代码目录与分析结果目录存放一致,便于task异常后的手动排错。
HPC中,常见的可用任务流
- SGE - SJM
SGE,基本上是传统集群最为常见类型,基于SGE集群开发的任务投递器有很多,其中不乏优秀的开源工具,这里介绍SJM主要是由于其定义任务和工作流的简洁性,任务监控及其日志记录等功能等完整性,详细的请常见SJM HPC-SGE 任务流投递管理工具简介。 - SGE - WDL
WDL,全程是workflow define language,译为工作流描述语言,具有一次定义流程,跨平台使用等特性,基于模块的整合而从头搭建流程,具有极强的书写格式要求,对初学者不太友好,但及其清晰且必要的标注了模块的输入输出,便于流程的阅读和拓展开发,所谓是越熟悉越容易上手。
详细阅读可参见WDL教程-基本概念篇和WDL - 基础结构(入门)。 - K8S - ARGO
以上两个都是介绍在在传统集群上的任务流定义(当然WDL不止适用于SGE集群,通过配置beckend可以实现超多集群环境的适配),基于容器和无服务器计算的兴起,K8S集群的占比在逐年上涨,原生的K8S资源定义不方便上手,而基于K8S开发的ARGO,它的资源定义相对而言便利、简洁,详细的信息参见ARGO-工作流部署与管理工具。
传统流程如何拆分
这里,我们把流程定义为以下资源的集合,资源分离后可以实现各个部分的独立部署,并分离升级热点
流程 = 流程代码 + 数据库 + 环境
- 流程代码
这里我们推荐使用gitlab管理组织内部的代码,迭代热点较高,并创建在局域网内,局域网外访问可通过Nginx代理访问。 - 数据库
一般而言又可以分为静态资源和动态资源,一般存放至硬盘和内存中,迭代热点较低。 - 环境
由于各类集群的差异,安装在集群的软件可能因各种环境的差异而导致使用异常,这里我们推荐使用容器技术进行环境封装,一般常用的为docker,迭代热点居中。
如何定义共用模板
模板是一个便于书写、理解和易于工具解析的文本,一般以通用的格式进行书写,例如xml、json、yaml等,由于yaml结构化展示更友好,这里我们选用yaml。
我们需要在模板中定义任务的资源属性,包括任务CPU、memory、队列、ENV、依赖关系以及最重要的任务命令。
我们来定义一个假设的任务流,数据的质量控制(QC),任务命令书写在{BIN}/Modules/QC.mk
文件中:
- step1:合并单个样本的所有数据(merge_data),任务流起点,无依赖模块;
- step2:单个样本的质量控制(qc),在merge_data 对应样本运行结束后运行;
- step3:所有样本的质控结果合并(qc_summary),任务流的终止模块,在所有模块结束后运行。
这个任务流我们就可以定义为如下结构:
default:
Queue: "test.q"
CPU: 2
Memory: "2G"
Image: "alpha:v1"
pipeline:
QC:
merge_data:
Part: "sample"
Queue: "test.q"
Image: "alpha:v1"
Depend:
-
Command: "{make} -f {BIN}/Modules/QC.mk outdir={OUTDIR} sample_id={Part[0]} merge_data"
qc:
Image: "centos:v1"
Depend:
- "merge_data_{Part[0]}"
Part: "Sample"
sched_options: "-V -cwd -l vf=2G,p=1"
Command:
- "{make} -f {BIN}/Modules/QC.mk sample_id={Part[0]} outdir={OUTDIR} qc"
- "echo yes && whoami"
qc_summary:
Image: "alpha:v1"
Depend:
- "qc"
- "merge_data"
Command: "{make} -f {BIN}/Modules/QC.mk outdir={OUTDIR} scriptdir={OUTDIR} qc_summary"
定义变量
变量,在流程生成与运行中是个及其重要的概念,它担负起流程运行灵活度的作用,好的变量设置可以使得流程构造很清晰。从使用中,变量主要分为三个大的类别,分别是流程相关变量、流程生成工具相关变量和任务/项目相关的变量。
- 流程相关变量
包括了流程中使用的软件、数据库路径和资源接口方法的定义,这些变量与待分析的项目无必然关系。
一般而言书写在配置文件中,通过中层的代码调用,配置给底层的代码进行执行,这里我们定义的中层的代码,指定是通过makefile书写而成的模板,makefile具有良好的解析配置文件和传递变量的能力,配置文件示例make_config.txt
如下:a=xxxx b=dddd
- 流程生成工具相关变量
指定义好任务流模板后,公共解码工具需要使用到的软件、模板和其他的资源,一般定义在工具目录的配置文件目录中,格式一般为ini
。[software] docker=docker sjm=/usr/bin/sjm java=/usr/bin/java make=make [image] image_home= [version] version_sjm=v1.0 [lib] sjm= sge_root=/opt/... [mount] mount=/etc/passwd|/etc/group
- 任务/项目相关的变量
与特定任务/项目相关,随着任务/项目的不同而发生改变的变量,一般也是书写在ini
文件中,示例如下。[sample] A1=A11 A12 [database] mysql= [others] b=0000000000
定义任务全局图,实现特定方案下的任务流构造
通过一般模板的定义,pipeline的关系如下:
parent_module:
- children_module
- task1
- Command
- Depend
- Name
- task2
- task3
- SGE - SJM 条件下的实现
待更新 - SGE - WDL 条件下的实现
待更新 - K8S - ARGO 条件下的实现
待更新