第3章:单机部署Cromwell(Linux环境)

先搞清楚Cromwell到底是什么

Cromwell是一个开源的工作流执行引擎。说白了,你写好一个WDL或者CWL脚本,它帮你把任务挨个跑完。我最初接触它时,觉得这个东西也没多厉害——不就是个任务调度器吗?真正用上之后才发现,它支持的并行任务调度、后端插件机制、日志聚合这些能力,远比我想的复杂。

特性 说明
WDL/CWL支持 原生运行两种工作流定义语言
多后端扩展 Local、HPC、云平台都能对接
RESTful API 提供HTTP接口进行任务控制
Scala编写 运行需要JVM环境

环境准备——先把JDK和Docker安好

我个人习惯先在干净的Linux机器上部署。Cromwell跑在JVM上,所以JDK是第一件要装的东西。

1. 安装JDK 11以上版本

sudo apt update
sudo apt install openjdk-11-jdk -y
java -version

输出结果应该是这样的:

openjdk version "11.0.18" 2023-01-17
OpenJDK Runtime Environment (build 11.0.18+10-post-Ubuntu-0ubuntu122.04)
OpenJDK 64-Bit Server VM (build 11.0.18+10-post-Ubuntu-0ubuntu122.04, mixed mode, sharing)
提示:JDK版本别低于11,否则Cromwell启动时会报错。我遇到过有人用JDK8跑新版Cromwell,结果一堆连接池异常,排查了好久才发现。

2. 安装Docker引擎

虽然单机部署时Cromwell可以直接跑本地任务,但我建议还是装上Docker。为什么呢?因为在实际项目中,很多WDL任务依赖特定镜像环境。

sudo apt install docker.io -y
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER

记得重启会话让组权限生效。我见过有人忘记加用户组,结果Docker命令全要加sudo,Cromwell调用容器时直接权限拒绝,跑了半天的任务全废掉。

下载Cromwell——两种方式选一种

方式一:通过GitHub release下载jar包

这是最简单的方式。去GitHub的broadinstitute/cromwell仓库,找到对应版本的jar。我个人习惯用最新稳定版。

wget https://github.com/broadinstitute/cromwell/releases/download/85/cromwell-85.jar

方式二:从源码编译

如果你的环境有网络限制,或者需要定制特定版本,那就自己编译。嗯,这里要注意编译过程非常吃内存,至少给sbt分配4GB以上。

git clone https://github.com/broadinstitute/cromwell.git
cd cromwell
sbt "project cromwell" "set cromwell/assembly / cromwell-assembly-*.jar" assembly
警告:编译Cromwell需要sbt和大量内存。服务器内存不足时,sbt会直接OOM崩溃。我曾经在2GB的云服务器上试过,等了40分钟然后报错,换了8GB的机器才顺利编译完。

配置文件——cromwell.conf的写法

Cromwell启动时会读取配置文件。不提供配置文件的话,它会用默认值。但那样做在正式项目里是跑不通的。

下面是一个最基本的本地模式配置文件:

backend {
  default = "Local"
  providers {
    Local {
      actor-factory = "cromwell.backend.impl.sfs.config.ConfigBackendLifecycleActorFactory"
      config {
        root = "/data/cromwell-executions"
        filesystems {
          local {
            localization = [
              "hard-link", "soft-link", "copy"
            ]
          }
        }
      }
    }
  }
}

database {
  profile = "slick.jdbc.H2Profile$"
  db {
    driver = "org.h2.Driver"
    url = "jdbc:h2:file:/data/cromwell-db/cromwell;MODE=MSSQLServer;"
    user = "sa"
    password = ""
    connectionTimeout = 30000
  }
}

各参数作用是什么?我列个表:

参数 含义 建议值
backend.default 默认使用的后端 Local
root 任务执行目录 /data/cromwell-executions
database.db.url 数据库连接地址 H2文件路径或MySQL地址
localization 输入文件本地化方式 hard-link, soft-link, copy
重要:如果任务执行目录的磁盘空间不够,Cromwell不会主动提醒你。它是等到写文件时才会报磁盘已满。所以提前规划好root路径,挂载大容量磁盘。

启动Cromwell

配置写完,接下来就是启动了。

mkdir -p /data/cromwell-executions
mkdir -p /data/cromwell-db
java -jar cromwell-85.jar server --config cromwell.conf

启动日志会输出到控制台。看到类似下面这行,说明启动成功:

2025-03-20 10:15:32,231 cromwell-system-akka.actor.default-dispatcher-3 INFO  - 
Cromwell Service started on port 8000...

默认端口是8000。你可以通过浏览器访问:

http://your-server-ip:8000

会返回一个简单的JSON响应,表示Cromwell正在运行。

提示:如果不想让进程在前台跑,用nohup或者screen。我习惯用screen,因为它能方便地查看日志。
screen -S cromwell
java -jar cromwell-85.jar server --config cromwell.conf
# 按下 Ctrl+A, D 脱离会话
screen -r cromwell  # 重新连接

提交一个简单的WDL任务验证安装

装好之后必须跑个任务看看。这里用最简单的hello world WDL:

先创建一个hello.wdl文件:

task hello {
  command {
    echo "Hello, Cromwell!"
  }
  output {
    String greeting = read_string(stdout())
  }
  runtime {
    docker: "ubuntu:latest"
  }
}

workflow hello_workflow {
  call hello
}

再创建输入JSON文件:

{}

然后通过RESTful API提交:

curl -X POST \
  --header "Accept: application/json" \
  -F "workflowSource=@hello.wdl" \
  -F "workflowInputs=@inputs.json" \
  http://localhost:8000/api/workflows/v1

提交后会返回一个workflow ID,表示任务已经被接收。接着用ID查询状态:

curl http://localhost:8000/api/workflows/v1/{workflow-id}/status

正常应该返回:

{"status": "Succeeded", "id": "xxxx-xxxx-xxxx-xxxx"}
记住:首次运行Cromwell会初始化数据库,所以会有几秒的延迟。如果提交任务后查询状态一直是Running,先等两分钟再看。

避坑指南——我踩过的那些坑

  • 文件权限问题:我曾经在NFS挂载的目录下运行Cromwell,结果Docker容器无法写入任务目录。原因是容器内用户ID与普通用户不同。解决方案是root目录用本地磁盘,或者设置合适的所有权。
  • 端口冲突:Cromwell默认端口8000。如果你的机器上已经运行了其他服务,改端口就行:用java -jar cromwell-85.jar server --config cromwell.conf -Dwebservice.port=8080
  • Docker拉取超时:Ubuntu镜像在国内有时候拉取超时。我的做法是提前在本地把镜像拉好:docker pull ubuntu:latest
  • 内存设置不足:Cromwell本身启动大概需要2GB内存。如果机器内存太小,建议增加JVM堆内存:java -Xmx4g -jar cromwell-85.jar server --config cromwell.conf

知识体系概览——这张图帮你理清流程

下面这张SVG图展示了单机部署Cromwell后,一个WDL任务的完整执行流程。

Cromwell单机部署核心执行流程 WDL工作流脚本 Cromwell Server 任务调度与并行执行 Local Backend Docker容器运行 执行目录存储 说明:WDL脚本 → Cromwell Server → 任务调度 → Local Backend → Docker容器 + 执行目录

单机部署的局限性——别等到生产环境才后悔

单机部署Cromwell跑小规模任务完全够用。但如果你要跑几百上千个并行任务,单机模式会暴露几个问题:

  • 资源瓶颈:单机CPU和内存有限,并行任务多了就排队
  • 磁盘压力:所有任务输入输出都挤在同一台机器的磁盘
  • 单点故障:Cromwell进程挂了,所有正在运行的任务跟着完蛋
  • 扩展困难:没法动态增加计算节点

不过这些问题是后续章节要解决的事。现阶段,单机部署是学习和调试的最佳起点。你想想看,如果连单机都跑不通,去搞混合云部署不是更痛苦吗?

建议:刚开始学Cromwell时,拿一台4核8GB内存的虚拟机练手就行。配置太高反而会掩盖一些资源管理的问题。