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 尤其适合以下场景:

  1. 全基因组/全外显子组分析

    从原始 fastq 到最终 VCF 的完整流程,涉及比对、去重、BQSR、变异检测、过滤等十几步。用 WDL 写出来,每一步都清晰可查。

  2. 单细胞 RNA-seq 分析

    Cell Ranger、STARsolo、Seurat 这些工具的组合调用,WDL 可以轻松处理多个样本的并行分析。

  3. 临床诊断流程

    对可追溯性和可重复性要求高的场景,WDL 的确定性执行特性非常适合。

  4. 大规模队列分析

    成百上千个样本的批量分析,WDL 配合 Cromwell 调度器,可以在云端自动扩缩容。

  5. 第三方工具集成

    你手里有一堆不同语言写的小工具,WDL 可以像胶水一样把它们粘成一条完整流水线。

我去年接了一个全基因组分析的活儿,客户要求流程必须在 AWS 和本地集群都能跑。我直接用 WDL 套了个壳,把 GATK Best Practices 的十几个工具串起来。交付的时候,客户只需要改一行输入路径就能跑通——他们都说“这才叫工作流”。

WDL 的工作流结构

为了让你对整个体系有个直观认识,我画了一张图。这张图展示了 WDL 的核心骨架:

WDL 工作流核心结构 输入数据 Workflow:编排整个流程 子流程 / 并行分支 Task:比对 Task:去重 Task:BQSR Call 调用:workflow.call_task1 + workflow.call_task2 + workflow.call_task3 输出文件

从这张图你可以看到,输入先交给 Workflow 做整体调度,Workflow 内部可以包含多个分支或串行步骤,每个步骤由一个 Task 完成。Task 之间通过 Call 调用连接起来,最终产出输出文件。

说白了,WDL 就是把你的分析思路翻译成一种计算机能看懂的流程图。你只需要关心“做什么”和“顺序是什么”,剩下的脏活累活——路径管理、环境配置、并行调度——WDL 和它的执行引擎 Cromwell 全包了。

我记得有个朋友跟我说,他在一个项目里用 Shell 脚本写过两百多行的流程,每次调试都像在破案。换成 WDL 之后,整个流程减少了至少 60% 的代码量,而且任意一步出错,日志和错误信息都清清楚楚指向具体的 Task。

小结一下

这一章我们聊了 WDL 的背景、核心概念、适用范围,也看了一个实际的代码示例。我希望你能感受到:WDL 不只是又一种新语言,它本质上是一种帮你把生物信息分析变得“可读、可复现、可扩展”的工具。

接下来的章节,我们会一步步拆解 WDL 的语法细节。从最基本的类型系统、变量声明,到复杂的条件分支、数组操作、Scatter 并行,再到如何把你的流程部署到云上和集群上。每章我都会结合自己的踩坑经历,帮你避开那些新手容易犯的错误。

准备好了?那我们开始吧。