WDL多任务工作流开发与资源调度 - 第1节
为什么你需要了解 WDL?
做生物信息学的朋友,肯定都遇到过这种场景:一个分析流程涉及十几个工具,每个工具又有几十个参数,还得手动管理输入输出路径。更头疼的是,今天在本地服务器跑通了,明天换到集群上又得重新配置一遍。
说实话,我刚入行那两年,大部分时间都花在写各种 Shell 脚本和 Makefile 上。一个 RNA-seq 分析流程,脚本写了三百多行,后来项目交接给同事,对方看了半天问我:“这步输出的文件到底去哪儿了?”
那时候我就想,能不能有一种专门的工作流描述语言,既能把分析步骤写得清清楚楚,又能自动处理输入输出、并行调度这类琐事?后来我遇到了 WDL——Workflow Description Language。嗯,就是它了。
WDL 到底是什么?
WDL 是 Broad Institute 开发的一种工作流描述语言。简单来说,它是一种专门用来写生物信息学分析流程的语言。你只要告诉它“输入什么、做什么、输出什么”,剩下的路径管理、并行执行、资源分配这些事,WDL 自动帮你搞定。
我举个例子,你用 Shell 写一个比对步骤可能是这样:
bwa mem reference.fa sample_R1.fq sample_R2.fq > aligned.sam
samtools view -bS aligned.sam > aligned.bam
samtools sort aligned.bam -o sorted.bam
换成 WDL 呢?你把上面的命令封装成一个 task,然后像搭积木一样,把多个 task 串成一条流水线。代码结构清晰,输入输出一目了然。
WDL 的核心概念
学 WDL 之前,你要先理解这几个基本概念:
| 概念 | 作用 | 类比 |
|---|---|---|
| Workflow | 定义整个流程的骨架,描述步骤之间的调用关系 | 就像做饭的菜谱:先洗菜、再切菜、最后炒菜 |
| Task | 定义一个个具体的分析步骤,包含命令和容器 | 菜谱里的单个步骤:比如“把土豆切成块” |
| Call | 在 Workflow 中调用 Task,并传入参数 | 执行“切土豆”这个动作 |
| Input/Output | 声明每个 Task 的输入文件和输出文件 | 告诉别人你要用什么材料、做出什么成品 |
一个小技巧:刚开始学的时候,你可以在脑子里把 Workflow 当成一个“总指挥”,Task 当成一个个“工人”。总指挥告诉工人该做什么,工人干完活把结果交回来,总指挥再安排下一步。
一个简单的 WDL 示例
光说理论有点抽象,我们直接看代码。下面这个例子做了两件事:先把两个 fastq 文件压缩成 bam,再对 bam 文件跑一个简单统计。
version 1.0
workflow alignment_pipeline {
input {
File ref
Array[File] fastq_files
}
call align {
input:
reference = ref,
reads = fastq_files
}
call stats {
input:
bam = align.output_bam
}
output {
File final_bam = align.output_bam
File report = stats.report
}
}
task align {
input {
File reference
Array[File] reads
}
command {
bwa mem ${reference} ${reads[0]} ${reads[1]} > aligned.sam
samtools view -bS aligned.sam > aligned.bam
samtools sort aligned.bam -o sorted.bam
}
output {
File output_bam = "sorted.bam"
}
runtime {
docker: "biocontainers/bwa:v0.7.17"
cpu: 4
memory: "8 GB"
}
}
task stats {
input {
File bam
}
command {
samtools flagstat ${bam} > flagstat.txt
samtools depth ${bam} | awk '{sum+=$3} END {print "Average depth: ", sum/NR}' > depth.txt
}
output {
File report = "flagstat.txt"
}
runtime {
docker: "biocontainers/samtools:v1.10"
cpu: 2
memory: "4 GB"
}
}
你看,核心逻辑就是这么清晰。每个 task 只管自己的事,workflow 只负责编排顺序和传参。就算是几百步的大流程,写成这样你也绝对能看懂。
注意:WDL 的 command 块里写的是正经的 Shell 命令。你平时怎么在终端敲命令,这里就怎么写。WDL 执行时会在容器里运行这些命令,不会污染你的宿主环境。
为什么 WDL 能流行起来?
你可能要问了:“我用 Snakemake 或者 Nextflow 也挺好的,为什么非要学 WDL?”
我自己的体会是,WDL 有几个难以替代的优势:
- 可移植性一流。 我写好一个 WDL 流程,本地测试通过后,直接丢到 Google Cloud、AWS 或者自家集群上跑,完全不需要改代码。因为 WDL 的 runtime 块把镜像、资源、环境这些都定死了。
- 天然的并行调度。 你只需要声明输入是 Array[File],WDL 会自动把每个元素分拆成独立任务并行执行,不需要你手动写循环或并行脚本。
- 模块化设计。 一个 task 就是一个独立的模块,你可以把它拷贝出来用在其他流程里,不需要改里面任何一行命令。
- 版本控制友好。 WDL 是纯文本,一行代码就是一个人体可读的工作流。你可以把它放到 Git 里做版本管理,每次改动都有记录。
- 社区生态成熟。 Broad Institute 维护了大量标准工具库,你要做 GATK 分析、单细胞分析、肿瘤变异检测,基本都有现成的 WDL 可以直接用。
我的个人经验:最开始我在一个项目中同时用了 Shell 脚本和 WDL 做对比测试。同样的分析,Shell 脚本用了 5 天才调试完,WDL 版本只花了两天。而且后续同事接手维护时,明显更愿意去改 WDL 的流程——因为看得懂、改起来有底。
WDL 的典型应用场景
根据我这几年的使用经验,WDL 尤其适合以下场景:
- 全基因组/全外显子组分析
从原始 fastq 到最终 VCF 的完整流程,涉及比对、去重、BQSR、变异检测、过滤等十几步。用 WDL 写出来,每一步都清晰可查。
- 单细胞 RNA-seq 分析
Cell Ranger、STARsolo、Seurat 这些工具的组合调用,WDL 可以轻松处理多个样本的并行分析。
- 临床诊断流程
对可追溯性和可重复性要求高的场景,WDL 的确定性执行特性非常适合。
- 大规模队列分析
成百上千个样本的批量分析,WDL 配合 Cromwell 调度器,可以在云端自动扩缩容。
- 第三方工具集成
你手里有一堆不同语言写的小工具,WDL 可以像胶水一样把它们粘成一条完整流水线。
我去年接了一个全基因组分析的活儿,客户要求流程必须在 AWS 和本地集群都能跑。我直接用 WDL 套了个壳,把 GATK Best Practices 的十几个工具串起来。交付的时候,客户只需要改一行输入路径就能跑通——他们都说“这才叫工作流”。
WDL 的工作流结构
为了让你对整个体系有个直观认识,我画了一张图。这张图展示了 WDL 的核心骨架:
从这张图你可以看到,输入先交给 Workflow 做整体调度,Workflow 内部可以包含多个分支或串行步骤,每个步骤由一个 Task 完成。Task 之间通过 Call 调用连接起来,最终产出输出文件。
说白了,WDL 就是把你的分析思路翻译成一种计算机能看懂的流程图。你只需要关心“做什么”和“顺序是什么”,剩下的脏活累活——路径管理、环境配置、并行调度——WDL 和它的执行引擎 Cromwell 全包了。
我记得有个朋友跟我说,他在一个项目里用 Shell 脚本写过两百多行的流程,每次调试都像在破案。换成 WDL 之后,整个流程减少了至少 60% 的代码量,而且任意一步出错,日志和错误信息都清清楚楚指向具体的 Task。
小结一下
这一章我们聊了 WDL 的背景、核心概念、适用范围,也看了一个实际的代码示例。我希望你能感受到:WDL 不只是又一种新语言,它本质上是一种帮你把生物信息分析变得“可读、可复现、可扩展”的工具。
接下来的章节,我们会一步步拆解 WDL 的语法细节。从最基本的类型系统、变量声明,到复杂的条件分支、数组操作、Scatter 并行,再到如何把你的流程部署到云上和集群上。每章我都会结合自己的踩坑经历,帮你避开那些新手容易犯的错误。
准备好了?那我们开始吧。