从Slurm到推理编排:打通GPU资源调度与模型服务最后一公里
2026/8/30 3:30:21 网站建设 项目流程

如果把一个深度学习模型的部署当成“把容器跑起来”,那大部分推理项目其实早该上线了。真正拦住你的,往往不是模型权重,不是推理框架,而是下面这个问题:集群里有几十块 GPU,但谁的任务在哪个节点、哪张卡上跑,由谁来安排?

很多团队的实际状态是:训练和推理共享一批 GPU,资源申请靠线下沟通,卡被谁占满要靠挨个ssh上去看nvidia-smi。一个新模型要上线,得先人工找到一台不太忙的节点,手动起容器,手动记端口,进程一断只能靠运维半夜爬起来。这种模式下,模型的迭代速度很快,但上线速度永远卡在资源协调上。

所以最近 NVIDIA 开源相关推理编排项目时,大家关注的重点往往不是“又一个调度器”,而是它把推理服务的生命周期管理和集群调度真正打通了。这类项目以srt-slurm为代表的方向,本质上是在解决推理部署的“最后一公里”:让一个推理任务像提交作业一样简单,让 GPU 分配、容器启动、模型加载、服务暴露和状态观测全部自动化

这篇文章不会只停留在概念层面。我会从推理编排的痛点讲起,说明 srt-slurm 这类方案在技术栈中的定位,然后用一套完整的 Slurm + NVIDIA Triton 推理服务示例,演示从集群配置、任务提交到推理接口验证的全过程。文章会覆盖环境准备、配置文件、常见报错和运维最佳实践,适合正在搭建内部推理平台的算法工程师、平台开发者和运维同学收藏备用。

1. 推理部署为什么需要编排

先看一个典型场景。

假设团队里有 6 台 GPU 服务器,每台 4 张 A100,总共 24 张卡。训练任务占用一部分,在线推理占用一部分,还有临时的算法实验经常“借卡跑一下”。如果没有统一编排,会遇到几类典型问题。

第一,资源利用率完全依赖人的判断。有人习惯把任务固定提交到某台节点,哪怕那台节点的 GPU 已经排满;另一边某些空闲节点却整晚没人用。等真正需要大规模推理时,最先爆掉的不是模型性能,而是人工排卡的效率。

第二,部署动作很难标准化。A 同事上线模型用docker run手动挂载参数,B 同事写成 systemd 服务,C 同事直接在物理环境 conda 里跑。每个人的启动方式不同,日志位置不同,环境变量不同,排查问题时要重新学习一套“约定”。

第三,GPU 资源没有隔离边界。一个任务在容器里执行nvidia-smi能看到所有显卡,如果不小心设置了错误的CUDA_VISIBLE_DEVICES,很可能一次实验就把整个节点显存打满,影响同节点的其他服务。

换一个角度理解:训练任务天然适合排队,因为训练的时间维度以小时甚至天为单位,晚一点开始影响不大。但在线推理服务对响应时间和稳定性更敏感,它需要的不是“等待排到”,而是“在指定节点、指定 GPU、以可重复的方式快速拉起”,同时还要能被监控、被健康检查、被优雅下线。

这就是推理编排要解决的问题。它不是要把一个推理框架再封装一遍,而是把“资源分配”和“服务运行时”这两层结合在一起。用户只需要告诉集群:我要一个什么样的推理任务、需要几块 GPU、用哪个镜像;剩下的事情——选节点、分配 GPU、起容器、映射端口、上报状态——交给编排层自动完成。

2. 从资源调度到推理编排:srt-slurm 的定位

说起资源调度,很多做平台开发的同学第一反应是 Kubernetes。这两年 K8s 在模型推理领域的接受度确实在提高,但有一个事实需要注意:在很多以科学计算和深度学习起家的高性能计算中心,Slurm 仍然是整个集群的调度底座

Slurm 是开源的高性能计算工作负载管理器,广泛用于管理集群中的计算节点、GPU 资源和作业排队。它本身的模型非常成熟:用户用sbatchsrun提交作业,Slurm 负责根据分区、队列、资源和优先级决定作业在哪些节点上运行,并负责资源隔离和作业生命周期管理。

但 Slurm 并不是天然的“推理部署平台”。它擅长的是把一个作业调度到资源上跑完,而在线推理服务通常需要长期驻留、需要提供 HTTP/gRPC 接口、需要对模型版本进行管理、需要自动重启。这些能力不是 Slurm 的开箱即用功能,需要有人在上层做一层设计。

NVIDIA开源的 srt-slurm 方向,看名称就能猜到它的技术路线:以 Slurm 作为资源调度底座,把推理运行时(如 NVIDIA Triton Inference Server)的部署动作封装成可编排的作业。它不是要把 Slurm 改造成 K8s,也不是要在 K8s 里模拟一个 Slurm,而是在现有 Slurm 集群上补足推理服务需要的“服务化”能力。

从架构定位上看,这类方案和 Kubernetes 方案的区别可以总结为:

维度Kubernetes 方案Slurm + srt-slurm 方案
调度模型面向容器和微服务面向作业和资源分配
资源划分Pod 级别,依赖 Device PluginNode + GRES + GPU 卡粒度,天然适合 HPC
服务发现Service + Ingress 较完善需要额外设计端口映射和服务注册
推理任务适配标准,但需要较多 YAML 编排贴近现有深度学习团队使用习惯
团队上手成本需要理解 Pod、Service、PVC 等概念对 HPC 用户几乎零成本

对已经在使用 Slurm 的团队来说,这类方案最大的价值是不用推翻已有的集群体系。用户提交推理任务的方式和提交训练任务一样,调度层共用同一套资源和账号体系,运维也不用同时维护两套调度系统。

需要说明的是,srt-slurm 这类项目仍处于快速迭代阶段,不同版本的具体命令和接口可能会有变化。本文后续不会逐条猜测细节,而是把侧重点放在整个推理编排链路上:无论项目怎么更新,你需要打通的环节——GPU 资源识别、容器运行时、作业提交、端口管理、健康检查——是稳定不变的。

3. 环境准备与前置条件

要把推理编排真正跑起来,至少需要准备三类环境:GPU 节点的驱动和 CUDA、容器运行时、Slurm 集群。下面按顺序说明。

3.1 操作系统与 GPU 驱动

GPU 驱动是推理环境的地基。驱动装不上,后面所有环节都无从谈起。以 Ubuntu 系统为例,安装驱动之前建议先确认自己需要的 CUDA 版本,再反向选择驱动版本。如果你只是跑推理服务而不是训练大模型,通常不需要安装完整的 CUDA Toolkit,因为 NVIDIA 官方推理镜像里已经自带了 CUDA 运行库。宿主机只需要有和镜像兼容的 GPU 驱动即可。

安装驱动的方式一般有三种:

  • 通过系统包管理器安装 NVIDIA 驱动,例如apt install nvidia-driver-xxx
  • 通过nvidia-detectubuntu-drivers devices查询可用驱动版本;
  • 手动从 NVIDIA 官网下载.run文件安装,风险较大,需要先禁用默认的 nouveau 驱动,不建议新手使用。

安装完成后,用nvidia-smi验证:

nvidia-smi

预期输出会列出 GPU 型号、驱动版本和 CUDA 版本。最小可用的确认标准是:能看到 GPU,并且驱动版本满足推理镜像的要求。

3.2 容器运行时与 NVIDIA Container Toolkit

推理服务建议统一用容器交付。在 GPU 节点上,Docker 默认无法直接访问 GPU,必须安装 NVIDIA Container Toolkit,把 NVIDIA Container Runtime 注册给 Docker。

安装步骤(以 Ubuntu/Debian 为例):

# 根据系统信息添加 NVIDIA Container Toolkit 软件源 distribution=$(. /etc/os-release;echo $ID$VERSION_ID) curl -s -L https://nvidia.github.io/libnvidia-container/gpgkey | sudo apt-key add - curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | sudo tee /etc/apt/sources.list.d/libnvidia-container.list # 安装并配置运行时 sudo apt-get update sudo apt-get install -y nvidia-container-toolkit sudo nvidia-ctk runtime configure --runtime=docker sudo systemctl restart docker

注意:不同发行版和不同 Docker 版本的命令可能有差异。如果你使用的是 containerd 而不是 Docker,需要把--runtime=docker替换为 containerd 对应的 runtime。

配置完成后,可以用一个小容器验证 GPU 是否透传成功:

docker run --rm --gpus all nvidia/cuda:11.8-base-ubuntu22.04 nvidia-smi

3.3 Slurm 集群

Slurm 需要部署在管理节点和计算节点上。完整部署一套 Slurm 涉及 munge 认证、slurmctld 管理进程、slurmd 计算节点进程和 slurmdbd 计费数据库等组件。这个流程本身可以单独写一篇文章,这里只强调几个与 GPU 推理编排强相关的点:

  • 所有节点必须使用统一的网络通信机制,建议配置 munge 认证;
  • /etc/slurm/slurm.conf是所有节点的核心配置;
  • 计算节点需要启动slurmd,管理节点需要启动slurmctld
  • 配置修改后,需要重启相关进程或使用scontrol reconfigure重新加载。

如果当前还没有 Slurm 环境,可以先在一台多卡机器上搭建“单节点集群”,也就是控制节点和计算节点都是同一台机器。这样既能验证 GPU 资源识别,也能把编排流程完整跑通,适合做最小实践。

4. 集群配置:让 Slurm 认识 GPU

Slurm 本身不会自动感知节点的 GPU,需要手动在配置里声明。这是最容易踩坑的一步:很多节点明明有 GPU,但提交带--gres=gpu的作业时永远 PENDING,就是因为配置里没有告诉 Slurm 这张卡存在

4.1 在 slurm.conf 中声明节点与分区

假设我们有node01node04四台节点,每台 4 张 GPU,用于推理任务的节点可以单独放到一个分区里。

# /etc/slurm/slurm.conf 关键片段 NodeName=node[01-04] Gres=gpu:4 CPUs=32 State=UNKNOWN PartitionName=infer Nodes=node[01-04] Default=YES MaxTime=INFINITE State=UP

这里的含义是:

  • NodeName声明节点列表;
  • Gres=gpu:4声明该节点有 4 个 GPU 资源;
  • CPUs=32声明可用 CPU 核数;
  • PartitionName=infer定义一个名为infer的分区,后续推理任务都提交到这个分区。

4.2 在 gres.conf 中定义 GPU 文件

Slurm 4.x 系列使用gres.conf来定义 GPU 设备文件。如果节点名和 GPU 编号规则一致,可以这样写:

# /etc/slurm/gres.conf NodeName=node[01-04] Name=gpu File=/dev/nvidia[0-3]

对于较新的 NVIDIA GPU,也可以使用CType=GPU之外更精细的资源配置,例如 MIG 切片:

NodeName=node01 Name=gpu File=/dev/nvidia0 CType=GPU NodeName=node01 Name=gpu File=/dev/nvidia1 CType=GPU

MIG 场景下的配置更复杂,且不同模型差异很大,建议先跑通完整链路后再深入。

4.3 验证 GPU 资源是否被识别

重启或重载配置后,使用以下命令检查:

sinfo -o "%n %G %P" scontrol show node node01 | grep -i gres

如果看到类似Gres=gpu:4的输出,说明 Slurm 已经正确识别 GPU 资源。此时再提交一个交互式作业测试:

srun --partition=infer --gres=gpu:1 nvidia-smi

这个命令会申请一块 GPU 并在对应节点上执行nvidia-smi。如果输出正常,说明 GPU 资源已经被调度器分配,这是推理编排流程真正能跑起来的前提。

5. 用 srt-slurm 提交推理任务

在 Slurm 的调度模型里,推理服务可以看作一个需要长期运行的作业。它的核心逻辑很简单:申请一块 GPU,启动一个容器,容器内运行推理服务,对外暴露端口。下面以 NVIDIA 官方的 Triton Inference Server 为例,演示一条完整的推理编排链路。

5.1 准备模型仓库

Triton 通过模型仓库识别和加载模型。模型仓库是一个特定目录结构,每个子目录代表一个模型,模型版本放在以数字命名的子目录里。一个典型的结构如下:

model_repository/ ├── text_encoder/ │ ├── 1/ │ │ └── model.plan │ └── config.pbtxt └── ensemble/ ├── 1/ └── config.pbtxt

config.pbtxt用文本协议描述模型名称、输入输出张量、后端类型和运行实例数。例如:

name: "text_encoder" platform: "tensorrt_plan" max_batch_size: 8 input [ { name: "input" data_type: TYPE_INT32 dims: [128] } ] output [ { name: "output" data_type: TYPE_FP32 dims: [512] } ] instance_group [ { count: 1 } ]

这一步决定了推理服务能做什么,也决定了后续部署验证是否有意义。实际生产环境中,模型仓库建议放在共享存储上,这样多个节点可以共用一套模型文件,方便版本更新。

5.2 编写推理作业提交脚本

sbatch提交一个作业脚本,让 Slurm 自动分配 GPU 并启动 Triton 容器。

#!/bin/bash #SBATCH --partition=infer #SBATCH --gres=gpu:1 #SBATCH --cpus-per-task=8 #SBATCH --job-name=triton-infer #SBATCH --output=/var/log/slurm/%x-%j.log #SBATCH --error=/var/log/slurm/%x-%j.log export CUDA_VISIBLE_DEVICES=${CUDA_VISIBLE_DEVICES:-0} docker run --rm \ --gpus "device=${CUDA_VISIBLE_DEVICES}" \ --shm-size=1g \ -p 8000:8000 \ -p 8001:8001 \ -p 8002:8002 \ -v /data/model_repository:/models \ nvcr.io/nvidia/tritonserver:<version>-py3 \ tritonserver --model-repository=/models

脚本中值得注意的地方:

  • #SBATCH --gres=gpu:1声明这个作业需要 1 块 GPU;
  • $CUDA_VISIBLE_DEVICES是 Slurm 在分配 GPU 后自动注入的环境变量,里面是当前作业可用的 GPU 编号列表;
  • Docker 参数--gpus "device=${CUDA_VISIBLE_DEVICES}"把调度器分配的那张卡传给容器,避免容器看到节点上的所有卡;
  • 端口映射8000:8000用于 HTTP 推理,8001用于 gRPC,8002用于 Prometheus 指标。

<version>替换成实际可用的镜像 tag 即可,不同 Triton 版本对 CUDA 和驱动版本的要求不同,以官方文档为准。

5.3 提交作业并查看状态

sbatch run_triton.sh squeue

squeue会显示作业状态。刚开始可能是PENDING,等资源分配成功后变成RUNNING。如果一直PENDING,可以用scontrol show job <jobid>查看等待原因,最常见的是Resources,说明没有空余资源;也可能是PartitionConfig,说明分区配置不正确。

6. 运行验证与资源观测

作业从PENDING变成RUNNING,并不意味着推理服务已经就绪。Triton 启动需要加载模型,这个过程通常需要几十秒到几分钟,取决于模型大小。我们需要通过健康检查和推理请求来确认服务真正可用。

6.1 检查服务健康状态

Triton 提供 HTTP 健康检查接口:

curl -v http://localhost:8000/v2/health/ready

返回 HTTP 200 表示服务已经就绪,可以接收推理请求。如果返回 503,说明模型仍在加载或部分模型加载失败。

也可以查看作业日志:

tail -f /var/log/slurm/triton-infer-<jobid>.log

日志中会出现模型加载记录。出现Successfully loaded model表示对应模型加载成功。

6.2 发送一个推理请求

接下来用 Python 客户端验证推理链路。以下代码使用 HTTP 接口向 Triton 请求一个模型推理结果:

import httpx payload = { "inputs": [ { "name": "input", "shape": [1, 128], "datatype": "INT32", "data": [[1] * 128] } ] } response = httpx.post( "http://localhost:8000/v2/models/text_encoder/infer", json=payload, timeout=30, ) result = response.json() print(result["outputs"])

如果输出中有output张量和对应的data,说明整个链路已经跑通:Slurm 分配 GPU、容器启动、模型加载、HTTP 推理请求全部正常。

6.3 在节点上观察 GPU 使用情况

在计算节点上执行:

nvidia-smi

会看到 Triton 进程占用了分配的 GPU。更重要的是检查显存和利用率是否符合预期。如果在容器内看不到 GPU,或者nvidia-smi报错,问题通常出在容器运行时配置或者环境变量传递环节,可以对照下一节的排查表处理。

7. 常见问题与排查思路

推理编排链路涉及的组件较多,从 Slurm 到容器运行时再到推理框架,每一层都可能出现问题。下面整理一份高频问题排查表。

问题现象可能原因排查方式解决方案
作业一直处于 PENDINGGPU 资源不足或分区配置不正确scontrol show job <jobid>查看 Reason扩容节点 GPU,或修改slurm.conf中的分区和Gres配置
作业已在 RUNNING,但容器内nvidia-smi报错NVIDIA Container Toolkit 未安装或 Docker 未配置 runtime宿主机执行sudo nvidia-ctk runtime configure --runtime=docker,重新测试容器重新配置并重启 Docker,验证docker run --rm --gpus all nvidia/cuda:... nvidia-smi
多卡节点上作业只用到一张卡脚本没有正确传递CUDA_VISIBLE_DEVICES查看作业日志中的环境变量输出在启动脚本中显式导出$CUDA_VISIBLE_DEVICES,并传给 Docker 的--gpus参数
Triton 启动成功,但部分模型加载失败模型仓库目录结构或config.pbtxt配置错误查看 Triton 日志中对应的模型加载报错检查模型目录的版本号子目录和config.pbtxt字段
推理请求返回 404 或 400请求的模型名、输入名或张量形状不匹配先访问/v2/models/<model_name>查看模型元数据按元数据修正请求中的nameshapedatatype
节点被 Slurm 标记为 DRAINEDGPU 设备异常或 slurmd 探测失败scontrol show node <nodename>查看 Drain Reason,在节点上执行nvidia-smi修复 GPU 驱动或硬件问题后执行scontrol update NodeName=<nodename> State=RESUME
端口被占用导致容器启动失败多个推理作业被分配到同一节点且映射相同端口查看节点端口占用ss -lntp改进端口分配策略,例如为每个作业分配独立端口段,或通过配置中心动态分配
容器启动后显存被其他任务挤占没有做显存隔离检查同节点其他容器的CUDA_VISIBLE_DEVICES收紧 GPU 资源隔离策略,必要时为推理分区单独预留节点

这些问题的共同特点是:很多错误并不会在调度层暴露,而是你等到服务真正启动后才发现环境不对。所以建议把“运行验证”作为标准化流程的一部分,每次提交作业后自动执行一次健康检查,而不是靠人工发现。

8. 生产环境最佳实践

从一个能跑的 Demo,到一个稳定的推理平台,中间还有不少工程化工作。下面是几条我认为最重要的实践建议。

8.1 分区规划要面向业务场景

不要把训练任务和在线推理任务混在同一个分区。推理服务通常要求资源能够立即获得,不能排太久队;而训练任务对排队不敏感,但对算力规模要求高。建议把 GPU 节点分成若干分区,例如train分区用于训练,infer-online分区用于在线推理,infer-batch分区用于离线批量推理。不同分区可以配置不同的MaxTime,在线服务分区甚至可以不允许作业因资源不足而无限等待。

同时,可以给关键节点打上不同的特征标签,通过Feature字段区分是否有 MIG、显存大小、GPU 型号等。这样在提交推理任务时,可以直接声明需要什么规格的 GPU。

8.2 GPU 隔离要落实到两层

第一层是 Slurm 的资源分配,通过--gres=gpu:1确保两个作业不会被调度到同一张卡上。第二层是容器内环境,依赖CUDA_VISIBLE_DEVICES和 NVIDIA Container Toolkit 让容器只“看得到”被分配的那张卡。

需要注意,默认的 Docker 容器在宿主机上只要具备--gpus all权限,仍然可能通过 GPU 直通或其他方式访问同一张卡上的资源。如果你的推理场景对隔离要求极高,需要结合 cgroup 的 GPU 设备控制,甚至考虑 MIG 切分,从硬件层面做隔离。

8.3 模型仓库和镜像版本化

推理服务的可重复性,很大程度取决于模型和镜像是否可追溯。模型仓库建议使用对象存储或共享文件系统统一管理,每个模型目录对应版本号;镜像则建议在 CI 阶段打好确定的 tag,部署时使用完整镜像地址,避免使用latest。这样出现问题后,可以快速回滚到上一个稳定版本。

8.4 健康检查与自动拉起

在线推理服务不能“挂了没人管”。在 srt-slurm 这类编排方案里,可以考虑给每个推理作业配置一个心跳脚本:定期请求/v2/health/ready,如果连续失败,则主动触发作业退出,由上层系统重新提交作业。这比人工发现故障再处理要可靠得多。

8.5 监控指标要统一采集

NVIDIA 官方推理镜像通常自带 Prometheus 指标导出。Triton 默认在8002端口提供/metrics接口,包括 GPU 利用率和推理请求延迟等关键指标。建议在集群中统一部署 Prometheus + Grafana,使用node-exporter采集节点指标,使用 Triton 的 exporter 采集推理指标。这是判断“编排之后资源利用率是否真的提升”最重要的数据来源。

8.6 安全与权限最小化

生产环境中应避免直接使用 root 运行推理容器。Triton 镜像支持非 root 运行,启动时通过--user参数指定用户,并且模型仓库目录不要让所有开发人员都能写。Slurm 本身的权限模型也需要严格设置,普通用户只允许向指定的分区提交作业,不能随便修改节点配置。

9. 总结与下一步

推理部署正在从“一个人用一条命令跑通一个模型”走向“一个平台支撑多个团队、多类模型、多卡集群的统一调度”。NVIDIA 开源 srt-slurm 这类项目,正是在这个大背景下出现的:它不追求替代你熟悉的调度系统,而是在 Slurm 的资源管理之上,把推理服务变成可提交、可部署、可观测的标准化实体。

如果你正在规划内部推理平台,我建议不要一上来就追求大规模的调度优化。先把最小链路跑通:准备一台带 GPU 的机器,装好驱动和容器运行时,配置 Slurm 识别 GPU,写一个脚本提交 Triton 服务,用 Python 客户端发一次推理请求。等到这条链路顺畅之后,再逐步增加分区规划、MIG 切分、监控告警和多节点调度。这个阶段会把大部分环境问题暴露出来,而它们恰恰是后续所有工作能稳定的基础。

最终你会发现在推理编排里,真正的技术难点不是某个单点能力有多强,而是从“资源”到“服务”的过程是否足够流畅、可重复、可观察。想清楚这一点,再去看 srt-slurm 或其他调度方案,你的判断会比单看功能介绍准确得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询