:完整项目——异构算力 MD 平台:适配层、调度器、基准流水线三位一体)
分子模拟异构算力适配开发教程20完整项目——异构算力 MD 平台适配层、调度器、基准流水线三位一体版本声明块工具/软件本系列全部组件适配层 18 篇、调度器 17 篇、流水线 12 篇、对账器 19 篇底座 K8sVolcano/HAMi14/15 篇或 SlurmApptainer16 篇语言/环境Python 3.10、Linux本文目标读完你拥有一个可落地的平台蓝图——分层架构、目录骨架、核心代码组装、可观测性设计与部署决策能把本系列任意单篇的产出接入对应层一句话结论异构 MD 平台 四层总装——底座层K8sVolcano/HAMi 或 SlurmApptainer管设备与作业组、适配层18 篇探测/协商/注入/执行/归一、调度层17 篇槽位回收/补位/aging 的 continuous batching 内核、运维层12 篇基准档案 19 篇对账巡检 Prometheus 指标——用户只见统一的 MDJobSpec 提交接口异构性引擎×硬件×精度×调度器全部被中间两层吸收。〇、本篇要解决的认知问题平台的完整分层是什么每层对应本系列哪几篇的产出三大组件适配层/调度器/流水线怎么组装——接口与数据流可观测性指标/档案/巡检怎么设计成平台的“体检系统”部署形态怎么选K8s 原生 / Slurm 旁挂 / 单机起步一、机制解析1.1 平台分层19 篇知识的归位图为什么这一节对你重要总装不是把 19 篇代码堆一起——每篇的产出有它的层放错层比如把引擎协商塞进 K8s 调度器、或把槽位管理下沉到 gmx 命令行会造成职责纠缠这是平台工程最常见的返工源。┌────────────────────────────────────────────────────────────┐ │ 用户接口层 mdctl submit / REST API / Web │ │ 统一作业描述 MDJobSpec18 篇 │ ├────────────────────────────────────────────────────────────┤ │ 调度层 MDScheduler17 篇槽位回收/补位/aging │ │ 平台队列策略优先级/配额的 app 层版本 │ ├────────────────────────────────────────────────────────────┤ │ 适配层 Adapter 族18 篇探测/协商/注入/执行/归一 │ │ GromacsAdapter / OpenMMAdapter / ... │ ├────────────────────────────────────────────────────────────┤ │ 底座层 K8s Volcano/HAMi14/15 篇 │ │ 或 Slurm GRES Apptainer16 篇 │ │ 设备分配、可见性注入、gang/配额 │ └────────────────────────────────────────────────────────────┘ 旁路系统贯穿四层 基准流水线12 篇 perf-archive.json→ 性能档案库 对账巡检19 篇 reconcile→ 数值健康 诊断体系19 篇分类学→ 故障定位各层职责的一句话边界底座管“有什么资源、怎么隔离”适配层管“这个作业具体怎么跑”调度层管“什么时候、在哪个槽位跑”用户层管“用户说什么语言”。旁路系统不属于任何层——它们消费所有层的输出。1.2 数据流一次作业的一生① 用户提交 MDJobSpec(kindtpr, precisionmixed, priority5) ② 调度层入队有效优先级 5 等待×aging——17 篇 ③ 槽位空闲 → 取出作业 → 交给适配层 ④ 适配层探测缓存的能力矩阵gmx 三要素 OpenMM 平台表——18 篇 → 协商形态/精度/偏好过滤→ 选定 GromacsAdapter ⑤ 环境注入build_envdevice → CUDA_VISIBLE_DEVICES——18 篇 → 执行gmx mdrun 全家桶 -cpi 断点语义——3/16 篇 ⑥ 运行中底座的健康流device plugin Unhealthy——14 篇异常 → 调度层标记、回收、换槽位续跑17 篇 checkpoint ⑦ 完成JobResultns/day、环境快照、轨迹路径 → 归一化入档perf-ledger.csv 追加——16 篇 ⑧ 夜间巡检抽体系跑三方对账19 篇→ 数值健康分两个设计决策值得点名能力矩阵带缓存探测有成本——gmx --version 是进程调用、OpenMM 枚举要 import缓存 TTL 手动刷新按钮健康流是旁挂的调度器不主动轮询设备订阅底座的事件——掉卡秒级感知的 K8s 版是 ListAndWatchSlurm 版是作业自检段的退出码。1.3 可观测性平台的体检系统三个维度、三套数据、一个消费口维度数据源存储消费性能md.log 六指标12 篇流水线perf-archive.json / ledger.csv趋势图、瓶颈定位19 篇、铁律 9 实测依据数值对账巡检19 篇巡检报告版本升级的回归门禁运行作业状态、槽位利用率、队列深度Prometheus 指标告警、容量规划Prometheus 指标命名平台侧约定Prom 命名惯例namespace_subsystem_namemdplatform_jobs_total{engine,backend,precision,state} # 计数作业按维度 mdplatform_slots{device_type} # gauge槽位在用/空闲 mdplatform_job_ns_per_day{engine,backend,system} # histogram性能分布 mdplatform_reconcile_pass_ratio{pair} # gauge对账通过率 mdplatform_queue_wait_seconds{priority_class} # histogram排队延迟SLA设计要点指标标签维度对齐能力矩阵engine/backend/precision——性能问题定位时能下钻到“是不是 MUSA 后端的作业普遍慢”对账通过率进指标——数值健康从“某天有人发现”变成“仪表盘红绿灯”。1.4 部署形态三种起步形态 A单机旁挂最快起步——适配层 调度器跑在一台 GPU 服务器cron 或 systemd无 K8s/Slurm 依赖。适合课题组级几人到十几人、几张卡。本篇骨架代码就是这个形态的直接可用版。形态 BSlurm 旁挂HPC 存量——平台调度器翻译作业为 sbatch 脚本16 篇模板GRES/可见性交给 Slurm平台只管队列策略与适配层。适合算力中心已有 Slurm 且不想动底座。形态 CK8s 原生平台化目标——调度层对接 Volcano Queue/PodGroup15 篇、HAMi 管切分14 篇适配层以 Job/DaemonSet 形态部署。多租户、弹性、混载推理MD的目标形态。演进路径 A→B/C 平滑因为适配层与调度内核在三种形态里零改动它们不感知底座底座差异被 build_env 与“提交翻译器”吸收——这是 18 篇分层设计的回报。二、完整代码与逐行剖析平台骨架把 17/18/19 篇组件组装成可运行的最小平台——形态 A 直接可用#!/usr/bin/env python3mdplatform.py —— 异构 MD 平台最小可用版形态 A单机。 组装MDScheduler17 Adapter 族与协商18 档案与对账12/19。 运行python mdplatform.py submit jobs.json # 提交一批作业 python mdplatform.py status # 查看队列/槽位 python mdplatform.py reconcile a b # 对账两个作业的能量 from__future__importannotationsimportjsonimportsysimporttimefrompathlibimportPath# ── 组件复用本系列产出非重写──────────────────────────────────# 17 篇调度内核槽位回收/补位/aging# from md_scheduler import MDScheduler, MDJob as SchedJob, State# 18 篇适配层探测/协商/注入/执行# from adapter_layer import (MDJobSpec, probe_gromacs, probe_openmm,# negotiate, GromacsAdapter, OpenMMAdapter)# 19 篇对账器# from reconcile import load_energies, reconcile_L1, Tolerance# —— 教学版内联演示组装关系生产用上面 import 连接真实模块———classPlatform:平台的组装点三个组件 一个档案口。def__init__(self,slots:int2,archive:strplatform-ledger.csv):frommd_schedulerimportMDScheduler self.schedulerMDScheduler(slotsslots,modedemo)# 能力矩阵带缓存1.2 节决策探测有成本self._caps_cache:list[dict]|NoneNoneself._caps_ts:float0.0self.archivePath(archive)defcapabilities(self,ttl:float300.0)-list[dict]:能力矩阵TTL 缓存——5 分钟内复用探测结果。ifself._caps_cacheisNoneortime.time()-self._caps_tsttl:fromadapter_layerimportprobe_gromacs,probe_openmm self._caps_cache[probe_gromacs(),probe_openmm()]self._caps_tstime.time()returnself._caps_cachedefsubmit_batch(self,specs:list[dict])-dict:批量提交MDJobSpec 字典 → 协商验证 → 调度器队列。 注意顺序先协商提交时就说不而不是运行时撞墙——18 篇问题 4。fromadapter_layerimportMDJobSpec,negotiatefrommd_schedulerimportMDJob accepted[]forsinspecs:specMDJobSpec(**s)chosennegotiate(spec,self.capabilities())# 提交期协商accepted.append(MDJob(job_idf{spec.kind}-{int(time.time()*1000)%100000},tprspec.tpror-,nstepsspec.nstepsor10,priorityspec.backend_prefinteractiveand5or0))self.scheduler.jobsaccepted statsself.scheduler.run()# 档案口12/16 篇结果追加进 ledger——平台的记忆withself.archive.open(a)asf:f.write(f{time.strftime(%FT%T)},{json.dumps(stats)}\n)returnstatsdefstatus(self)-dict:队列/槽位快照1.3 节指标的来源。return{capabilities:self.capabilities(),jobs:[{id:j.job_id,state:j.state.value}forjinself.scheduler.jobs]}defmain()-None:cmdsys.argv[1]iflen(sys.argv)1elsestatusplatPlatform(slots2)ifcmdsubmit:specsjson.loads(Path(sys.argv[2]).read_text(encodingutf-8))print(json.dumps(plat.submit_batch(specs),ensure_asciiFalse,indent2))elifcmdstatus:print(json.dumps(plat.status(),ensure_asciiFalse,indent2))elifcmdreconcile:fromreconcileimportload_energies,reconcile_L1,Tolerance rreconcile_L1(load_energies(Path(sys.argv[2])),load_energies(Path(sys.argv[3])),Tolerance())print(json.dumps(r,ensure_asciiFalse,indent2))else:print(__doc__)if__name____main__:main()配套的作业描述文件用户视角的全部复杂度——这就是“用户只说什么语言”的答案// jobs.json —— 用户提交的全部语言一个 JSON 数组[{kind:tpr,tpr:/data/benchMEM.tpr,nsteps:5000,precision:mixed,backend_pref:gromacs,device:0},{kind:tpr,tpr:/data/ligand-prod.tpr,nsteps:500000,precision:mixed,backend_pref:null,platform_hint:null,device:1}]逐段剖析组装的本质是“连接件”Platform 类没有重写任何引擎逻辑——它做的是三件事能力缓存性能决策、提交期协商体验决策错误早爆、档案口记忆决策。平台代码的价值在连接件不在重复造轮子——19 篇的组件各就各位。submit_batch的协商前置第 18 篇问题 4 的落实作业不合法如 tprdouble 但 gmx 是 mixed 构建在提交秒级被拒——带原因的拒绝比运行三小时后的崩溃好一百倍。status()的输出结构直接映射 1.3 节指标capabilities 是标签维度来源、jobs 的 state 分布是队列深度——接 Prometheus exporter 就是把这两个 dict 变成 gauge/counter 的事。jobs.json 里backend_pref: null自动协商与显式gromacs并存——平台对“懂的用户”开放控制、对“不懂的用户”提供默认——两种用户都被同一层服务。部署清单形态 C 的 K8s 化要点对照 14/15 篇# 平台组件的 K8s 形态示意# md-scheduler → Deployment无状态对接 Volcano Queue 的 app 层策略# adapter-worker → Job/DaemonSet有状态执行走 HAMi vGPU 申请 gpumem# capability-probe → CronJob定期刷新能力矩阵——底座设备变化的感知# prometheus exporter → 侧车容器消费 status() 输出# 底座前置Volcano 部署 HAMi 部署 各厂商 device plugin#全部在第 14/15 篇有落地步骤平台的 K8s 化不改变适配层与调度内核——1.4 节承诺三、常见报错与排查问题 1现象——平台上线后用户反馈“还是直接 gmx 命令快平台有开销”。根因开销解剖——正常开销协商毫秒级、环境构造微秒级可忽略异常开销三个来源能力探测无缓存每次全量探测18 篇决策未落地调度器 poll 过密17 篇问题 3作业排队时间被计入“平台慢”其实是槽位不足的容量问题。解法三层分别处理——TTL 缓存本文 capabilitiespoll 调到秒级容量问题看 queue_wait_seconds 指标1.3 节说话加槽位或错峰。用指标分清“开销”与“排队”——多数抱怨其实是后者。问题 2现象——某引擎升级后平台作业全体数值告警对账巡检红了。根因这是巡检系统正常工作——引擎升级改变了数值行为精度路径/积分器实现变化平台用 19 篇的对账把它拦在了生产之前。解法按 19 篇层级定性差异合法变化 vs 真 bug——合法则在平台登记新基线巡检的黄金值随版本更新带版本标签真 bug 则冻结该引擎版本能力矩阵把它摘掉上游修复后再放行。巡检不是挡板的成本是灰度的依据。问题 3现象——双底座K8s Slurm 各管一部分节点时能力矩阵与可见性行为不一致。根因两底座注入语义差异Slurm 的 job step 环境 vs K8s 的 Allocate 注入漏过了适配层的统一收口——某处代码绕过 build_env 直接用了 os.environ。解法架构纪律检查——适配层外的任何代码不许构造子进程环境grep 检查 subprocess 调用点的 env 参数能力探测在双底座节点各自跑CronJob/旁挂探测按节点记录——矩阵的粒度是节点不是集群。问题 4现象——平台故障调度器崩了时在跑的作业全部丢失。根因把“调度器存活”与“作业存活”耦合了——其实 gmx/OpenMM 进程是底座systemd/K8s/Slurm的公民调度器崩不该带走它们丢失的是“管理”不是“计算”。解法调度器崩溃恢复流程——重启后扫描作业产物目录md.log/deffnm 文件在、按 checkpoint 状态重建作业清单-cpi 语义天然支持、继续调度本文 Platform 的 jobs 状态可持久化到 ledger已经在做——控制面与数据面分离这是 17 篇“调度内核与执行后端分离”的运维版。四、动手练习练习 1基础把 17/18 篇的模块文件md_scheduler.py/adapter_layer.py与本文 mdplatform.py 放同目录跑python mdplatform.py status看到带缓存的能力矩阵输出。判定成功标准status 输出双引擎能力或明确的 unavailable二次调用 status 在 TTL 内不再触发探测加个 print 在 probe 里验证缓存生效。练习 2进阶给 Platform 加bench命令——调第 12 篇流水线对当前槽位跑 benchMEM 短程基准把 ns/day 追加进 ledger 并打印与历史均值读 ledger 计算的对比。判定成功标准ledger 里出现新的性能记录对比输出含“当前/历史均值/偏差%”三要素偏差超过 30% 时给出 19 篇决策树的入口提示。练习 3思考题无标准答案平台上线一年后能力矩阵膨胀到几十个条目多引擎版本 × 多硬件 × 多精度协商变慢、维护变难——该怎么治理思考方向验证要点① 矩阵的生命周期管理废弃条目的淘汰机制——性能档案能否提供依据② 协商结果的缓存与失效策略③ “能力即代码”capability as config还是“能力即探测”runtime probing的长期取舍。五、系列总结20 篇收官。回到第 1 篇的三个问题现在的你能这样回答修改引擎GROMACS 四后端的构建2 篇与执行控制3 篇、gpu_utils 抽象层5 篇、HIP/SYCL/MUSA 三条移植路线的完整方法论6/7/9 篇——昇腾的特殊性11 篇与“没有后端”时的决策树。封装引擎OpenMM 平台抽象与插件协议4/8/10 篇、双引擎统一适配层18 篇——能力探测、协商、环境收口。调度封装从 Device Plugin/vGPU14 篇到 Volcano/vNPU15 篇到 Slurm/Apptainer16 篇的底座全景推理调度思想迁移的 MD 批调度器17 篇验证与诊断闭环12/19 篇最终总装成平台20 篇。贯穿全系列的十条铁律第 0 篇计划定义在每篇各自主战场兑现版本先锚定2/13 篇的环境变量三代史、单后端编译2 篇、工具边界6 篇 hipify、昇腾无后端的事实链11 篇、GPU-resident 前提3 篇、性能带上下文12 篇、双精度慎用2 篇、移植必须过验证12 篇、切分必须实测14/17 篇、封装不改物理18/19 篇。下一步去哪把第 9 篇的 SCS 容器跑起来用第 12 篇流水线给你的硬件建档第 18 篇的适配层接入你手头的引擎——平台从第一个真实作业开始生长。本篇认知问题回显FAQQ1异构 MD 平台的完整分层是什么A四层——用户接口层统一 MDJobSpec 提交、调度层continuous batching 内核槽位回收/补位/aging、适配层能力探测/后端协商/环境注入/执行/结果归一、底座层K8sVolcano/HAMi 或 SlurmGRESApptainer管设备分配与可见性旁路系统基准档案、对账巡检、诊断体系贯穿四层——每层职责一句话底座管资源隔离、适配层管作业怎么跑、调度层管何时何槽位、用户层管语言。Q2平台的三大组件怎么组装A连接件模式不重复造轮子Platform 类做三件事——能力矩阵 TTL 缓存探测有成本、提交期协商前置作业不合法秒级拒绝而非运行数小时后崩溃、档案口结果追加 ledger 形成平台记忆调度器崩不影响在跑作业控制面与数据面分离重启后按 checkpoint 产物重建清单续调。Q3MD 平台需要哪些可观测性指标A三维度对齐能力矩阵标签engine/backend/precision性能维度mdplatform_job_ns_per_day histogram、六指标来自 12 篇流水线、数值维度mdplatform_reconcile_pass_ratio 对账通过率——版本升级的回归门禁、运行维度jobs_total 计数、slots gauge、queue_wait_seconds 排队延迟 SLA——性能定位能下钻到“某后端是否普遍慢”数值健康从人工发现变成仪表盘红绿灯。Q4平台部署形态怎么选A三种起步——单机旁挂适配层调度器 systemd 跑一台 GPU 服务器课题组级最快可用、Slurm 旁挂翻译作业为 sbatchGRES 交给 SlurmHPC 存量友好、K8s 原生对接 Volcano Queue/HAMi vGPU多租户弹性目标形态演进平滑的关键是适配层与调度内核对底座零感知底座差异被环境注入与提交翻译器吸收。