Autodock Vina分子对接:Slurm三节点并行批处理实战 简介围绕 Autodock Vina 批量分子对接与 Slurm 集群调度整理的实操文档面向具备 Linux 基础、正在开展药物虚拟筛选或生物计算实验的高校学生与科研人员。内容以 Ubuntu 18.04 环境为线索先说明 Open Babel、Autodock Vina 与 MGLTools 的依赖安装再给出 prepare_receptor4.py、prepare_ligand4.py 预处理受体配体的做法以及通过 conf.txt 控制 CPU 数量与搜索空间、用 obabel 转换格式并把 pdbqt 结果汇总为 sdf 的流程同时记录了 vina.sh 中 PATH 环境变量的写法。随后延伸到 Slurm 的 munge 认证、slurm.conf 拓扑与资源分配、slurmctld 与 slurmd 服务启动最终落到在调度系统上循环提交小分子库对接任务。资源包仅含 1 个 PDF 文件约 1013KB按步骤编排便于对照服务器环境复现。已有 5264 人学习可作为搭建批量对接流水线与排查依赖缺失、环境变量、作业提交问题的参考。1. 跑完 500 个配体单机串行要十几个小时拆到三台机器上只要两小时——前提是先把几个基础环境问题解决清楚真正让人卡住的往往不是分子对接本身而是装完 MGLTools 后prepare_ligand4.py一跑就报错、sbatch提交完作业秒退、三台机器同时往同一个output.sdf里追加导致文件内容错乱。这份资料就是一个完整的实施记录在 Ubuntu 18.04 上装齐 Open Babel、Autodock Vina、MGLTools写一个vina.sh串行批处理再叠加 Slurm从单机sbatch一路扩到三节点分片并行分子对接。它适合手里有几台闲置服务器、正在做小分子虚拟筛选、又不想上重型平台的人照着敲能把「一条命令跑一批小分子」的流程完整复现出来。2. 环境准备Ubuntu 18.04 上把 Open Babel、Vina、MGLTools 装进同一条 PATH这一章解决的问题是「三个工具各自能跑凑在一起就不能跑」。分子对接的链路很长SDF 转 MOL2 靠 Open Babel受体和配体转 PDBQT 靠 MGLTools 里的两个 Python 脚本打分靠 Vina 二进制。三者版本之间没有严格的编译依赖但路径依赖非常强装错位置、PATH 少一层后面所有脚本都会以各种看似不相关的方式报错。2.1 依赖清单与目录约定先看系统依赖。这些包装了主要是为了 MGLTools 的 Python 扩展能编译通过、AutoDockTools 的 GUI 模块能被正常 import以及csh这类老脚本的运行时。sudo apt-get update sudo apt-get install -y build-essential libxt-dev libglu1-mesa-dev \ libxmu-dev libxi-dev libpng-dev libjpeg-dev libtiff-dev \ csh python-tk python-numpy python-dev python-pmw逐个说清用途省得你删错包包名用途build-essentialinstall.py编译扩展时需要 gcc/makelibxt-devlibxmu-devlibxi-devMGLTools 里 X11 相关模块的链接依赖libglu1-mesa-devlibpng-devlibjpeg-devlibtiff-dev分子可视化的图形与图像库cshAutoDockTools 部分包装脚本的 shebang 是 cshpython-tkpython-numpypython-devpython-pmwMGLTools 的 Python 2 运行时与 PMV 模块提示python-dev、python-numpy、python-tk这几个在 18.04 上指向 Python 2.7在 20.04 之后的发行版里已经改名成python2-dev、python2-numpy直接照抄会提示找不到包。这一点值得先确认否则后面install.py一定会失败。然后是目录约定。所有组件都放在同一个父目录下后面写 PATH 时才能一次写完/home/autodock/ ├── autodock_vina_1_1_2_linux_x86/ # Vina 二进制 ├── MGLTools-1.5.6/ # MGLTools 安装目录 │ ├── bin/ # pythonsh、pmv 等 │ └── MGLToolsPckgs/ │ └── AutoDockTools/Utilities24/ # prepare_receptor4.py 等 ├── pro.pdb # 受体结构 ├── testset.sdf # 配体集合 ├── conf.txt # Vina 配置 └── vina.sh # 批处理脚本sudo mkdir -p /home/autodock sudo chown -R $USER:$USER /home/autodock cd /home/autodock tar -xvf autodock_vina_1_1_2_linux_x86.tgz tar -xvf mgltools_x86_64Linux2_1.5.6.tar_.gz sudo apt-get install -y openbabelVina 是预编译的 tgz 包解压后bin/下直接就是可执行文件没有configure make这一步这也是它在教学和小规模筛选场景里流行起来的原因。2.2 MGLTools 的 Python 2 依赖与 install.pyMGLTools 的安装脚本是个老式交互程序问一句装到哪回车确认即可。它本身是用 Python 2 写的这一点决定了整个链路的版本基线。cd /home/autodock/MGLTools-1.5.6 python2 install.py # 提示 Install directory 时直接回车装到当前目录install.py干的事情不只是拷贝文件它还会把MGLToolsPckgs下的 Python 源码做一次字节编译并生成bin/pythonsh这个包装解释器。装完之后目录里应该能看到bin/和MGLToolsPckgs/两个子目录缺一个都说明安装过程没走完。注意如果系统里python已经指向 Python 3install.py会在语法层面直接崩掉。稳妥的做法是明确写python2 install.py不要图省事用python。装完后可以顺手验证一下 Open Babelobabel -V能打印出版本号就行。它的角色是全流程的格式转换工具SDF → MOL2、PDBQT → SDF 这两步都靠它不需要额外配置。2.3 PATH 拼装三个来源目录缺一不可这是整套方案里最容易漏的地方。Vina、MGLTools 主目录、Utilities24 是三个独立目录必须同时进 PATH少任何一个都会报command not found。export PATH/home/autodock/autodock_vina_1_1_2_linux_x86/bin:$PATH export PATH/home/autodock/mgltools_x86_64Linux2_1.5.6/bin:$PATH export PATH/home/autodock/mgltools_x86_64Linux2_1.5.6/MGLToolsPckgs/AutoDockTools/Utilities24:$PATH第一行提供vina这个可执行文件第二行提供pythonsh是 MGLTools 内部脚本指定使用的 Python 解释器第三行提供prepare_receptor4.py和prepare_ligand4.py这两个脚本本身不带可执行权限的话还得chmod x一下。验证方式是逐条which不要只看vinawhich vina prepare_receptor4.py prepare_ligand4.py三条路径都能打出来环境算搭完。这里有个血泪经验这三个 export 要写进作业脚本里不能只写进~/.bashrc。Slurm 的sbatch用的是非登录 shell~/.bashrc里在非交互判断之后的部分根本不会执行作业里的 PATH 是干净的。这个问题在第 5 章还会再提一次。3. 串行批处理拆解vina.sh一个 for 循环里藏着的四步单机版脚本只有二十来行但它把整条流水线的形状定死了受体处理一次、配体批量拆分、逐个对接、结果追加合并。后面上 Slurm 只是把这个循环拆开分给不同节点核心逻辑一行没变所以这一章值得逐段拆清楚。3.1 受体预处理为什么只做一次受体 PDBQT 只跟受体结构本身有关跟你跑多少个配体完全无关。所以它应该在循环外做也只做一次prepare_receptor4.py -r pro.pdb -e -o receptor.pdbqt-r指定输入受体结构-o指定输出的 PDBQT-e是让脚本遇到问题时直接打错误信息并退出不同版本这个开关的行为略有差异拿不准的时候敲prepare_receptor4.py -h看当前安装的说明比照抄别人的命令稳输出文件名固定叫receptor.pdbqt后面conf.txt会按这个名字引用。这点在并行场景下非常关键三台机器只需要共享同一份receptor.pdbqt不用各自再算一遍。所以它应该放在 NFS 挂载的共享目录里而不是每台机器本地各放一份——版本一旦不一致打分结果就没法横向比较了。3.2 多分子 SDF 拆分obabel -m的命名规则一个testset.sdf里通常摞着几百个分子Vina 一次只吃一个配体所以得先拆开# 先把多分子 SDF 转成多分子 MOL2保留键级与电荷信息 obabel -isd testset.sdf -omol2 -O testset.mol2 mkdir -p mols cd mols # -m 按分子逐个切分生成 vs1.mol2、vs2.mol2 ... obabel ../testset.mol2 -O vs.mol2 -m这里有个常见的参数误用-O大写是输出文件名-o小写是输出格式。写成obabel ../testset.mol2 -o vs.mol2 -m不会报错但结果会直接打到标准输出刷屏你以为文件生成了实际上ls出来是空的。加了-m之后文件名会带上序号切出来的数量应该和原 SDF 里的分子数对得上跑之前用ls *.mol2 | wc -l核一下能省掉后面排查半天。3.3 单个配体的对接与结果回写循环体是整套脚本的重心四步一循环cd mols for mol2 in ls *.mol2 do # 1) 配体转 PDBQT prepare_ligand4.py -l $mol2 -o ligand.pdbqt # 2) 对接日志和构象分别落盘 vina --config ../conf.txt --cpu 1 --out out.pdbqt --log log.txt # 3) PDBQT 转 SDF方便下游用化学工具读 obabel -ipdbqt out.pdbqt -osd -O temp.sdf # 4) 追加到总结果文件清掉中间产物 cat temp.sdf output.sdf rm -f log.txt ligand.pdbqt out.pdbqt temp.sdf done cd ..逐条说逻辑prepare_ligand4.py -l读单个 MOL2输出ligand.pdbqt。它会补氢、算 Gasteiger 电荷、合并非极性氢这几步直接决定打分是否合理别跳过。vina --cpu 1把线程数锁到 1。单机上这么写是浪费但在 Slurm 里每个任务只分到 1 个核写成多线程会和其他任务争抢 CPU反而整体变慢。这是个刻意的选择不是笔误。--out和--log每次都被同名覆盖。串行跑没问题因为上一轮的产物已经rm掉了一旦多节点同时跑这就是第一个出错的地方。cat temp.sdf output.sdf是唯一的汇总动作整个流程只有一个写入口串行时是安全的。log.txt里其实有每个构象的亲和力明细但脚本跑完就删了只留下 SDF。这意味着你事后想按 RMSD 筛选或者看打分分布数据已经没了——这是个设计上的取舍第 6 章会给一个保留日志的改法。3.4conf.txt里的盒子与穷举度conf.txt决定了搜索空间是影响结果质量最直接的一份配置receptor ../receptor.pdbqt ligand ligand.pdbqt center_x 15.19 center_y 53.90 center_z 16.91 size_x 22 size_y 22 size_z 22 exhaustiveness 8 num_modes 9 energy_range 3参数含义调整建议receptor/ligand输入文件路径相对于 vina 的工作目录在mols/里执行所以受体要写../receptor.pdbqtcenter_*搜索盒中心坐标取受体结合口袋的几何中心别照抄别人的值size_*搜索盒三边长度单位埃刚好包住口袋即可盲目放大到 40 以上会显著拖慢且降低精度exhaustiveness搜索穷举度默认 8精筛可上到 1632粗筛降到 4 换速度num_modes最多输出多少个构象一般 9 够用energy_range输出构象与最优构象的能量差上限默认 3想要更多备选构象就放宽参数表里的center_*是最值得花时间的一项。盒子对不准后面再调exhaustiveness都是白费——我见过把盒子中心按蛋白质质心算的跑完一堆构象全在溶剂区纯属浪费机时。4. Slurm 调度落地从 munge 认证到第一个sbatch作业单机跑通之后瓶颈就变成「一台机器一次只能跑一个配体」。Slurm 要做的就是把那个 for 循环拆成 N 份分给不同节点同时保证作业排队、失败重试、资源不打架。整套配置其实不复杂麻烦的是 Ubuntu 打包出来的路径和官方文档对不上。4.1 munge 与依赖包安装munge 是 Slurm 的认证基础节点之间靠它确认彼此身份。它必须在所有节点上装着同一份密钥否则sinfo会一直显示down。sudo apt-get install -y make hwloc libhwloc-dev libmunge-dev libmunge2 munge \ mariadb-server libmysqlclient-dev sudo systemctl enable --now munge sudo systemctl status mungemariadb-server和libmysqlclient-dev是为作业记账accounting准备的。资料里的配置把AccountingStorageType设成了accounting_storage/none也就是不记账这时候数据库其实用不上但装上不影响后面想开sacct统计机时就不用再补了。验证 munge 是否正常最快的方式是自己跟自己握一次手munge -n | unmunge输出里能看到STATUS: Success就说明密钥和解码链路都通了。多节点集群里如果这一步失败八成是/etc/munge/munge.key权限不对或者三台机器不一致。4.2slurm.conf里真正要改的不到十个字段官方 configurator 生成的配置文件有上百行但真正影响能不能跑起来的就那么几个ClusterNamevina-cluster # 控制器所在节点旧版本写作 ControlMachine/ControlAddr SlurmctldHostnode01(172.28.10.12) SlurmUserroot SlurmdUserroot AuthTypeauth/munge CryptoTypecrypto/munge StateSaveLocation/var/spool/slurmctld SlurmdSpoolDir/var/spool/slurmd SlurmctldPidFile/var/run/slurmctld.pid SlurmdPidFile/var/run/slurmd.pid SlurmctldPort6817 SlurmdPort6818 ProctrackTypeproctrack/pgid TaskPlugintask/none SwitchTypeswitch/none SelectTypeselect/linear SchedulerTypesched/backfill SlurmctldTimeout120 SlurmdTimeout300 ReturnToService1 SlurmctldDebug3 SlurmdDebug3 SlurmctldLogFile/var/log/slurm-llnl/slurmctld.log SlurmdLogFile/var/log/slurm-llnl/slurmd.log AccountingStorageTypeaccounting_storage/none JobCompTypejobcomp/none JobAcctGatherTypejobacct_gather/none NodeNamenode01 NodeAddr172.28.10.12 CPUs1 RealMemory1800 StateUNKNOWN NodeNamenode02 NodeAddr172.28.10.13 CPUs1 RealMemory1800 StateUNKNOWN NodeNamenode03 NodeAddr172.28.10.14 CPUs1 RealMemory1800 StateUNKNOWN PartitionNamep1 Nodesnode01 DefaultYES MaxTimeINFINITE StateUP PartitionNamep2 Nodesnode02 MaxTimeINFINITE StateUP PartitionNamep3 Nodesnode03 MaxTimeINFINITE StateUP几个字段值得单独拎出来讲SlurmctldHost老版本17.x 及更早写的是ControlMachine加ControlAddr照抄老配置到新版本会直接被拒绝启动报Invalid generic type。SlurmUserroot教学环境图省事生产环境建议建独立用户。以 root 跑的前提是/var/spool下相关目录归属正确。SelectTypeselect/linear按整节点分配资源小集群够用。改成select/cons_tres才能按单核、单 GPU 粒度分配那是后话。SchedulerTypesched/backfill允许小作业插空跑对一堆短时对接任务很友好。StateSaveLocation目录必须提前存在Slurm 不会帮你创建。这是新手最常见的一个问题。PartitionName的DefaultYES只能有一个分区带这个标记三个都写YES会让slurmctld直接起不来。节点名和地址建议同时写进/etc/hosts别只依赖 DNScat EOF | sudo tee -a /etc/hosts 172.28.10.12 node01 172.28.10.13 node02 172.28.10.14 node03 EOF4.3 目录、开机自启与hostname验证配置文件就位后补目录、起服务sudo mkdir -p /var/spool/slurmctld /var/spool/slurmd /var/log/slurm-llnl sudo chown -R root:root /var/spool/slurmctld /var/spool/slurmd sudo systemctl enable --now slurmctld sudo systemctl enable --now slurmd sinfosinfo里三个分区都显示idle这场环境战役就算打赢了。接下来用最小作业验证调度链路脚本越简单越好别一上来就提交对接任务#!/bin/bash #SBATCH -o job.%j.out #SBATCH -p p1 #SBATCH -J myFirstJob #SBATCH --nodes1 #SBATCH --ntasks-per-node1 hostname-o job.%j.out让输出文件名带上作业 ID避免多次提交互相覆盖-p p1指定分区不写会用默认分区hostname是关键它告诉你作业到底落在哪台机器上执行。sbatch job1.sh squeue cat job.*.out输出里的主机名和你sbatch所在机器不一致说明调度真的生效了。资料里那行--qoslow建议先删掉QOS 没在配置里定义的话提交会被直接拒绝。5. 排障记录批量对接在 Slurm 上最容易出问题的五个地方下面五条都是这套环境里真实会遇到的问题按「现象 → 原因 → 解决」写遇到类似的直接对号入座。5.1 作业提交成功RUNNING两秒后变FAILED现象squeue里看到作业闪一下就消失job.%j.out里只有一行prepare_ligand4.py: command not found或者vina: command not found。原因PATH 是在交互式 shell 里 export 的而sbatch启动的作业是非登录、非交互 shell~/.bashrc里放在case $- in *i*)判断之后的语句根本不会执行。你的终端能跑作业里跑不了就这么简单。解决把三条export PATH...原样搬进作业脚本开头别偷懒依赖环境。#!/bin/bash #SBATCH -p p1 #SBATCH -o logs/job.%j.out export PATH/home/autodock/autodock_vina_1_1_2_linux_x86/bin:$PATH export PATH/home/autodock/mgltools_x86_64Linux2_1.5.6/bin:$PATH export PATH/home/autodock/mgltools_x86_64Linux2_1.5.6/MGLToolsPckgs/AutoDockTools/Utilities24:$PATH5.2slurmctld起不来日志里说配置找不到现象systemctl start slurmctld失败journalctl -u slurmctld提示Couldnt find the slurm.conf file或Unable to read slurm.conf。原因Debian/Ubuntu 打包出来的默认路径是/etc/slurm-llnl/slurm.conf而很多教程和手工建的目录是/etc/slurm/slurm.conf。你把文件放对了地方但守护进程找的是另一个目录。解决确认实际搜索路径然后两边都放一份或者建软链sudo mkdir -p /etc/slurm-llnl sudo ln -sf /etc/slurm/slurm.conf /etc/slurm-llnl/slurm.conf sudo systemctl restart slurmctld5.3vina报Could not open receptor现象单机跑得好好的conf.txt搬到作业脚本里就报找不到受体文件或者报Parse error on line 2 in file ../conf.txt。原因conf.txt里的receptor是相对路径相对的是vina 进程的当前目录而不是配置文件所在目录。脚本里cd mols之后receptor.pdbqt就跑到上一层去了多节点场景下如果每台机器的目录布局不一致问题会更隐蔽。解决共享路径一律写绝对路径比如receptor /vina/receptor.pdbqtligand同理。绝对路径不好看但能省掉一整轮排查。5.4 三个分区同时跑output.sdf内容错乱现象三个节点的作业都显示COMPLETED但汇总的output.sdf分子数和预期对不上有的分子出现两次有的记录被截断成半截。原因三个进程同时用cat temp.sdf output.sdf往同一个 NFS 文件追加。追加写入在 NFS 上不是原子操作也没有锁几个 write 交错在一起就成了这种需要排查的异常状态。更麻烦的是 NFS 客户端有属性缓存ls -l看到的文件大小都可能滞后。解决改成「每人写自己的文件事后统一合并」。作业里输出到out/${LIG}.pdbqt全部跑完再一次性合并cat out/*.sdf output.sdf合并动作只在登录节点做一次没有并发不会相互覆盖。5.5prepare_ligand4.py报AttributeError或 import 失败现象脚本第一行就崩报ImportError: No module named numpy或者AttributeError: module object has no attribute ...。原因MGLTools 依赖 Python 2而系统里可能同时装了 Python 2 和 3 两套 numpy。prepare_ligand4.py的 shebang 指向pythonsh但pythonsh会去系统 site-packages 里找模块找错了版本就是这个结果。解决先确认解释器版本再显式指定head -1 $(which prepare_ligand4.py) pythonsh -c import numpy; print(numpy.__version__)如果确实是 numpy 冲突用python2 -m pip install numpy装一份到 Python 2 的环境里或者干脆用python2 prepare_ligand4.py -l x.mol2 -o x.pdbqt显式调用绕过 shebang。6. 二次开发把分片脚本改造成可复用的对接流水线原始脚本能跑通但要长期用有三个地方必须改输出文件名唯一化、日志保留、任务分发自动化。改完之后的作业脚本长这样#!/bin/bash #SBATCH -J vina-array #SBATCH -p p1 #SBATCH -o logs/job.%A_%a.out #SBATCH --nodes1 #SBATCH --ntasks1 #SBATCH --cpus-per-task1 #SBATCH --array1-500%6 export PATH/home/autodock/autodock_vina_1_1_2_linux_x86/bin:$PATH export PATH/home/autodock/mgltools_x86_64Linux2_1.5.6/bin:$PATH export PATH/home/autodock/mgltools_x86_64Linux2_1.5.6/MGLToolsPckgs/AutoDockTools/Utilities24:$PATH # 每个数组下标对应一行配体名 LIG$(sed -n ${SLURM_ARRAY_TASK_ID}p ligand.list) [ -z $LIG ] exit 0 WORK/vina/run/$LIG mkdir -p $WORK cd $WORK || exit 1 prepare_ligand4.py -l /vina/mols/$LIG.mol2 -o ligand.pdbqt vina --config /vina/conf.txt --cpu $SLURM_CPUS_PER_TASK \ --out out.pdbqt --log log.txt obabel -ipdbqt out.pdbqt -osd -O result.sdf 2/dev/null # 保留最优亲和力供后续汇总 grep -m1 REMARK VINA RESULT out.pdbqt best.txt--array1-500%6的%6是同时运行的任务数上限按集群实际核数调%A_%a让日志按「作业号_下标」命名几百个任务的日志不会互相覆盖--cpu $SLURM_CPUS_PER_TASK让 Vina 的线程数和 Slurm 分配到的核数自动对齐改资源申请时不用再改脚本。汇总环节从result.sdf里翻数据很痛苦直接从 PDBQT 的REMARK VINA RESULT读第一行更省事Vina 按亲和力降序输出第一个 MODEL 就是最优构象import glob, os, re pat re.compile(rREMARK VINA RESULT:\s(-?\d\.\d)) rows [] for p in glob.glob(/vina/run/*/best.txt): name os.path.basename(os.path.dirname(p)) with open(p) as fh: m pat.search(fh.read()) rows.append((name, float(m.group(1)) if m else None)) rows.sort(keylambda r: (r[1] is None, r[1])) with open(summary.csv, w) as fh: fh.write(ligand,best_affinity\n) for name, aff in rows: fh.write(f{name},{ if aff is None else aff}\n)验证方式很简单先拿 3 个配体跑一遍数组作业确认summary.csv行数对、亲和力量级合理多数小分子在 -5 到 -10 kcal/mol 之间再放大到全量。我踩过最不划算的一次是直接提交 800 个任务跑到一半才发现conf.txt的盒子中心填错了六小时机时全废。从那以后我每次上集群都强制先用 3 个配体走通全链路再放大规模这个习惯帮我省下的机时远比想象中多。希望帮到你。本文还有配套的精品资源点击获取