1. Cromwell 简介与背景
做生信流程开发的人应该都听过 Cromwell。它是什么?说白了就是一个工作流管理系统,专门用来跑 WDL(Workflow Description Language)写的流程。我最早接触这东西是在 2018 年,当时团队还在用 Shell 脚本串工具,每次换环境都要改一堆路径,非常痛苦。后来切换到 Cromwell,才体会到什么叫“一次定义,到处运行”。
Cromwell 的核心价值在于它把流程定义和执行环境彻底解耦了。你写好的 WDL 工作流,可以在本地笔记本跑,也可以直接扔到 AWS Batch、Google Cloud Life Sciences 或者 HPC 集群上,底层的计算资源完全不用改流程代码。嗯,这就是为什么这门课要专门讲本地与云端切换——你不能只会在本地调通,线上崩了就不会排查。
1.1 Cromwell 解决了什么问题?
在没有工作流引擎之前,生信流程通常是这样的:
- 每个步骤写成独立的 Shell 脚本
- 用 Makefile 或 Snakemake 串联依赖
- 日志散落在不同目录,失败时手动重跑
- 切换云平台得重写整个 IO 逻辑
Cromwell 帮你封装了这些东西:
- 声明式依赖 – 你在 WDL 里写
call stepB { input: file_from_stepA },引擎自动判断顺序 - 可插拔后端 – 后端只负责执行,流程本身不关心
- 持久化状态 – 就算机器重启,Cromwell 也能从断点继续跑
- 标准输出 – 每次运行的 metadata 都是 JSON 格式,便于追踪
java -jar cromwell.jar run 本地调通,再切到云端。这样能在开发阶段快速迭代,避免浪费云资源。
1.2 WDL 与 Cromwell 的关系
WDL 是语言,Cromwell 是引擎。你写出 WDL 文件,Cromwell 负责解析、调度、执行。它不是唯一能跑 WDL 的引擎(还有 miniwdl),但 Cromwell 是社区最成熟、对云支持最好的一个。
看一个最简单的 WDL 文件长什么样:
workflow hello {
call sayHello
}
task sayHello {
command {
echo "Hello, Cromwell!"
}
output {
String greeting = read_string(stdout())
}
}
用 Cromwell 跑它:
java -jar cromwell-85.jar run hello.wdl
输出里你会看到每个任务的 stdout、stderr 和返回值。我在生产环境里经常把 output 字段写成 File 类型,这样 Cromwell 会自动把结果上传到云存储,不需要我手动 copy。
1.3 多种后端支持
Cromwell 官方支持的后端,我用表格列一下常用的:
| 后端类型 | 典型配置 | 适用场景 |
|---|---|---|
| Local | 单机 JVM 线程池 | 开发、调试、小规模测试 |
| HTCondor | 集群调度器 | 高校/HPC 现有集群 |
| AWS Batch | Docker + S3 | 弹性伸缩,按需付费 |
| Google Cloud Life Sciences | GCS + 虚拟机组 | Google 生态下的生信 |
| SLURM | HPC 作业管理器 | 超算中心 |
注意一个关键点:本地后端和云后端对输入的路径处理方式完全不同。本地用绝对路径或相对路径,云端则要用 URI(如 gs://bucket/path/to/file)。这门课后面会专门讲路径映射的配置,这是个容易翻车的地方。
File 输入指定了 gs:// 路径,本地后端根本读不到。从那以后我都在 WDL 里用 String filename 存储原始路径,由配置文件决定怎么解析。
1.4 Cromwell 工作流执行架构
下面这张图展示了 Cromwell 的核心工作流程:
流程很清晰:你写好 WDL 丢给 Cromwell 引擎,引擎根据配置文件决定把任务发给本地还是云端。每个后端的行为差异,比如文件系统挂载、Docker 支持、资源请求上限,都由 Cromwell 的配置层吸收掉了。
1.5 为什么要学“本地与云端切换”
很多人学 Cromwell 只停留在本地跑 demo,觉得配置好复杂。等真要上生产,发现云端的路径映射搞不定,Docker 镜像拉不下来,任务日志找不着,这才回头翻文档。我的观点是:最好从一开始就按“可切换”的方式来组织你的 WDL 和配置文件。
举一个最简单的对比:
- 本地路径写法:
/home/user/data/sample.fastq - 云端路径写法:
s3://bucket/project/sample.fastq
看起来只是前缀不同,但 Cromwell 的 call_cache 和 workflow_failure_mode 在云上会有更复杂的交互。后面我会用专门的一章讲 Input 路径的抽象化原则。
1.6 检查你的 Cromwell 环境
在开始课程后续章节之前,我建议你先确认自己的 Cromwell 版本。执行以下命令:
java -jar cromwell.jar --version
如果输出类似 Cromwell 85 或更高版本,就说明环境正常。如果是 83 以前的版本,有些新特性(如 file_input 模式)可能不支持,建议升级。我干活时用的都是 85 以上,遇到 bug 也比较少。
另外,检查你的 cromwell.conf 是否引用了正确的后端。一个最简的本地配置是这样的:
backend {
default = "Local"
providers {
Local {
actor-factory = "cromwell.backend.impl.sfs.config.ConfigBackendLifecycleActorFactory"
config {
root = "/tmp/cromwell-executions"
filesystems {
local {
localization = [
"soft-link",
"hard-link",
"copy"
]
}
}
}
}
}
}
这个配置告诉 Cromwell:默认用 Local 后端,文件通过软链接本地化。等后面切换到云后端时,你只需要在这个文件里新增一个 AWS 或 GCP 的 provider 块,然后把 default 改过去就行。
好了,背景铺完了。第二章我们会动手搭建第一个 Cromwell 本地环境,然后跑一个真实(但不复杂)的 WDL 流程。到时候你会看到“解析-调度-执行”的完整链路,顺便踩几个我当年踩过的坑。