安装与部署:单机、Docker 与集群的实战选择
说到安装 Cromwell,很多人以为就是下载个 jar 包跑起来。其实没那么简单——部署方式直接影响后续的调度效率和资源管理。我个人习惯把 Cromwell 的部署分成三种:单机、Docker 和集群(HTCondor / SLURM)。每种背后都有不同的设计思路,选错了后面会很痛苦。
这一章我会把三种部署方式的要点、配置文件、常见坑都讲清楚。你跟着过一遍,基本上能应付 90% 的场景。
单机模式安装:最直接,但别小看它
单机模式说白了就是你把 Cromwell 当作一个 Java 进程来跑。你只需要 Java 8+ 环境,然后下载最新的 Cromwell jar。
# 安装 Java(以 Ubuntu 为例)
sudo apt update
sudo apt install openjdk-11-jre-headless
# 验证 Java 版本
java -version
# 下载 Cromwell(推荐指定版本)
wget https://github.com/broadinstitute/cromwell/releases/download/85/cromwell-85.jar
下载完后可以直接启动:
java -jar cromwell-85.jar server
嗯,就这样。默认会监听 localhost:8000。你访问 http://localhost:8000 就能看到 Swagger API 页面。
我的经验:如果你只是在笔记本上测试简单的 WDL 工作流,单机模式完全够用。我刚开始学 Cromwell 时就在 MacBook 上直接跑,一个晚上跑了十几个小任务。但要注意内存——Cromwell 默认 JVM 堆是 1G,处理大输入文件会 OOM。我习惯手动设大点:
java -Xms2g -Xmx4g -jar cromwell-85.jar server
单机的限制也很明显:任务只能串行或有限并发,没法跨节点调度。你想想看,如果一次要跑上千个样本,单机肯定卡死。
Docker 部署:一劳永逸的环境管理
我个人非常推荐把 Cromwell 跑在 Docker 里,特别是当你需要和其他团队共享环境时。Cromwell 官方提供了 Docker 镜像,但你也可以自己封装配置。
拉取官方镜像:
docker pull broadinstitute/cromwell:85
运行容器:
docker run -d -p 8000:8000 \
--name cromwell-server \
-v /data/cromwell:/cromwell \
broadinstitute/cromwell:85
挂载的 /data/cromwell 目录会存放 Cromwell 的数据库文件、工作目录和日志。你可以把配置文件也挂进去:
docker run -d -p 8000:8000 \
-v /data/cromwell:/cromwell \
-v /home/user/cromwell.conf:/cromwell/cromwell.conf \
broadinstitute/cromwell:85 server
配置文件 cromwell.conf 的内容稍后我会讲。这里先给个简单的 Docker 专属配置片段:
# cromwell.conf (Docker 版)
backend {
default = Local
providers {
Local {
actor-factory = "cromwell.backend.impl.sfs.config.ConfigBackendLifecycleActorFactory"
config {
root = "/cromwell/work"
filesystems {
local {
localization = ["copy", "hard-link", "soft-link"]
}
}
}
}
}
}
database {
profile = "slick.jdbc.HsqldbProfile$"
db {
driver = "org.hsqldb.jdbcDriver"
url = "jdbc:hsqldb:file:/cromwell/db/cromwell;shutdown=false;hsqldb.default_table_type=cached"
}
}
我曾经踩过一个坑:Docker 容器内的 Cromwell 默认使用 HSQLDB 作为数据库,但如果你误删了容器,数据库文件就全丢了。我建议在 production 环境至少配置一个外部的 MySQL 或 PostgreSQL,或者把 /cromwell/db 挂载到持久化卷上。
Docker 部署的好处是环境完全隔离,Cromwell 版本、Java 版本、依赖库都不会干扰宿主机。你在 CI/CD 流水线里用它特别爽。
集群部署(HTCondor / SLURM):真正的大规模作战
如果你的工作流涉及数百个样本甚至更多,单机或 Docker 都不够。这时候需要把 Cromwell 作为集群的前端调度器——它只负责解析 WDL,实际的 task 交给 HTCondor 或 SLURM 去分发。
Cromwell 的集群后端本质上是 SFS (Shared File System) 模式:所有节点共享存储,Cromwell 生成作业脚本提交给调度器,调度器分配资源并报告状态。
HTCondor 后端配置
用 HTCondor 需要先装好 condor 客户端,然后在 Cromwell 的配置里声明 backend:
backend {
default = HTCondor
providers {
HTCondor {
actor-factory = "cromwell.backend.impl.sfs.config.ConfigBackendLifecycleActorFactory"
config {
root = "/shared/workdir"
runtime-attributes = """
Int request_cpus = 1
Float request_memory = 1.0
String submit_command = "condor_submit"
String check_status_command = "condor_q"
"""
script {
submit = """#!/bin/bash
universe = vanilla
executable = /usr/bin/bash
arguments = "{{script}}"
request_cpus = {{request_cpus}}
request_memory = {{request_memory}}GB
queue
"""
}
filesystems {
local {
localization = ["copy", "hard-link", "soft-link"]
}
}
}
}
}
}
配置里最关键的是 submit 模板,它决定了 Cromwell 怎么生成 condor submit 文件。我建议在集群中提前准备好共享目录,比如 /shared/workdir,所有节点都能读写。
SLURM 后端配置
SLURM 的配置思路类似,只是提交命令和模板不同:
backend {
default = SLURM
providers {
SLURM {
actor-factory = "cromwell.backend.impl.sfs.config.ConfigBackendLifecycleActorFactory"
config {
root = "/shared/workdir"
runtime-attributes = """
Int cpu = 1
Float memory_gb = 1.0
String submit_command = "sbatch"
String check_status_command = "squeue"
"""
script {
submit = """#!/bin/bash
#SBATCH --job-name=cromwell_job
#SBATCH --cpus-per-task={{cpu}}
#SBATCH --mem={{memory_gb}}G
#SBATCH --output={{cwd}}/slurm_%j.out
#SBATCH --error={{cwd}}/slurm_%j.err
srun {{script}}
"""
}
filesystems {
local {
localization = ["copy", "hard-link", "soft-link"]
}
}
}
}
}
}
注意 SLURM 的 sbatchsqueue 轮询状态。如果你的集群有队列限制,可以在 runtime-attributes 里添加 partition 等参数。
对比总结:三种部署的选型建议
| 场景 | 推荐方式 | 理由 |
|---|---|---|
| 本地测试、小数据(<10 样本) | 单机 | 零配置,开箱即用 |
| 团队协作、CI/CD、需要环境隔离 | Docker | 环境一致,易于版本管理 |
| 大规模生产(>100 样本)、HPC 集群 | HTCondor/SLURM | 利用集群资源,可扩展 |
集群部署的通用注意事项
不管用 HTCondor 还是 SLURM,有几个共性问题我遇到过多次:
- 共享文件系统必须 NFS 或 Lustre。Cromwell 的 job 脚本和输入输出都依赖共享路径,NFS 是最简单的选择。我曾经见过有人用 S3 作为共享层,结果网络延迟导致任务失败率飙升。
- 注意文件系统锁。Cromwell 在更新 job 状态时会写锁定文件,如果 NFS 不支持分布式锁,可能会产生冲突。我习惯把 Cromwell 的数据库放在和作业存储分离的路径下,用异步数据库(如 MySQL)来避免锁问题。
- 提交命令的路径要在所有节点上一致。比如
sbatch一般在/usr/bin/sbatch,但不同节点的环境可能不同。我建议在 Cromwell 配置里写绝对路径。
一个小技巧:在集群部署模式下,你可以在 runtime-attributes 里为不同的 task 设置不同的 request_cpus 和 request_memory。这样 Cromwell 生成的 submit 文件会灵活适配,避免小任务占用大资源、大任务被限制。