第3章:参数配置文件(nextflow.config)
这一章我们聊聊nextflow.config。说实话,我刚开始接触Nextflow时,觉得配个文件而已,随便写两行就行。结果第一个正式项目就跑崩了——原因特别低级:缓存路径没配对,重跑时一直用旧缓存。你说冤不冤?
后来养成了习惯,每次新项目都先把配置文件搭好框架。这一章我就把这套框架拆开讲清楚,你顺着走一遍就能避开我当时踩的坑。
3.1 参数配置的层级结构
nextflow.config 里的参数不是平铺的,而是有明确的层级。我用一张图先让你看明白整体结构。
从上往下看,最顶层是全局 params.*,你可以把它当成整个管道的“公共变量区”。往下一层是执行器(executor)和工作目录(workDir),这些决定了任务跑在哪、缓存存哪。再往下走就是进程级别的独有配置,比如 process.scratchDir 和 process.cache。还有一个隐藏的“作用域配置”,通过 withLabel 或 withName 做精细化调整。
我自己项目里最常用的是这个组合:全局参数放输入输出路径,进程配置给内存和 CPU,缓存相关参数单独用 withLabel 去控制。不乱套,也容易复用到其他项目。
3.2 核心参数详解
这一节我挑三个最容易搞出事情的参数来聊。记住一句话:配置弄对了,缓存复用才可靠。否则你调 -resume 等于没调。
3.2.1 executor + workDir
executor 决定了任务调度方式。本地跑用 local,集群用 slurm / pbs / awsbatch。workDir 就是缓存地狱的根源——假设你改了代码但 workDir 没变,Nextflow 会认为任务已经完成,直接跳过。我曾经犯过一个低级错误:本地和服务器各跑了一遍,workDir 都用的默认 ./work,结果一合并就出乱。
// 建议显式指定唯一路径
workDir = '/vast/scratch/project_xyz/work'
// 或者用环境变量
workDir = System.getenv('WORK_DIR') ?: './work'
3.2.2 process.cache
这个参数控制单个进程的缓存策略。默认是 deep,也就是对输入文件做完整哈希比较。如果你只改了参数没改文件,又想强制重跑某个进程,可以临时设成 false。不过一般不建议长期关缓存,否则 pipelines 会越来越慢。
process {
withName: 'bwa_mem' {
cache = false // 测试阶段用
}
}
我个人习惯:正式生产环境所有进程都保持默认 deep,只有调试某个进程时才局部关掉。跑完马上改回来。
3.2.3 resume 的依赖条件
很多人以为 -resume 是“万能后悔药”,其实不是。它依赖两个前提:1) 进程定义未变;2) 输入文件的哈希值未变。你改了个命令行参数但没改进程脚本,resume 就失效。这时候你得看日志里 Cached: skipped 的数量来判断缓存生效没。
# 启动时带上 -resume,并观察输出
nextflow run main.nf -resume -ansi-log false
3.3 配置文件组织与复用技巧
一个成熟项目通常会有多个 config 文件:基础配置、集群配置、调试配置。我推荐你用 -c 参数叠加配置文件,而不是在一个文件里写死所有东西。
# 基础参数配置(所有环境共通)
base.config:
params.input = 'data/raw'
params.outdir = 'results'
workDir = '/scratch/work'
# 集群特定配置(比如用 slurm)
cluster.config:
executor = 'slurm'
queue = 'normal'
cpus = 16
memory = '64 GB'
# 调试专用配置(局部关缓存)
debug.config:
process {
withLabel: 'slow_step' { cache = false }
}
运行时按需组合:
nextflow run main.nf -c base.config -c cluster.config
# 调试时再加一个 debug
nextflow run main.nf -c base.config -c debug.config -resume
base.config 加到你的项目模板仓库里,新项目直接 fork 改几条参数就行。省时省力。
3.4 实战案例:一个包含缓存复用的完整配置文件
下面是我自己项目里用的模板,你可以直接拿去改。重点看 cache 和 resume 相关的配置项。
// nextflow.config
params {
input = 'samples.csv'
outdir = 'results'
genome = 'hg38'
}
workDir = '/scratch/myproject/work'
executor {
name = 'slurm'
queue = 'compute'
cpus = 8
memory = '32 GB'
time = '48h'
}
process {
cache = 'deep'
scratch = true // 每次任务使用临时目录,避免 workDir 膨胀
// 针对特定标签做调整
withLabel: 'no_cache' {
cache = false
}
}
// 特殊参数:控制 resume 的详细行为
resume {
historySize = 50 // 保留最近 50 次运行缓存记录
cacheStrategy = 'copymove' // 避免大量硬链接失败
}
docker {
enabled = true
runOptions = '-u $(id -u):$(id -g)'
}
// 避免在 workDir 里生成大量 .command.* 文件
report {
enabled = true
trace.enabled = true
}
timeline {
enabled = false
}
这个配置跑在生产上已经大半年没出过缓存相关的幺蛾子了。核心就两点:workDir 唯一 和 cache 分进程管理。嗯,记住这个就够用。
3.5 常见问题与避坑
- 缓存明明是新的,但 resume 不认? —— 检查 workDir 是否被多个项目共用。我遇到过最离谱的一次,队友把两个项目 workDir 设成同一个,缓存互相污染。
- 配置文件报错:Unrecognized property —— 看版本。Nextflow 22 以后有些参数名改了,比如
process.cache = 'lenient'改成了cache = 'lenient'直接写在顶层。记得先nextflow info看版本。 - 为什么我关掉了缓存,任务还是没重跑? —— 检查
cache是否设置正确的作用域。有时候你在process块里写了cache = false,但withLabel又覆盖了,没生效。建议用-dump-hashes参数打出哈希对比表定位问题。
一句话总结: 配置文件是你管道的“宪法”,参数覆盖规则不搞清楚,缓存复用就是空谈。我建议你新项目必做的第一件事,就是敲定 nextflow.config 的骨架,而不是急着写脚本。