3. 规则执行机制:作业调度、资源分配、日志输出
Snakemake 的执行机制,说白了就是三件事:怎么把规则拆成作业、怎么给作业喂资源、怎么把作业干了啥记下来。这三件事搞不明白,DAG 画得再漂亮也跑不起来。
3.1 作业调度:从 DAG 到实际运行
Snakemake 不是一股脑把所有规则全跑上。它先解析 Snakefile,构建出 DAG,然后根据依赖关系一层层往下推。推到哪一层能跑?没有输入依赖的规则先跑,跑完了通知下一层。这就是所谓的“拓扑顺序”。
我个人习惯在跑大流程前先看一眼 DAG,用 snakemake --dag 生成图确认依赖没错。否则等跑起来了才发现箭头反了,那叫一个难受。
# 查看 DAG 但不执行
snakemake --dag | dot -Tpdf > pipeline.pdf
3.2 资源分配:别让你的集群打架
资源分配是 Snakemake 最难搞的部分,没有之一。你想想看,一个流程里可能有几十个规则,有的要 64GB 内存,有的只要 1 核。你要是不管,Snakemake 可能把 10 个内存大胃王同时丢到一台机器上——然后 OOM 给你看。
解决方法是给每个规则写清楚资源需求:
rule align:
input:
"data/{sample}.fastq"
output:
"results/{sample}.bam"
resources:
mem_mb=16000,
runtime_min=120
shell:
"bwa mem -t {threads} ..."
注意,资源里的 threads 不是 resources 的子项,它是单独的关键字。我当初刚上手时搞混过,死活拿不到多核。
Snakemake 支持多种资源后端:本地、SLURM、PBS、SGE。每个后端的资源参数略有不同,但核心概念一样——告诉调度器这活儿需要多少东西。
| 后端 | 配置方式 | 常用资源参数 |
|---|---|---|
| 本地 | --cores N | mem_mb, disk_mb |
| SLURM | --cluster "sbatch ..." 或配置文件 | mem_mb, runtime_min, partition |
| PBS | --cluster "qsub ..." | mem_mb, walltime |
| SGE | --cluster "qsub ..." | mem_mb, h_rt |
注意:资源参数的名字和单位在不同后端里可能不一样。比如 SLURM 里的 mem_mb 是 MiB,而 PBS 的 mem 默认是 KB。我踩过这个坑:写了个 16000 以为够了,结果给了 16KB……作业秒退。
3.3 日志输出:审计的命根子
日志不只是给运维看的,也是给自己看的。Snakemake 的日志分三种:
- 终端标准输出——你在控制台看到的实时信息。
- .snakemake 目录里的运行日志——每次运行 Snakemake 都会生成一个带时间戳的日志文件。
- 规则内自定义日志——你自己的命令把 stdout/stderr 重定向到文件。
我最常用的是第三种。每个规则都写日志,万一跑崩了,我能直接去看那一个规则的日志,省得在海量输出里翻。
rule align:
input:
"data/{sample}.fastq"
output:
"results/{sample}.bam"
log:
"logs/{sample}.align.log"
shell:
"bwa mem -t {threads} {input} 2> {log} | samtools sort > {output}"
这里 2> 把 stderr 重定向到日志文件。如果命令正常,日志就干干净净;如果报错,日志里会写清楚哪一步挂了。
技巧:我喜欢在每条规则里加 log: "logs/{rule}_{wildcards}.log",然后用 2>> {log} 追加模式。这样日志文件会把每次运行的输出都累积下来。方便回头比对。
3.4 作业并行与限流
Snakemake 的作业调度可以指定最大并行数。本地用 --cores,集群用 --jobs。但要注意:并行数不是越大越好。我见过有人把 500 个作业同时扔到集群上,结果调度器自己先崩了。
# 本地最大用 8 核
snakemake --cores 8
# 集群最多同时提交 20 个作业
snakemake --cluster "sbatch --mem=8G" --jobs 20
另外,Snakemake 还支持“组调度”。你可以把几个规则打包成一个组,组内串行、组间并行。这在有些工具需要独占资源时特别好用。
localrules:
- bwa_index
rule bwa_index:
output: "ref/genome.bwt"
group: "index_group"
shell: "bwa index ref/genome.fa"
rule align:
input: "ref/genome.bwt"
output: "results/{sample}.bam"
group: "alignment_group"
shell: "..."
3.5 日志的审计价值
曾经有一次,我明明跑完了流程,结果 reviewer 说“你怎么证明你跑了正确的版本?” 从那天起,我养成了跑完流程后把 .snakemake 目录打包备份的习惯。
审计内容至少包括:
- 运行的 Snakefile 版本(最好用 git commit)
- 每条规则的实际命令和 exit code
- 资源使用情况(内存、CPU)
- 开始/结束时间
- 输入输出文件的 checksum
Snakemake 的 .snakemake 目录里有个 log/ 子目录,里面存着每次运行的日志。日志文件名格式是 年-月-日T时:分:秒.log。打开看,里面有完整的调度信息和作业状态。
核心理解:作业调度决定“谁先跑”,资源分配决定“怎么跑”,日志输出决定“跑得怎么样”。三者缺一不可。
3.6 实践建议
最后整理几条我自己的习惯:
- 每条规则都写 log 字段。 哪怕你觉着不会死,等真死了你绝对会后悔没写。
- 用
--restart-times 3,遇到临时 IO 错误会自动重试,不用手动跑第二遍。 - 跑之前用
--dry-run看一眼要执行的命令。 我曾经因为通配符写错了,差点把整个参考基因组删了……幸亏先 dry-run 了。 - 存好 .snakemake 目录。 一来审计用,二来下次
--rerun-incomplete时还要读里面的状态。
特别注意: 日志文件如果没重定向,Snakemake 默认只捕获 exit code。拿不到命令内部的错误详情。所以一定要在 shell 命令里主动 2> 或 > 到 log 路径。
好了,作业调度、资源分配、日志输出这三块你都吃透了。后面我们再深入看看 DAG 审计中怎么利用这些日志做回放和排查。