全面迁移到WDL
背景
在构建生物信息分析流程时,工作流语言的选择至关重要。我最初选择了 Nextflow,毕竟它是主流选择,社区活跃、文档丰富。但随着流程复杂度的增加,Nextflow 的语法让我越来越难以忍受。
为什么放弃 Nextflow
Nextflow 基于 Groovy,语法灵活但同时也意味着——过于灵活。同一个逻辑可以写出 N 种风格,阅读他人写的流程常常是一种折磨:
// 风格1
process align {
input:
tuple val(sample_id), path(reads)
output:
tuple val(sample_id), path("*.bam")
script:
"""
bwa mem -t 16 ref.fa ${reads} | samtools view -bS - > ${sample_id}.bam
"""
}
// 风格2:各种花式写法
align = {
exec "bwa mem -t 16 $ref $reads > $output.bam"
}
更让人头疼的是,Nextflow 的 DSL2 虽然引入了模块化,但同时也带来了更多的语法糖和隐式行为。调试一个复杂流程时,往往需要在多层抽象中追踪数据流向,心智负担很重。
WDL:所见即所得
相比之下,WDL(Workflow Description Language)给我的感觉是——所见即所得:
version 1.2
workflow my_workflow {
input {
File reference
File reads
}
call align {
input:
reference = reference,
reads = reads
}
}
task align {
input {
File reference
File reads
}
command <<<
bwa mem -t 16 ${reference} ${reads} | samtools view -bS - > output.bam
>>>
output {
File bam = "output.bam"
}
}
WDL 1.2 版本之后,还支持了文件夹作为参数导入,这在处理多文件输入时非常方便:
input {
Directory reference_dir # 直接传入文件夹
}
运行时:miniwdl vs Cromwell
WDL 的官方运行时是 Cromwell(由 Broad Institute 开发)。但 Cromwell 太重了:
- 依赖 Java
- 配置复杂
- 资源占用大
- 主要面向大规模集群调度
对于中小规模场景,miniwdl 是一个轻量的替代方案:
| 特性 | Cromwell | miniwdl |
|---|---|---|
| 语言 | Java | Python |
| 依赖 | 重 | 轻(纯Python) |
| 启动速度 | 慢 | 秒级 |
| 适用场景 | 大规模集群 | 单机/小集群 |
| 学习曲线 | 陡峭 | 平缓 |
开发效率对比:WDL + miniwdl 的组合,开发效率比 Nextflow 高很多。语法清晰、调试方便、错误信息直观。
美中不足
miniwdl 并非完美。最大的问题是:没有原生的 FastAPI 接口。
如果想要构建一个 Web 服务来管理流程,需要自行开发。这其实也是 miniwdl 定位决定的——它是一个命令行工具,而非 Web 服务框架。
我的解决方案:Server + Agent 架构
既然没有现成的,那就自己造轮子。我的设计方案如下:
┌─────────────────────────────────────────────────────────┐
│ Server 端 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Web UI │ │ FastAPI │ │ Database │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
↑
│ HTTP (主动推送)
↓
┌─────────────────────────────────────────────────────────┐
│ Agent 端(部署在计算实例上) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 定时任务:每分钟抓取 miniwdl 状态 → 推送给 Server │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────┐ │
│ │ miniwdl │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
核心设计
- Agent 主动推送:Agent 每分钟抓取一次 miniwdl 的运行状态,主动推送给 Server
- 无需开放端口:计算实例不需要监听任何端口,安全风险更低
- 无需知道实例 IP:Server 不需要维护实例列表,Agent 主动上报身份
- 实时状态同步:Server 端可以实时掌握所有计算实例的流程动态
工作流程
Agent 启动
↓
检测本地 miniwdl 运行状态
↓
POST /report → Server
{
"agent_id": "instance-001",
"workflow": "wes-analysis",
"status": "running",
"progress": "45%",
"tasks": [...]
}
↓
Server 更新数据库
↓
Web UI 展示最新状态
优势
| 特性 | 传统方案 | 本方案 |
|---|---|---|
| 实例需要开放端口 | 是 | 否 |
| Server 需要知道实例 IP | 是 | 否 |
| 防火墙配置 | 复杂 | 无需配置 |
| 实例动态上下线 | 需要注册/注销 | 自动处理 |
| 网络穿透问题 | 可能存在 | 无 |
小结
从 Nextflow 迁移到 WDL,是一个"苦尽甘来"的过程:
- 语法清晰:WDL 的声明式风格让流程逻辑一目了然
- 开发效率高:所见即所得,调试方便
- 运行时轻量:miniwdl 够用且足够轻
- 可控性强:没有现成的 Web 接口,那就自己造一个
Server + Agent 的架构设计,本质上是一个控制反转:让计算实例主动汇报,而不是被动等待查询。这在云环境下尤其实用——实例可以随时创建、随时销毁,Server 无需关心实例细节,只需要接收汇报即可。
接下来的工作就是把这个架构实现出来,让 miniwdl 也能拥有企业级的流程监控能力。