3、本地单机部署:从源码/二进制包搭建Cromwell,配置MySQL + 本地文件系统

先聊个题外话。我最早接触Cromwell时,项目组直接扔给我一个JAR包,告诉我“跑起来就行”。结果MySQL没配好,任务日志全丢了,排查了一整天。后来我养成了一个习惯——不管是用源码编译还是下载二进制包,都先把底层存储和数据库捋清楚。

这一节,咱们就一起从零搭一套本地单机Cromwell。说白了就是:Cromwell进程 + MySQL + 本地文件系统。你可以在自己的笔记本、服务器或者云主机上复现,完全够你开发调试和跑小规模工作流。

从源码还是二进制?我的选择

先说结论:快速验证用二进制,定制功能用源码。我个人的经验是,生产环境我倾向用官方构建好的二进制包(稳定),而我在做二次开发或调试时,会从源码编译。

下表对比了两种方式的典型差异:

对比项二进制包(JAR)源码编译
获取方式下载一个 JAR 文件clone 仓库,用 sbt 或 mvn 编译
版本可控性只能选官方发布版可以锁定任意 commit,甚至修改源码
构建时间几十秒下载首次编译十多分钟
依赖管理无需安装sbt/Java SDK需要Java 11+、sbt/Maven
我遇到的坑官方包有时缺第三方驱动编译时内存不够,交换分区慢得要命

这一节我们两种方法都演示一遍,你选喜欢的就行。

准备环境

开始前,确认你的机器上有:

  • Java 11+(Cromwell 官方推荐 Java 11,我常用 OpenJDK 11)
  • MySQL 8.0(5.7 也行,但 8.0 更稳)
  • sbt(如果你要从源码编译)
  • Git(克隆仓库用)

检查命令很简单:

java -version
mysql --version
git --version
sbt --version     # 如果打算编译
提示:MySQL 安装后,记得先启动服务,创建一个数据库和用户。我习惯叫 cromwell 库,用户 cromwell / 密码别太简单。

方式一:从源码编译

克隆 Cromwell 仓库,我建议锁定一个稳定 tag,比如 85:

git clone https://github.com/broadinstitute/cromwell.git
cd cromwell
git checkout 85  # 或者你想要的版本

编译命令:

sbt assembly

这个过程它会下载依赖、编译、打包。我第一次编译等了快 20 分钟……嗯,你可以去泡杯茶。编译成功后会生成一个胖 JAR:cromwell-85-????????.jar。

注意:编译时默认使用所有核心,如果你的机器内存小于 8G,建议先 export SBT_OPTS="-Xmx2g -Xms1g",否则 OOM 了别怪我没提醒。

方式二:下载二进制 JAR

更省事的方法。去 GitHub Releases 页面 找到最新版,直接下载 cromwell-XX.jar。命令行:

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

搞定。我大多数时候用这种方式,除非需要修 bug。

配置 MySQL 数据库

Cromwell 默认用 H2 内存数据库,重启数据就没了。生产环境必须上 MySQL。先创建数据库和用户:

mysql -u root -p
CREATE DATABASE cromwell CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'cromwell'@'localhost' IDENTIFIED BY 'YourPassword';
GRANT ALL PRIVILEGES ON cromwell.* TO 'cromwell'@'localhost';
FLUSH PRIVILEGES;

然后写一个 Cromwell 配置文件,比如 cromwell.conf:

database {
  profile = "slick.jdbc.MySQLProfile$"
  db {
    driver = "com.mysql.cj.jdbc.Driver"
    url = "jdbc:mysql://localhost:3306/cromwell?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"
    user = "cromwell"
    password = "YourPassword"
    connectionTimeout = 5000
    numConnections = 5
  }
}

这里注意时区参数 serverTimezone=UTC。我之前没加这个,Cromwell 启动时报错说数据库时区异常,加上了就好了。

配置本地文件系统

Cromwell 需要一个工作根目录,存放中间文件和输出。我习惯创建一个专用文件夹:

mkdir -p /data/cromwell/work
export CROMWELL_ROOT=/data/cromwell

在配置文件中加上文件系统后端:

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

我建议把 root 设在一个有足够磁盘空间的位置,否则跑几个任务就满了。

启动 Cromwell

最终启动命令很简单:

java -jar cromwell-85.jar server --config cromwell.conf

看到类似下面日志就成功了:

INFO  o.eclipse.jetty.server.Server - Started @xxxxms
INFO  c.r.c.CromwellServer - Cromwell Server started on port 8000

打开浏览器访问 http://localhost:8000,能看到 Swagger UI。你也可以用 curl 测试:

curl http://localhost:8000/api/workflows/v1/query

返回空的 JSON 数组说明正常工作。

经验之谈:我第一次启动时忘了配 MySQL 驱动。Cromwell 默认没打包 MySQL 驱动,需要手动下载 mysql-connector-java-8.x.x.jar 放到 classpath 里。后来我干脆把驱动 jar 放到了 lib/ 目录下,启动时加 -cp "cromwell.jar:lib/*"。

整体架构图

下面这张图能帮你理解本地单机部署各组件的协作关系:

本地单机 Cromwell 部署架构 Cromwell Server (JAR 进程,端口 8000) MySQL 8.0 (工作流元数据存储) 本地文件系统 (工作根目录) cromwell.conf(数据库、后端配置) 数据流向说明 1. 用户通过 REST API 提交 WDL 工作流 → Cromwell 接收请求 2. Cromwell 将工作流元数据(状态、输入输出)写入 MySQL 3. 任务执行时,中间文件和结果写入本地文件系统根目录 4. 重启后 MySQL 恢复元数据,工作流不丢失

避坑指南

  • MySQL 连接超时:我踩过坑,默认连接池太少,若同时提交多个工作流会报错。建议把 numConnections 改成 10 以上。
  • 文件系统权限:Cromwell 进程用户要对工作根目录有读写执行权限。我之前用 root 启动,后来切普通用户忘了 chown,任务全失败。
  • Java 版本问题:Java 8 能跑但官方不再支持,一旦遇到奇怪异常,直接换 Java 11。
  • 时区导致的时间错乱:MySQL 和 Java 时区不一致,数据库里的时间字段对不上。统一用 UTC 最省心。
一个小技巧:写一个简单的 WDL 工作流(比如只跑一句 echo "hello")来验证部署。如果这能跑通,基本环境就没问题了。我从不用复杂工作流来测配置,浪费时间。

小结

本地单机部署的核心就是三件事:Cromwell 进程(源码或 JAR) + MySQL 元数据库 + 本地文件系统。你把这三者配好,一个开发调试环境就完成了。

我个人建议二进制包起步,等需要改源码时再编译。配置文件的重点就两个:数据库连接和文件系统目录。搞定这两个,Cromwell 就能跑起来。

另外记得,MySQL 驱动需要手动放进去。我把它列到环境变量里,这样后续升级时不用重复拷贝。