在 AI for Science 平台落地异构调度:从「数据搬运工」到「算力调度器」的架构重构实录
在 AI for Science 平台落地异构调度从「数据搬运工」到「算力调度器」的架构重构实录上周有个需求砸下来院里新建的 AI for Science 协作平台要上线对外开放支撑分子动力学MD长周期模拟与大模型预训练混部。测试环境跑通的流程一到生产环境就炸——不是 OOM 就是存储 IO 打满研究员提个任务排队 6 小时起步跑俩小时挂了还得从头来过。项目背景简单交代上海某研究院自建算力集群约 200 张 H800 500 张 A800存储端挂着 3PB CephFSCeph 18.2.2 Quincy和 200TB Alluxio 缓存层。技术栈锁定在 Kubernetes 1.29.4、Slurm 23.11.8、KubeRay 1.1.0、Spring Boot 3.3.2JDK 21.0.3、Fluid 0.9.0Alluxio Runtime 2.9.3。核心痛点不在模型算法而在异构负载下的数据亲和性调度与有状态长任务的生存保障。背景混部场景下的「三高」噩梦先说业务画像。分子动力学典型作业单任务跑 7×24 小时甚至数周MPI 进程间通信极其敏感中断一次等于白跑Checkpoint 文件动辄 2TB大模型预训练则是 Gang Scheduling 启动PyTorch DDP NCCL对带宽抖动零容忍但支持 Tensor Parallel 切分、支持频繁torch.save。这俩负载混在同一套 K8s 集群里还要套一层多租户配额研究组级别Slurm 管着裸金属 MPI 任务K8s 管着容器化训练任务中间隔着一层自研的统一提交网关。测试阶段没暴露问题因为测试数据集小、并发低、无抢占。真实上线第一周三个指标扎眼跨节点数据拉取占任务总耗时 68%Alluxio 缓存命中率仅 12%万兆网卡跑满却喂不饱 GPU。Slurm 抢占 K8s 任务导致 PyTorchJob 重启率 43%无法恢复研究员直接在群里骂娘。配额失效A 组提交 50 张卡的 RayJobB 组 8 张卡的 MD 任务排队饿死Kueue 的 ClusterQueue 配置在 Gang Scheduling 面前形同虚设。过程把「数据搬到算力」改成「算力贴着数据转」1. 调度层拆解别让 K8s 调度器干不擅长的事最早想用 K8s 原生kube-schedulerSchedulerPlugin解决数据亲和性写了个PreFilter插件去查 FluidDatasetCR 的status.nodes字段再给 Pod 打nodeAffinity。压测发现调度延迟 P99 从 200ms 飙到 3.4s因为每个 Pod 创建都要 List 所有 Dataset CR、计算拓扑距离、打分排序调度器主线程被卡死。换路子了。引入Scheduler ExtenderHTTP 扩展器做「粗筛」只在Filter阶段调用逻辑下沉到独立 Go 服务scheduler-extender-v0.3.1缓存 Dataset 节点分布在本地 RedisValkey 7.2.5单次请求 15ms 以内。K8s 原生插件只保留Score阶段做微调。java// Spring Boot 网关侧任务提交时自动注入数据亲和性标签不依赖用户手填 YAMLRestControllerRequestMapping(/api/v1/jobs)RequiredArgsConstructorpublic class JobSubmitController {private final FluidDatasetClient fluidClient; // Feign 调用 Fluid Controllerprivate final SchedulerExtenderClient extenderClient;private final KubernetesClient k8sClient;PostMappingpublic ResponseEntity submit(Valid RequestBody JobSubmitRequest req) {// 1. 解析数据集引用查询 Fluid 缓存分布List datasetNames req.getDatasetRefs();Map datasetNodeMap fluidClient.getCachedNodes(datasetNames);if (datasetNodeMap.isEmpty()) {throw new BusinessException(DATASET_NOT_CACHED, 目标数据集未预热至任何节点请先提交预热任务);}// 2. 计算最优节点集合交集优先并集兜底Set candidateNodes computeOptimalNodes(datasetNodeMap, req.getGpuCount());// 3. 构建 PodSpec注入 nodeAffinity tolerations专用节点池污点Pod jobPod buildPodSpec(req, candidateNodes);// 4. 根据负载画像分发MPI - Slurm OperatorDDP/Ray - Kueue K8s JobSetif (req.getWorkloadType() WorkloadType.MPI_MD) {return submitToSlurm(jobPod, req);}return submitToKueue(jobPod, req);}private Set computeOptimalNodes(Map datasetNodeMap, int gpuCount) {// 简单启发式求交集不足则按缓存完整度评分取 Top-Nreturn datasetNodeMap.values().stream().reduce((a, b) - a.stream().filter(b::contains).collect(Collectors.toList())).orElseGet(() - datasetNodeMap.values().stream().flatMap(List::stream).collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).entrySet().stream().sorted(Map.Entry.comparingByValue().reversed()).limit(gpuCount / 8 2) // 每节点 8 卡冗余 2 个节点.map(Map.Entry::getKey).collect(Collectors.toSet()));}} 这个方案虽然官方文档不推荐 Extender维护成本高但在我们「数据集固定、节点拓扑变化慢」的场景下比插件模式稳得多。插件模式适合通用调度器开发我们是业务侧深度定制别被「最佳实践」绑架。2. 抢占与生存CRIU 在 GPU 场景是个坑应用层 Checkpoint 才是正解Slurm 抢占 K8s 任务最初想用 CRIU (3.17.1) 做容器级热迁移/检查点。实测发现CRIU 根本 Dump 不下来显存状态cuda-checkpoint只能存 host 侧内存恢复时 NCCL Communicator 直接炸报ncclInternalError。折腾两周放弃。转而强制要求业务侧实现应用层 Checkpoint 接口平台侧只负责「发信号、存制品、拉起新实例」。yamlKueue JobSet 模板定义 PreStop Hook 共享 PVC 挂载 Checkpoint 目录apiVersion: jobset.x-k8s.io/v1alpha2kind: JobSetmetadata:name: ${{JOB_NAME}}spec:replicatedJobs:name: workerreplicas: ${{GPU_COUNT}}template:spec:serviceAccountName: jobset-sarestartPolicy: OnFailurevolumes:name: ckpt-volumepersistentVolumeClaim:claimName: ${{PVC_NAME}}containers:name: trainerimage: ${{IMAGE}}command: [bash, -c, torchrun --standalone --nnodes1 --nproc_per_node8 train.py --ckpt-dir /ckpt]volumeMounts:mountPath: /ckptname: ckpt-volumelifecycle:preStop:exec:command: [/bin/sh, -c, touch /ckpt/SIG_CHECKPOINT sleep 120] # 留 2 分钟刷盘env:name: PLATFORM_CHECKPOINT_SIGNALvalue: SIGUSR1 # 应用层监听信号触发 torch.save平台侧控制器job-lifecycle-controller v0.4.0监听PodDeletionTimestamp发现被抢占立即Patch PodterminationGracePeriodSeconds180防止默认 30s 截断。向容器发SIGUSR1业务代码捕获后执行torch.save({model: ..., optimizer: ..., rng: ...}, f/ckpt/ckpt_rank{dist.get_rank()}_step{step}.pt)。等待SIG_CHECKPOINT文件出现或超时再允许 Pod 彻底终止。重建 JobSet 时initContainer自动探测/ckpt最新 step传入--resume-step启动。效果MD 任务虽不支持热迁移但应用层每 30 分钟落盘一次PLUMEDRESTART关键字抢占恢复仅丢 ≤30 分钟算力研究员能接受。3. 多租户配额Kueue Slurm 统一视图的「伪全局调度」Kueue 管不了 SlurmSlurm 管不了 K8s。搞了个中间层quota-syncerGo 协程每 10s 跑一次从 Slurmsacctmgr show assoc formataccount,GrpTRESMins拿各账户已用 GPU 分钟数。从 KueueClusterQueue.status.flavors[].used拿 K8s 侧占用。汇总写入 Redisquota:{org_id}:used_gpu_minutes提交网关准入时只看这键。go// quota-syncer 核心同步逻辑片段func (s *Syncer) syncLoop() {ticker : time.NewTicker(10 * time.Second)for range ticker.C {slurmUsage, _ : s.slurmClient.GetAccountUsage() // map[org]float64 (gpu-min)kueueUsage, _ : s.kueueClient.GetClusterQueueUsage() // map[org]float64merged : make(map[string]float64)for k, v : range slurmUsage { merged[k] v }for k, v : range kueueUsage { merged[k] v }pipe : s.redis.Pipeline()for org, used : range merged {limit : s.getQuotaLimit(org) // 从 CM 读配额上限pipe.Set(ctx, fmt.Sprintf(quota:%s:used, org), used, 0)pipe.Set(ctx, fmt.Sprintf(quota:%s:limit, org), limit, 0)// 提交网关 Lua 脚本原子检查if used request limit then reject}_, _ pipe.Exec(ctx)}}提交网关侧 Lua 脚本保证原子性lua-- check_and_consume_quota.lualocal used_key KEYS[1]local limit_key KEYS[2]local request tonumber(ARGV[1])local used tonumber(redis.call(GET, used_key) or 0)local limit tonumber(redis.call(GET, limit_key) or 0)if used request limit thenreturn {0, used, limit} -- 拒绝endredis.call(INCRBY, used_key, request)return {1, used request, limit} -- 通过方案对比为何放弃「全 K8s」与「全 Slurm」| 维度 | 纯 K8s (Kueue JobSet MPI Operator) | 纯 Slurm (Pyxis Enroot) |混合模式当前落地|| :--- | :--- | :--- | :--- ||MPI 通信性能| 依赖 HostNetwork 手动亲和性NCCL 拓扑感知弱 | 原生srun --mpipmi2拓扑感知最优 |Slurm 独占 MPI 池零损耗||容器化生态兼容| 原生支持 Ray/PyTorchJob/KubeFlow | 需 Enroot 转镜像调试链路长 |K8s 侧原生Slurm 侧仅跑传统 MPI||抢占/回收粒度| Kueue Preemption 支持优先级但 Gang Scheduling 易死锁 | Slurm 抢占成熟但无感知容器状态 |分域抢占Slurm 抢 SlurmKueue 抢 K8s跨域仅配额联动||数据亲和性调度| Scheduler Plugin/Extender 灵活 |sbatch --constraint表达力弱 |Extender Fluid 缓存感知跨域数据预热走统一 DataLoad Job||运维复杂度| 单一栈但 MPI Operator 仍不稳定 (v0.7.0) | 成熟稳定但容器化体验差 |双栈运维成本 30%换取业务零改造上线| 别信「一套调度器走天下」。MPI 死磕 K8s 是给运维找罪受PyTorch 死磕 Slurm 是给研发找罪受。分域治理、配额联动、数据解耦才是混部的务实解。效果数字说话上线 3 周观测窗口2026-07-01 至 2026-07-21对比重构前基线| 指标 | 重构前 | 重构后 | 变化幅度 || :--- | :--- | :--- | :--- ||平均数据加载耗时首轮 Epoch/Step| 2h 14min |18min|↓ 86.5%||Alluxio 缓存命中率| 12% |91%|↑ 79pp||任务排队 P99 时长| 4h 12min |19min|↓ 92.4%||抢占导致的有效算力损失| 100%全量重跑 |≤ 30min/次|↓ 99%||集群 GPU 平均利用率| 45% |78%|↑ 33pp||跨租户配额争抢投诉| 日均 6 单 |0 单|清零|存储侧顺带优化了 Cephosd_op_threads8、bluestore_min_alloc_size64K针对 2MB 大块顺序读配合 Alluxioalluxio.user.file.readtype.defaultCACHE_PROMOTE单节点顺序读吞吐从 3.2 GB/s 稳在 9.8 GB/s接近 100Gbps 网卡物理上限。总结后端架构的「确定性」不在模型层在调度层这回重构的核心心得就三条数据亲和性调度必须下沉到 Extender/Operator 层别指望业务侧写nodeSelector也别指望通用调度器插件能扛住高并发打分。GPU 任务的 Checkpoint 是应用层契约不是基础设施魔法。CRIU 在显存面前失效强制约定SIGUSR1torch.save/ PLUMED RESTART平台只负责信号传递与制品版本管理。异构集群别强行合一Slurm 管 MPI、K8s 管 Cloud-Native Training配额层做最终一致性同步即可别试图搞统一调度器那是架构师的自嗨不是工程师的交付物。下一步打算把quota-syncer替换成基于 KueueAdmissionCheck的原生准入链路把 Slurm 用量作为外部指标喂给 Prometheus Adapter再砍掉一层 Redis 中间商。不过那是下个迭代的事了——当下集群跑稳了研究员不骂娘了这才是后端交付的硬指标。#后端 #Java #SpringBoot #Kubernetes #Slurm #AI4S #异构调度你在实际项目中有遇到类似问题吗欢迎在评论区分享你的经验和解决方案。

相关新闻

KMS_VL_ALL_AIO:Windows与Office智能激活技术的深度解析

KMS_VL_ALL_AIO:Windows与Office智能激活技术的深度解析

KMS_VL_ALL_AIO:Windows与Office智能激活技术的深度解析 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 在当今数字化办公环境中,Windows操作系统和Microsoft Office套件…

2026/9/12 4:11:13 阅读更多 →
Navicat重置工具:轻松实现Mac版无限试用的完整指南

Navicat重置工具:轻松实现Mac版无限试用的完整指南

Navicat重置工具:轻松实现Mac版无限试用的完整指南 【免费下载链接】navicat_reset_mac navicat mac版无限重置试用期脚本 Navicat Mac Version Unlimited Trial Reset Script 项目地址: https://gitcode.com/gh_mirrors/na/navicat_reset_mac 还在为Navicat…

2026/9/14 15:06:55 阅读更多 →
支付宝前端大神集体转型Agent开发,小白也能抓住下一个程序员黄金赛道!

支付宝前端大神集体转型Agent开发,小白也能抓住下一个程序员黄金赛道!

支付宝顶尖前端团队转型Agent开发,引发行业关注。文章分析了Agent开发的兴起原因,包括AI编程工具的普及、企业对能交付AI产品的人的需求增加以及Agent开发的商业价值。Agent开发全栈工程师需要前端、后端和AI能力,能够独立交付企业级Agent&am…

2026/9/18 8:31:58 阅读更多 →

最新新闻

改进蛇优化算法优化BP神经网络:从原理到回归预测实战

改进蛇优化算法优化BP神经网络:从原理到回归预测实战

1. 为什么想到用改进蛇优化算法去调BP神经网络先聊聊我最初碰到的问题。做数据回归预测的朋友应该都有这种感觉,BP神经网络这玩意儿,理论看着挺美,真上手跑起来就两件事最头疼:第一是初始权重和阈值太敏感,随机初始化一…

2026/9/20 2:31:53 阅读更多 →
小升初英语音标训练:听辨神经回路与语流音变实战

小升初英语音标训练:听辨神经回路与语流音变实战

简介:本资源是一份专为小学高年级学生设计的英语音标专项训练习题集,聚焦小升初阶段语音基础强化需求,系统解决音标识别不准、发音混淆、组合规则掌握薄弱等常见问题。文档共1个Word文件(.doc格式),大小仅1…

2026/9/20 2:31:53 阅读更多 →
SpringBoot+Vue宠物交易平台:从数据库设计到订单状态管理的全栈实战

SpringBoot+Vue宠物交易平台:从数据库设计到订单状态管理的全栈实战

基于SpringBootVue的宠物交易管理平台设计与实现前后端分离架构在校园毕设和中小型项目中几乎成了标配,SpringBoot负责后端接口,Vue负责前端页面,两者配合能快速搭建一个可直接演示、可扩展的完整系统。这篇文章就围绕“宠物交易管理平台”这…

2026/9/20 2:31:53 阅读更多 →
SpringBoot天气可视化分析系统毕设开发指南

SpringBoot天气可视化分析系统毕设开发指南

又是一年毕设季,后台私信里"天气可视化分析系统"的咨询量明显上来了。这个题目看着不起眼,但确实是个好选题——它把SpringBoot后端开发、定时任务调度、HTTP接口对接、MySQL表结构设计、ECharts数据可视化、Redis缓存这些大数据方向的核心技能…

2026/9/20 2:31:53 阅读更多 →
Claude Code CLI 2025:上下文感知的AI开发协作者

Claude Code CLI 2025:上下文感知的AI开发协作者

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/20 2:31:53 阅读更多 →
open-design 设计系统溯源证据解析:Figma 包的来源边界与 Token 契约机制

open-design 设计系统溯源证据解析:Figma 包的来源边界与 Token 契约机制

open-design 设计系统溯源证据解析:Figma 包的来源边界与 Token 契约机制 【免费下载链接】open-design 🎨 Best DeepSeek Harness Design Plugin. The open-source Claude Design alternative. 🖥️ Local-first desktop app. 🖼…

2026/9/20 2:30:53 阅读更多 →

日新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/19 23:01:36 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/19 17:50:38 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →