安装与部署:单机、Docker 与集群的实战选择

说到安装 Cromwell,很多人以为就是下载个 jar 包跑起来。其实没那么简单——部署方式直接影响后续的调度效率和资源管理。我个人习惯把 Cromwell 的部署分成三种:单机、Docker 和集群(HTCondor / SLURM)。每种背后都有不同的设计思路,选错了后面会很痛苦。

这一章我会把三种部署方式的要点、配置文件、常见坑都讲清楚。你跟着过一遍,基本上能应付 90% 的场景。

Cromwell 部署方式结构图 Cromwell 的三种部署场景 单机部署 Java 直接运行 jar Docker 部署 容器化运行 集群部署 HTCondor / SLURM 单机:测试、小数据量 / Docker:可移植、环境隔离 / 集群:生产级、大作业 无需额外服务 资源有限 依赖 Docker engine 适合 CI/CD 需要集群调度器 适合超算/云

单机模式安装:最直接,但别小看它

单机模式说白了就是你把 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 文件会灵活适配,避免小任务占用大资源、大任务被限制。