一、Cromwell简介:我们先聊聊它是什么
说白了,Cromwell 就是一个工作流管理系统。你可能会问:工作流管理系统多了去了,为什么偏要用它?
我个人习惯把 Cromwell 比作一个“流水线调度员”。你写好一份工作流脚本(通常是 WDL),告诉它“先做 A,再做 B,如果 A 失败了就跑 C”。剩下的活儿——比如在哪台机器上跑、怎么分配资源、怎么传数据——全都交给 Cromwell 去操心。
我在项目中遇到过不少类似的工具,有些太重,有些又太死板。Cromwell 不一样的地方在于——它对底层计算环境没有偏见。本地跑可以,上云也可以,混合着来照样行。嗯,这一点在混合云迁移的场景里特别关键。
核心概念:你得先搞懂这几个东西
刚接触 Cromwell 时,我建议你先抓住三个核心概念。其他的都是锦上添花。
- Workflow(工作流):整个任务的蓝图。它定义了一系列步骤和它们之间的依赖关系。
- Task(任务):工作流里的最小执行单元。一个 Task 做一件事,比如“比对基因序列”或“转换文件格式”。
- Call(调用):在工作流中真正去执行某个 Task 的动作。你可以多次调用同一个 Task,传入不同的参数。
打个比方吧:Workflow 是菜谱,Task 是具体的烹饪步骤(切菜、炒菜、装盘),Call 就是你真的拿起锅铲去做的那一下。
| 概念 | 类比 | 我的理解 |
|---|---|---|
| Workflow | 菜谱 | 定义了“做什么”和“按什么顺序做” |
| Task | 烹饪步骤 | 一个不可再拆分的原子操作 |
| Call | 实际操作 | 把步骤真正执行一遍,可能执行多次 |
WDL 介绍:工作流描述语言长什么样
WDL 是 Cromwell 的“母语”。你不用学太多,掌握几个基本语法就能上手。
来看一个最简单的例子。下面这个 WDL 脚本定义了一个工作流,它调用一个 Task 来打印 “Hello World”:
workflow my_workflow {
call my_task
}
task my_task {
command {
echo "Hello World"
}
output {
String out = read_string(stdout())
}
runtime {
docker: "ubuntu:latest"
}
}
你发现了吗?结构非常清晰:
workflow块定义一个工作流task块定义一个任务command是你真正要执行的 shell 命令output定义怎么从执行结果里提取数据runtime指定运行环境,比如 Docker 镜像
runtime 指定 Docker 镜像。这能保证任务在任何环境下的行为是一致的。云上云下跑出来的结果不会打架。
WDL 还支持变量、条件判断、循环、以及更复杂的输入输出定义。你想想看,如果只用 shell 脚本去管理这些,早乱成一锅粥了。
workflow simple_math {
File input_file
call add_one { input: num = read_int(input_file) }
call multiply_two { input: num = add_one.result }
output {
Int final_result = multiply_two.result
}
}
task add_one {
Int num
command {
echo $((num + 1))
}
output {
Int result = read_int(stdout())
}
}
task multiply_two {
Int num
command {
echo $((num * 2))
}
output {
Int result = read_int(stdout())
}
}
这段代码展示了一个典型的数据依赖链:先加一,再乘二。Cromwell 会自动解析这些依赖关系,保证 multiply_two 在 add_one 完成后才启动。你完全不用操心调度顺序。
这张图展示了我对 Cromwell 架构的理解。你从顶层塞进去一个 WDL 脚本,中间层是 Cromwell 引擎——它负责解析、调度、追踪状态。最下层是各种后端适配器。Cromwell 之所以适合混合云,就是因为它允许你在同一套工作流里,把不同任务分发给不同的后端。
举个例子,你可以让数据预处理跑在本地集群,而计算密集型的分析任务自动提交到云上的大规模计算集群。配置改一行就能切换,工作流本身不用动。
一句话总结:Cromwell = WDL(你描述想做什么) + 可插拔后端(你决定在哪儿做)。理解了这个组合,你就掌握了 Cromwell 的精髓。