ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

NVIDIA开源srt-slurm:SLURM集群上的GPU推理服务编排

NVIDIA开源srt-slurm:SLURM集群上的GPU推理服务编排 NVIDIA 开源 srt-slurm把 GPU 推理服务的编排做到 SLURM 里先说一个判断推理部署真正的瓶颈往往不是显卡性能不够而是 GPU 集群里“谁能用、怎么用、出了问题谁来管”这件事太乱了。单机跑大模型还好一旦上了多节点、多团队、多模型的集群环境手动分配 GPU、手动启动推理服务、手动盯着进程是否挂掉会迅速耗尽你的时间和耐心。NVIDIA 开源的 srt-slurm表面上是个调度工具本质上是在把“靠人盯集群”变成“靠编排管服务”。我写这篇文章是想把一个容易被忽略的视角说清楚很多团队已经有 SLURM 集群也装了 NVIDIA 驱动推理服务也能跑起来但距离“像 K8s 那样交付一个推理服务”还差一层编排能力。srt-slurm 正是补这层的东西。读完全文你会理解它解决什么真实问题、适合什么场景也能拿到一套基于 SLURM 和 NVIDIA 推理服务的可落地配置思路。1. 推理部署和“编排”之间差的那一步先回到一个典型场景你有一个 8 卡的 GPU 服务器上面装了 SLURM几个算法工程师都在往里提交训练任务。突然有一天业务方说要部署一个线上推理服务要求 7x24 小时可用还要支持多个模型切换。这时候你会发现SLURM 默认的工作模式是“作业”而不是“服务”。训练任务跑完就退出资源立刻释放推理服务要长期运行必须一直占着 GPU。如果用它跑一个srun --gresgpu:1 python run_infer.py一旦进程被 OOM 杀死、节点重启、或者 SLURM 回收了作业服务就没了没人知道你线上已经中断了一个小时。更麻烦的是服务发现和流量调度。一个分布式推理服务通常有多个副本前端需要一个入口把这些副本的地址聚合起来。SLURM 只负责把作业调度到节点上它不知道你的服务监听哪个端口、是不是还活着、要不要扩容。这些原本属于 Kubernetes 的能力在传统 HPC 集群里是缺失的。所以srt-slurm 的出现本质上是在 SLURM 的“资源调度层”和推理服务的“业务交付层”之间加了一个翻译器和调度大脑。它把“向 SLURM 提交作业”这件事抽象成“声明一个推理服务”然后由这个编排层去处理在哪个分区分配 GPU启动哪个推理引擎vLLM、TensorRT-LLM、NVIDIA NIM 等服务启动后把地址注册到哪里健康检查失败后自动重启作业结束后清理残留进程。这样的设计非常适合已经围绕 SLURM 建立了资源管理体系的团队。你不需要推倒重来切换到 Kubernetes而是在现有集群上获得接近云原生的服务化体验。2. 认识 srt-slurm它是什么解决什么问题从现有公开信息看srt-slurm 是一个围绕“SLURM 编排 推理服务部署”定位的开源项目。具体缩写的全称目前公开材料不多理解它的时候不必纠结字面意思直接看它做的事更有价值让 GPU 推理服务在 SLURM 集群上以声明式、可编排的方式运行。它的核心价值可以拆成三层。第一层是资源抽象。你把“我想跑一个 Qwen2.5-7B 的推理服务需要 1 张 A100”这样一句话写成配置而不是自己去记哪台机器有空闲、该提交什么 srun 命令。编排层会解析配置生成对应的 SLURM 作业提交到正确的分区。第二层是服务生命周期管理。推理服务和普通批处理任务的差异在于它要长期存活。因此 srt-slurm 需要支持健康检查、日志回灌、进程守护、失败重启。服务启动后它会探测推理服务的 HTTP 端口确认模型加载完成、API 可以响应才认为“部署成功”。第三层是入口与服务发现。当一个推理服务跑起来之后外部怎么找到它这里面通常需要一个注册机制把节点 IP 端口 服务名写入某个存储中例如共享文件、Redis 或 Nacos。前端网关再从注册中心拉取可用地址做转发。拿它和 Kubernetes 对比会更好理解。Kubernetes 天生把“部署、服务、伸缩、自愈”放在一起SLURM 擅长的是排队调度和 GPU 资源分配但对服务化能力支持较弱。srt-slurm 更像是给 SLURM 装上了一个“在线服务模式”让 HPC 管理员不需要改变底层作业调度习惯也能交付稳定的推理服务。当然并不是所有场景都适合它。如果你的团队对 Kubernetes 已经很熟且业务完全云原生化继续用 KServe 这类方案可能更完整如果你的集群规模很小、只有一块 GPU用手动启动脚本也够用。srt-slurm 最有价值的地方是那些“已经有 SLURM、有多卡、有多个用户、但缺少服务化工具”的中间地带。3. 核心基础SLURM 作业调度与 GPU 服务化的差异想用好 srt-slurm先要理解 SLURM 的基本模型以及推理服务为什么不能完全照搬训练作业的管理方式。SLURM 里有几个最常见的概念分区partition把节点按用途分组比如gpu分区、cpu分区节点node是实际执行作业的计算资源作业job是你提交的调度单元按状态分为排队PD、运行R、结束CD、失败F等。作业可以交互式运行也可以批处理运行。训练任务通常是一次性的我跑 1000 个 epoch跑完就结束SLURM 自然回收资源。推理服务却要求一直活着只要用户不主动下线它就要持续监听端口、接收请求。于是这里出现了几个关键差异。第一个差异是资源占用模式。训练任务用完即释放推理服务必须长期持有 GPU。在 SLURM 里长期作业一直占用节点会让排队时间变长所以需要给推理服务设立独立的 QoS 或分区避免它和训练任务互相挤占。第二个差异是启动判定标准。训练任务启动后就开始计算谈不上“服务 ready”。推理服务则不同模型权重加载就要几十秒到几分钟显存分配完成后 HTTP 端口才会监听。编排层必须有能力判断“这个服务是否真正可用”而不是只看进程有没有起来。第三个差异是故障恢复。训练任务挂了可以重新排队用户能等推理服务挂了线上的请求直接失败。所以 srt-slurm 这类编排层必须做自动重启、健康检查和异常通知甚至要考虑把服务调度到其他健康节点上。下面用一个表格把这两类作业的差异列清楚对比维度传统 SLURM 作业推理服务化作业生命周期短暂跑完即退长期驻留持续对外提供服务资源占用任务结束自动释放长期持有 GPU需要独立配额管理成功标准进程正常退出、产生产物HTTP 端口可访问、模型可响应重启策略失败后手动重新提交失败自动拉起或重新调度访问方式用户通过文件和日志获取结果通过 API 或网关访问服务监控需求任务日志、资源利用率端口健康、请求延迟、并发量、GPU 显存从这个表可以看出srt-slurm 要做的就是把第二列的能力补齐。它本身并不是取代 SLURM而是让 SLURM 从“批处理调度器”升级成“混合负载调度器”。4. 环境准备与前置条件srt-slurm 的具体部署方式以官方仓库 README 为准但整体环境依赖通常包含以下几个部分。这里我给出的是通用检查思路你只要按这套清单核对基本不会走偏。4.1 基础组件清单需要准备的核心组件如下SLURM 集群包括slurmctld控制节点和slurmd计算节点版本建议使用当前主流的大版本避免过老版本缺少关键特性。NVIDIA GPU 节点包含 NVIDIA 驱动、CUDA 运行环境以及 NVIDIA Container Toolkit。推理引擎vLLM、TensorRT-LLM、NVIDIA NIM 或其他兼容 OpenAI API 的推理服务。模型文件放在所有计算节点都能访问的位置一般是共享存储。服务发现组件可能是共享文件、Redis、etcd 等取决于 srt-slurm 的实现。4.2 检查 SLURM 集群状态在管理节点上执行sinfo squeue预期的输出应该能看到节点处于idle或mix状态并且有可用的 GPU partition。如果节点状态是down或drain需要先恢复节点。scontrol show nodes scontrol show partition这些命令可以帮助你确认节点 GPU 配置和分区名称。后面的编排配置里要写对 partition否则作业会一直排队。4.3 检查 GPU 与容器运行时在被调度的 GPU 节点上执行nvidia-smi nvidia-smi -L确认驱动已正确加载。如果要在容器里跑推理还需要安装 NVIDIA Container Toolkit。Ubuntu 系统下典型的安装步骤是curl -s -L https://nvidia.github.io/libnvidia-container/gpgkey | sudo apt-key add - curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list sudo apt-get update sudo apt-get install -y nvidia-container-toolkit sudo nvidia-ctk runtime configure --runtimedocker sudo systemctl restart docker如果你的环境无法访问外网需要把上面的软件源替换成内部镜像源。安装完成后用这条命令验证容器能否识别 GPUdocker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi如果能看到类似nvidia-smi的输出说明容器里的 GPU 透传没有问题。这一步非常关键因为 srt-slurm 在计算节点上很可能是通过容器拉起推理服务的。5. 从“手动启动推理服务”到“编排提交”我们先看一个最原始的推理服务启动方式再逐步过渡到编排模型这样你才能理解 srt-slurm 到底帮你省了什么。5.1 手动启动推理服务在 GPU 节点上手动执行python3 -m vllm.entrypoints.openai.api_server \ --model /models/Qwen2.5-7B-Instruct \ --tensor-parallel-size 1 \ --host 0.0.0.0 \ --port 8000这样服务确实能跑但你有几个问题没法解决如果机器重启服务不会自动拉起如果服务崩溃没人帮你重启端口被占用时只能手动改配置多副本时没有入口聚合前端只能写死 IP。5.2 用 SLURM 作业提交推理服务把上面的命令写进 SLURM 作业脚本可以在一定程度上解决“谁在跑”的问题#!/bin/bash #SBATCH --job-namemanual-infer #SBATCH --partitiongpu #SBATCH --gresgpu:1 #SBATCH --cpus-per-task8 #SBATCH --time01:00:00 #SBATCH --outputslurm-%j.out python3 -m vllm.entrypoints.openai.api_server \ --model /models/Qwen2.5-7B-Instruct \ --tensor-parallel-size 1 \ --host 0.0.0.0 \ --port 8000然后用sbatch提交sbatch start_infer.sh squeue这是“作业方式”的推理服务。它能保证服务在不出错的情况下长期运行但离完善的“编排”仍然很远。你仍然要手动查作业日志手动处理端口冲突还要在服务崩溃后重新提交作业。5.3 用 srt-slurm 声明推理服务到了 srt-slurm 这一层你不再直接写 SLURM 脚本而是写一份服务声明由工具转换成作业并持续维护。下面是一个演示性质的思路# 示例命令具体参数以官方 README 为准 srt-slurm submit \ --name qwen-infer \ --model /models/Qwen2.5-7B-Instruct \ --engine vllm \ --partition gpu \ --gpu 1 \ --port 18000 \ --health-path /health提交后编排层会做几件事自动计算合适的 SLURM 参数、向集群提交作业、轮询服务端口、确认/health返回 200然后把服务的访问地址注册到服务发现中心。整个过程你提供的只是“服务意图”而不是“机器命令”。6. 完整示例与代码实现下面给出一套完整的、可以在真实 SLURM 集群上落地的推理服务编排示例。这个示例不完全等同于 srt-slurm 的内部实现但它包含了所有关键环节你理解之后再看官方文档会非常快。6.1 模型目录准备假设模型已经放在共享存储/models/Qwen2.5-7B-Instruct。如果 srt-slurm 会派发作业到不同节点那么所有计算节点必须都能访问这个路径。路径挂载不一致是推理服务启动失败最常见的原因之一。6.2 编写服务声明文件假设编排工具支持 YAML 声明服务定义可能长这样# 文件路径services/qwen-infer.yaml name: qwen-infer engine: vllm model: /models/Qwen2.5-7B-Instruct partition: gpu gpu: 1 cpus: 8 timeout: 7200 port: 18000 replicas: 2 healthcheck: path: /health interval: 10 timeout: 5 retries: 3字段含义如下name服务名称在集群内唯一。engine推理框架例如vllm、tensorrt-llm或nim。model模型路径。partitionSLURM 分区。gpu每个副本申请的 GPU 卡数。replicas服务副本数用于高可用。port服务对外暴露的端口。healthcheck健康检查规则用于判断服务是否 ready。再次提醒具体字段名以官方实现为准这里的意义在于让你理解“声明式”长什么样。6.3 提交服务假设 CLI 工具名为srt-slurmsrt-slurm apply -f services/qwen-infer.yaml srt-slurm list srt-slurm status qwen-infer预期行为是list能看到服务处于running或deploying状态status能显示每个副本所在的 SLURM 作业 ID 和节点地址。6.4 在 SLURM 节点上实际运行的作业脚本如果不依赖编排工具直接用 SLURM 脚本模拟单副本推理服务脚本如下#!/bin/bash #SBATCH --job-nameqwen-infer-1 #SBATCH --partitiongpu #SBATCH --gresgpu:1 #SBATCH --cpus-per-task8 #SBATCH --time02:00:00 #SBATCH --output/var/log/slurm/qwen-infer-%j.out #SBATCH --error/var/log/slurm/qwen-infer-%j.err echo JOB_START_TIME$(date) nvidia-smi python3 -m vllm.entrypoints.openai.api_server \ --model /models/Qwen2.5-7B-Instruct \ --tensor-parallel-size 1 \ --host 0.0.0.0 \ --port 18000 \ --served-model-name qwen2.5-7b-instruct echo JOB_END_TIME$(date)提交sbatch scripts/start_infer.sh6.5 服务调用验证等到作业运行后先看服务是否监听端口squeue scontrol show job JOB_ID ss -lntp | grep 18000在能访问该节点的机器上执行curl http://NODE_IP:18000/v1/models如果返回模型列表说明推理服务已经就绪。再发一个对话请求curl http://NODE_IP:18000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: qwen2.5-7b-instruct, messages: [{role: user, content: 用一句话解释为什么推理服务需要编排}], max_tokens: 128 }正常响应中应该包含choices字段并且带出生成的文本。6.6 服务发现与网关接入在生产环境前端不可能写死节点 IP。更合理的做法是把每个副本的地址写入 Redisredis-cli HSET srt:services:qwen-infer node-0 10.0.1.20:18000 redis-cli HSET srt:services:qwen-infer node-1 10.0.1.21:18000然后 API 网关或者 Nginx 从 Redis 拉取地址列表做负载均衡。这一步可以很轻量不一定需要引入完整的注册中心。7. 运行结果与效果验证跑通这套流程后你应该按顺序验证几件事。7.1 作业状态sinfo看到节点被分配squeue看到作业从PD变成Rsqueue -u $USER状态为R表示作业正在运行PD表示还在排队。如果一直停在PD说明资源不足或你写的分区名称不对。7.2 服务端口在作业所在节点上确认端口监听ss -lntp | grep 18000 curl http://127.0.0.1:18000/health如果配置了/health探针应该返回 200。7.3 日志验证日志是排查问题的第一手信息tail -n 50 /var/log/slurm/qwen-infer-JOB_ID.err常见的日志内容包括模型加载过程、tokenizer 配置、显存分配以及最终的Application startup complete。看到这个信息说明服务已经就绪。7.4 失败后的第一步排查如果服务处于异常状态第一步先把“异常”定义清楚作业有没有启动看squeue容器或进程有没有崩溃看作业日志GPU 是否可见在节点上跑nvidia-smi端口是否被占用看ss -lntp模型路径是否可读在节点上执行ls /models/Qwen2.5-7B-Instruct。大多数问题都出在这五个环节里。如果一个一个排查完还找不到原因再去翻 srt-slurm 控制端的日志。8. 常见问题与排查思路问题现象可能原因排查方式解决方案作业一直停在 PD 状态分区无空闲 GPU或资源配额不足sinfo查看节点状态squeue查看排队情况等待资源释放调整分区降低申请卡数服务启动后 HTTP 接口无法访问服务监听地址不是0.0.0.0端口没有暴露ss -lntp查看监听地址curl 127.0.0.1 测试设置--host 0.0.0.0检查防火墙和安全组容器内执行 nvidia-smi 报错NVIDIA Container Toolkit 未安装或未配置nvidia-container-cli info安装 Toolkit确认 docker runtime 已配置模型加载非常慢冷启动加载权重共享存储吞吐不足查看作业日志中的加载耗时预热模型使用本地缓存增大存储带宽服务启动后立刻报 OOM模型参数量超出单卡显存查看dmesg和作业日志切换量化版本设置--tensor-parallel-size健康检查失败服务仍在加载模型探活超时时间过短curl 手动访问 /health查看服务日志延长健康检查超时增加重试次数端口冲突多个服务申请了同一端口ss -lntp看端口占用情况让编排层分配动态端口建立端口分配表节点重启后服务没有恢复编排层未配置自动重启scontrol show job查看作业状态配置自动重启策略结合 systemd 守护每个问题都不要只盯着表象。比如端口冲突根因可能是编排层没有做端口分配也可能是你手动启动了一个残留进程。先把“现在谁在监听这个端口”查清楚再决定杀掉哪个进程。9. 最佳实践与工程建议推理服务编排上了生产环境不能只看“能跑通”。下面这些实践是我在同类系统里反复验证过值得投入的环节。9.1 服务定义尽量声明式把服务名称、模型路径、GPU 数量、分区、端口写成配置而不是散落在 shell 历史记录里。这样团队其他成员能直接看到集群里有哪些服务、用的什么模型、占了多少资源。9.2 固定端口和动态端口要有规则小规模集群可以约定端口段比如18000-19000归推理服务使用规模再大一点最好由编排层统一分配。否则时间一长端口冲突会变成每日问题。9.3 所有推理服务必须暴露健康检查接口vLLM 默认提供/health端点TensorRT-LLM 和 NVIDIA NIM 也有对应的探活方式。这是编排层判断服务是否需要重启的唯一可靠依据。没有健康检查自动拉起就无从谈起。9.4 日志不能只留在节点本地SLURM 作业日志默认写在计算节点节点一旦故障日志就丢了。建议将所有推理服务的日志发送到集中采集系统例如 Elasticsearch、Loki 或云日志服务。这样在排障时不需要先定位节点再翻文件。9.5 网络暴露必须做认证如果你通过 srt-slurm 暴露推理 API不要把端口直接开放到公网。建议前置一层认证网关例如使用 API Key、OIDC 认证或者至少做 IP 白名单。无认证的推理服务在公网上会很快被扫描和恶意调用这个风险不是危言耸听。9.6 给推理服务设置独立的 QoS 或分区训练作业通常是高吞吐、短时间、可排队推理服务是长期占用、低延迟、不能排队。两类负载混在一个分区里会导致推理服务被训练作业挤到后面延迟不稳定。最好单独设置一个infer分区配合 SLURM QoS 限制最长运行时间或最大并发数。9.7 预留模型预热策略大模型冷启动时间较长。如果服务频繁重建调用方会一直看到超时。一种做法是把服务常驻并定期探活另一种是启动脚本里先向模型发送一个最小请求完成预热再注册到服务发现中心。后者在自动扩容场景下非常有用。9.8 用 Prometheus 监控 GPU 和延迟NVIDIA 提供了dcgm-exporter可以采集 GPU 利用率、显存、温度等指标。推理服务层面可以暴露请求延迟、QPS、token 吞吐。把这些接入 Prometheus Alertmanager一旦 GPU 利用率异常或 P95 延迟升高就能第一时间收到告警。10. 总结与后续学习方向srt-slurm 这类项目的价值不是提供一个又一个炫酷命令而是把 GPU 推理服务的运行方式从“脚本 人工盯”变成“声明 编排自动维护”。它适合那些已经有 SLURM 集群、想在不动底层调度系统的情况下获得服务化能力的团队。如果你正在搭大模型推理平台可以重点关注它如何处理作业声明、健康检查、服务发现和自动重启四个环节。下一步的实践建议很直接先准备一个两节点的 SLURM 测试环境装好 NVIDIA Container Toolkit用 vLLM 跑通一个最小推理服务再对照 srt-slurm 官方仓库的 README把这里描述的通用概念映射到具体命令上。不用一上来就追求多副本和高可用能把“提交一个服务、自动拉起、API 可通、失败能重启”这条链路跑通就已经离生产环境很近了。把这篇文章收藏下来动手实验时遇到“作业排队、服务端口不通、容器不识 GPU、健康检查失败”四类问题回来对照第 8 节的排查表基本能省下不少找资料的功夫。
返回列表