4. Nextflow项目骨架搭建
从 Snakemake 迁移到 Nextflow,第一步不是急着把规则逐条翻译成 process,而是先把工程骨架立起来。Snakefile 时代,一个文件往往就能承载全部逻辑:规则、输入输出、shell 命令、参数、资源声明都挤在一起。Nextflow 的工程组织方式不同,它鼓励把配置、流程编排、可复用模块和子工作流分层放置。骨架搭得合理,后续迁移会顺很多;骨架搭得随意,后面每加一个 process 都要回头改路径、改参数、改引用。
本节要完成的目标很明确:创建 nextflow.config、main.nf、modules 目录和 subworkflows 目录,并规划出迁移后的工程结构。下面先给出一个可直接落地的目录布局,再逐层说明每个文件或目录承担什么职责、里面应该写什么、以及如何验证骨架可用。
迁移后的工程结构总览
一个适合从 Snakemake 迁移过来的 Nextflow 项目,推荐采用如下结构。它既保留了 Nextflow 官方推荐的模块化习惯,也照顾了 Snakemake 用户对“规则文件”和“配置文件”的直觉。
my_pipeline/
├── main.nf
├── nextflow.config
├── modules/
│ ├── fastqc.nf
│ ├── trim_galore.nf
│ └── alignment/
│ └── bwa_mem.nf
├── subworkflows/
│ └── qc_trim_align.nf
├── conf/
│ ├── base.config
│ ├── test.config
│ └── local.config
├── bin/
│ └── summarize_stats.py
├── data/
│ └── samplesheet.csv
└── results/
这个结构里,main.nf 是入口,nextflow.config 是全局配置,modules/ 放最小可复用单元,subworkflows/ 放多个模块组合成的阶段性子流程,conf/ 放环境相关配置,bin/ 放自定义脚本,data/ 放样本表,results/ 是默认输出目录。下面这张图展示了各部分的依赖与调用关系。
nextflow.config:把散落在 Snakefile 里的参数收拢
Snakemake 中,线程、内存、容器、输出目录等常写在规则里,或者通过 config.yaml 传入。Nextflow 把这些统一收进 nextflow.config。骨架阶段不需要写全,但至少要把参数入口、执行器、默认资源和输出目录定下来。
// nextflow.config
params {
samplesheet = "data/samplesheet.csv"
outdir = "results"
fastqc = true
trimmer = "trim_galore"
}
process {
executor = 'local'
cpus = 2
memory = '4 GB'
time = '2h'
errorStrategy = 'retry'
maxRetries = 2
}
docker {
enabled = true
}
// 按环境加载额外配置
includeConfig 'conf/base.config'
这里有几个迁移时容易踩的点。第一,params 中的默认值只是兜底,实际运行时可以用 --samplesheet 覆盖,这与 Snakemake 的 --config 思路一致。第二,process 块中的资源是全局默认值,单个 process 可以覆盖,这相当于 Snakemake 中规则级 threads 和 resources 的默认继承。第三,includeConfig 让环境差异从主配置中分离,测试环境、本地环境和集群环境各用各的,避免把集群队列名写死在主配置里。
main.nf:只做编排,不写具体命令
main.nf 是流程入口,它的职责是定义 workflow、读取输入、调用子流程或模块,而不是把 shell 命令堆在里面。迁移初期,可以先把 Snakemake 的 rule all 对应成 Nextflow 的顶层 workflow,再把每个 rule 逐步下沉到 modules。
// main.nf
nextflow.enable.dsl = 2
include { QC_TRIM_ALIGN } from './subworkflows/qc_trim_align.nf'
workflow {
samplesheet = file(params.samplesheet)
QC_TRIM_ALIGN(samplesheet)
}
这段代码虽然短,但已经体现了 Nextflow 的核心组织方式:入口文件保持干净,真正的处理逻辑通过 include 引入。后续如果增加变异检测或报告生成,只需要在 main.nf 中再 include 一个子流程,而不必改动已有模块。
modules 与 subworkflows:从规则到可复用单元
modules/ 存放最小可复用单元,通常一个文件对应一个 process。以 fastqc.nf 为例,它应该只负责一件事:接收 reads,运行 FastQC,输出报告。
// modules/fastqc.nf
process FASTQC {
tag "$sample_id"
publishDir "${params.outdir}/fastqc", mode: 'copy'
input:
tuple val(sample_id), path(reads)
output:
path("${sample_id}_fastqc.html")
path("${sample_id}_fastqc.zip")
script:
"""
fastqc -t ${task.cpus} ${reads}
"""
}
subworkflows/ 则把多个模块串成一个阶段。例如 qc_trim_align.nf 可以依次调用 FASTQC、TRIM_GALORE 和 BWA_MEM,把“质控—修剪—比对”这条链封装起来。这样 main.nf 只需要关心“我要跑哪个阶段”,而不需要知道阶段内部有几个 process、谁先谁后。
// subworkflows/qc_trim_align.nf
include { FASTQC } from '../modules/fastqc.nf'
include { TRIM_GALORE } from '../modules/trim_galore.nf'
include { BWA_MEM } from '../modules/alignment/bwa_mem.nf'
workflow QC_TRIM_ALIGN {
take:
samplesheet
main:
reads_ch = Channel.fromPath(samplesheet)
.splitCsv(header: true)
.map { row -> tuple(row.sample_id, file(row.fastq_1), file(row.fastq_2)) }
FASTQC(reads_ch)
TRIM_GALORE(reads_ch)
BWA_MEM(TRIM_GALORE.out.trimmed_reads)
}
注意这里的 take 和 main 是子流程的输入声明与主体。迁移时,建议先把 Snakemake 中耦合较紧的规则按“阶段”分组,再决定哪些进 modules、哪些进 subworkflows。判断标准很简单:如果一个 process 只做一件事、可以被别的流程复用,就放 modules;如果它是一组固定搭配的阶段,就放 subworkflows。
骨架验证:先跑通空流程,再填内容
骨架搭好后,不要急着把所有规则搬进来。先用一个最小样本表跑通空流程,确认路径、配置和 include 都没有问题。样本表可以只有两列:sample_id 和 fastq_1。运行命令如下:
nextflow run main.nf -profile test --samplesheet data/samplesheet.csv
如果此时还没有写任何 process,可以把 main.nf 中的子流程调用暂时注释掉,只保留 workflow { println "skeleton ok" },确认 Nextflow 能识别配置和入口。然后再逐步取消注释,每加一个模块就运行一次 -stub-run,用桩运行检查通道连接和输入输出声明是否正确。这样做的价值在于:把“工程结构问题”和“业务逻辑问题”分开排查,避免后面出错时不知道是路径写错还是命令写错。
include 路径和输出目录互相干扰。可以先把旧项目放在 legacy/ 下,迁移完成后再删除。工程结构规划中的几个决策点
第一,模块粒度。Snakemake 中一个 rule 可能同时做比对和排序,迁移到 Nextflow 时建议拆成两个 process,因为 Nextflow 的通道机制更适合“一个 process 一个明确输出”。第二,子流程边界。按生物学阶段划分通常比按工具划分更稳定,例如“质控与修剪”“比对与去重”“变异检测与注释”,这样即使更换工具,子流程接口也不用大改。第三,配置分层。nextflow.config 只放默认值和参数入口,环境差异放 conf/,通过 -profile 切换。第四,输出目录。统一用 params.outdir 控制,各 process 的 publishDir 只负责子目录,避免硬编码绝对路径。
完成本节后,你应该得到一个可以运行、可以扩展、职责清晰的 Nextflow 工程骨架。它不要求一次把所有规则迁移完,但要求每个目录和文件都有明确归属:配置进 config,编排进 main,最小单元进 modules,阶段组合进 subworkflows。后续迁移时,每翻译一个 Snakemake 规则,就把它放进对应的 module,再在 subworkflow 中接线,工程结构不会随着迁移进度而失控。