Slurm 20.02.3 slurmrestd 实战:JWT认证与REST API接口调用指南
1. 为什么要在 Slurm 20.02.3 上折腾 slurmrest集群用久了命令行那套squeue、sbatch、scontrol确实顺手但一旦要把作业调度能力接进内部平台、监控面板或者自动化流水线靠subprocess去拼命令行、再正则解析文本输出维护成本会高得离谱。Slurm 从 20.02 版本开始正式把slurmrestd这个 REST API 守护进程纳入发行版20.02.3 是这条线上一个相对稳定的补丁版本它让外部程序可以用标准 HTTP 接口去查询节点、提交作业、管理分区而不用再和命令行文本格式死磕。这篇内容面向的是刚接触 slurmrest 的运维和平台开发同学。我会把 20.02.3 这个特定版本下 slurmrestd 的启动方式、认证机制、核心接口调用、以及实际踩过的坑讲清楚。需要先说明一点Slurm 的 REST API 在 20.02 到 21.08 之间经历过比较大的演进20.02.3 属于早期形态接口路径、认证头、数据格式和后来的版本差异不小网上很多教程其实是针对 21.08 之后的直接照搬会各种报错。所以这篇专门锁定 20.02.3把这一版的真实用法还原出来。先给一个整体认知slurmrestd 是一个独立的守护进程它自己不直接读slurm.conf去操作集群而是通过 Slurm 的C 库接口libslurm与slurmctld通信。这意味着它必须运行在一个能访问到slurmctld的节点上并且编译时链接的 Slurm 库版本要和集群一致。20.02.3 里它支持两种认证方式JWT和SOCKET前者适合跨网络的 HTTP 调用后者适合本机 Unix socket 调用。理解这两条路径的区别是后面所有操作的基础。我见过不少人在这一步就卡住装好了 slurmrestdcurl一发请求就返回 401 或者直接连接被拒然后开始怀疑人生。其实多数情况不是接口写错了而是认证没配对或者守护进程根本没监听在你以为的地址上。下面我会按“先跑起来、再调通、最后用顺”的顺序展开每一步都给出这一版特有的细节。2. slurmrestd 在 20.02.3 里的启动方式与监听配置2.1 二进制从哪来、依赖什么20.02.3 的 slurmrestd 通常随 Slurm 主包一起安装但有些发行版的打包会把它拆到单独的包里。你可以先用which slurmrestd确认如果没有检查一下是不是漏装了带 REST 支持的组件。它运行时会依赖libslurm、libslurmdb以及 HTTP 解析相关的库这一版内部用的是 libhttp_parser 一类的组件。用ldd $(which slurmrestd)看一眼动态链接能快速判断依赖是否齐全。一个容易被忽略的点slurmrestd 链接的 Slurm 库版本必须和slurmctld完全一致。20.02.3 的协议在 minor 版本间是兼容的但如果你在 20.02.3 的集群上跑了一个 20.11 编译的 slurmrestd通信时可能因为数据结构变化而出现莫名其妙的解析错误。所以最稳妥的做法是slurmrestd 和 slurmctld 用同一份源码、同一套编译参数构建。2.2 两种监听模式TCP 与 Unix Socket这一版 slurmrestd 支持通过-参数指定监听地址格式是host:port或者unix:/path/to/socket。举几个实际会用到的例子# 监听所有网卡的 6820 端口走 TCP slurmrestd 0.0.0.0:6820 # 只监听本机回环安全性更高 slurmrestd 127.0.0.1:6820 # 走 Unix socket适合同机调用 slurmrestd unix:/var/run/slurmrestd.socket这里有个 20.02.3 特有的行为如果你不指定认证方式它默认会尝试用SOCKET认证也就是依赖 Unix socket 的对端身份。但如果你监听的是 TCPSOCKET 认证就没法用了必须显式启用 JWT。很多人启动时不加参数然后从另一台机器curl结果一直 401就是因为这个默认行为。启动时还可以用-a指定认证插件用-t指定超时用-v提高日志级别。调试阶段我强烈建议加上-vv它会把每个请求的解析过程和认证结果打到 stderr排查问题时能省掉大量猜测。2.3 用 systemd 托管时的几个关键字段生产环境一般会用 systemd 拉起 slurmrestd。20.02.3 官方没有自带 unit 文件需要自己写。下面是一个经过验证的模板[Unit] DescriptionSlurm REST daemon Afterslurmctld.service Requiresslurmctld.service [Service] Typesimple Userslurm Groupslurm EnvironmentSLURM_JWTdaemon ExecStart/usr/sbin/slurmrestd -a rest_auth/jwt -vv 0.0.0.0:6820 Restarton-failure RestartSec5 [Install] WantedBymulti-user.target几个要点After和Requires保证它等slurmctld起来后再启动User必须是能读取 Slurm 配置和认证密钥的账号EnvironmentSLURM_JWTdaemon是这一版启用 JWT 认证的关键环境变量少了它 JWT 插件不会加载。我踩过一次坑unit 里忘了设这个变量服务看着是起来了但所有 JWT 请求都返回“认证插件未启用”查了半天才发现是环境变量的问题。3. JWT 认证在 20.02.3 中的生成与使用细节3.1 这一版的 JWT 和后来版本有什么不同20.02.3 的 JWT 实现还比较原始。它用的签名算法是 HS256密钥来自slurm.conf里AuthAltTypes和AuthAltParameters的配置或者由scontrol token命令动态生成。和 21.08 之后引入的jwt_key文件、RS256 支持相比这一版没有非对称签名密钥是共享的所以密钥文件的权限管理格外重要一旦泄露任何人都能伪造合法 token。生成 token 最直接的方式是用scontrol tokenscontrol token usernameroot lifespan3600它会输出类似SLURM_JWTeyJhbGciOi...的一行。这里的username是 token 归属的用户lifespan是有效期秒。注意这个命令需要在能访问slurmctld的节点上执行因为它要向控制器申请。生成的 token 直接拿来做 HTTP 请求的 Bearer 头即可。3.2 把 token 塞进请求头的正确姿势拿到 token 后请求头这样写TOKEN$(scontrol token usernameroot lifespan3600 | cut -d -f2) curl -H X-SLURM-USER-TOKEN: $TOKEN \ -H X-SLURM-USER-NAME: root \ http://127.0.0.1:6820/slurm/v0.0.36/ping注意两个头X-SLURM-USER-TOKEN放 tokenX-SLURM-USER-NAME放用户名。20.02.3 里这两个头是必须同时提供的只给 token 不给用户名服务端无法确定以哪个用户身份执行操作会直接拒绝。这一点和后来某些版本只用 token 就能推断用户名的行为不一样是这一版特有的要求。还有一个细节token 里的用户名和请求头里的X-SLURM-USER-NAME必须一致否则会返回权限错误。我见过有人用 root 生成的 token却在头里写了普通用户名结果一直 403排查时完全没往这个方向想。3.3 token 过期与刷新策略lifespan设得太短自动化脚本跑一半就失效设得太长安全性又打折扣。我的经验是交互式调试用 3600 秒后台服务用 86400 秒并配合定期刷新。刷新逻辑很简单就是在 token 快过期时重新调一次scontrol token把新值替换掉。如果你的服务是用某个固定账号跑的可以在启动脚本里先申请一个长有效期 token写进环境变量再由服务读取。需要提醒的是20.02.3 没有提供 token 吊销接口一旦签发在有效期内始终可用。所以不要把 token 硬编码进代码仓库也不要在日志里打印完整 token。我一般会在日志里只打前 8 位和后 8 位中间用星号代替既方便排查又不泄露。4. 核心接口的调用逻辑与数据格式4.1 接口路径里的版本号不是随便写的20.02.3 的 REST 路径形如/slurm/v0.0.36/...这个v0.0.36是API 数据版本不是 Slurm 版本号。它对应的是这一版 Slurm 内部数据结构的版本标识。如果你把路径写成/slurm/v0.0.35/或者干脆不写版本服务端会返回 404 或版本不匹配错误。所以第一步永远是确认当前 slurmrestd 支持的版本号可以用curl -H X-SLURM-USER-TOKEN: $TOKEN \ -H X-SLURM-USER-NAME: root \ http://127.0.0.1:6820/slurm/v0.0.36/ping返回{ping: pong}就说明版本号对了、认证也通了。这个 ping 接口是我建议每个人第一个去调的它能一次性验证三件事网络通不通、认证对不对、版本号准不准。4.2 查询类接口节点、分区、作业查询节点用/slurm/v0.0.36/nodes查询分区用/slurm/v0.0.36/partitions查询作业用/slurm/v0.0.36/jobs。这几个接口都支持在路径后加具体名称比如/slurm/v0.0.36/nodes/node01只查某个节点。返回的是 JSON字段名和scontrol show的字段基本能对应上但结构是嵌套的需要花点时间熟悉。举个实际例子查所有节点并只看状态和 CPU 数curl -s -H X-SLURM-USER-TOKEN: $TOKEN \ -H X-SLURM-USER-NAME: root \ http://127.0.0.1:6820/slurm/v0.0.36/nodes \ | python3 -c import sys, json d json.load(sys.stdin) for n in d[nodes]: print(n[name], n[state], n[cpus]) 这里有个 20.02.3 的特点state字段返回的是字符串数组而不是单个字符串因为一个节点可能同时处于多个状态比如IDLE和DRAIN。解析时要注意它是 list直接当字符串用会出错。我第一次写解析脚本时就栽在这后来统一用,.join(n[state])处理才稳。4.3 提交作业POST 请求的 body 结构提交作业用POST /slurm/v0.0.36/job/submitbody 是 JSON。20.02.3 的字段命名和sbatch参数有对应关系但用的是 JSON 的驼峰或下划线风格需要对照文档。一个最小可用的提交示例curl -s -X POST \ -H X-SLURM-USER-TOKEN: $TOKEN \ -H X-SLURM-USER-NAME: root \ -H Content-Type: application/json \ -d { script: #!/bin/bash\nhostname\nsleep 10\n, job: { name: rest_test, partition: debug, tasks: 1, cpus_per_task: 1 } } \ http://127.0.0.1:6820/slurm/v0.0.36/job/submit注意script字段里换行要用\n整个脚本作为字符串传入。job对象里放调度参数。返回里会带job_id拿到后可以用/slurm/v0.0.36/job/{job_id}去查状态。这一版的提交接口对字段校验比较严格写错一个字段名可能整个请求被拒但错误信息还算清楚会告诉你哪个字段不合法。4.4 取消与控制作业取消作业用DELETE /slurm/v0.0.36/job/{job_id}这个比较直观。控制作业比如 hold、release在 20.02.3 里支持得有限部分操作需要通过提交时带参数或者用scontrol配合完成。如果你的场景强依赖运行时控制建议先确认这一版是否覆盖了你需要的操作不要想当然地以为所有scontrol功能都有 REST 对应。5. 实测中反复出现的坑与排查链路5.1 401 与 403先分清是认证还是授权401 通常是 token 缺失、过期或格式不对403 是认证过了但权限不够。排查顺序应该是先用 ping 接口确认认证链路通不通通了再去看具体接口的权限。如果 ping 都 401问题一定在 token 或请求头如果 ping 通了但某个接口 403那多半是X-SLURM-USER-NAME对应的用户在 Slurm 里没有相应权限。我遇到过一次很隐蔽的情况token 是用 root 生成的请求头也写了 root但 slurmrestd 进程是以slurm用户跑的而slurm用户没有读取某个配置的权限导致它在解析用户身份时失败返回了一个看起来像认证失败的 401。后来把 slurmrestd 的运行用户调整到有足够权限的账号才解决。所以守护进程的运行身份也是排查链路里必须确认的一环。5.2 版本号写错导致的 404前面提过路径里的v0.0.36但实际部署时这个数字可能因为编译选项不同而有差异。最可靠的办法是看 slurmrestd 启动日志它会在启动时打印支持的 API 版本。如果日志级别不够用-vv重启一次就能看到。不要凭记忆写版本号每次部署新环境都确认一遍。5.3 JSON 解析失败与字段类型陷阱20.02.3 对 JSON 的解析比较严格多余字段、类型不匹配都会导致 400。常见的有把数字写成字符串、把数组写成单值、字段名大小写错误。排查时把-vv日志打开它会打印出解析失败的具体位置。另外前面提到的state是数组这一点在写解析代码时一定要按数组处理否则你的监控脚本会在某些节点进入多状态时突然崩溃。5.4 长连接与超时设置slurmrestd 默认的超时可能不适合大批量查询。如果你要一次性拉取上千个作业的信息请求可能在中途超时。这一版可以用-t参数调整超时也可以在客户端设置合理的curl --max-time。我的做法是查询类请求超时设 30 秒提交类设 60 秒并在客户端做重试重试时注意幂等性——提交作业的重试可能导致重复提交需要配合业务层的去重逻辑。6. 把 slurmrest 接进实际工作流的经验6.1 封装一层薄客户端比直接裸调靠谱直接在业务代码里拼 URL 和 JSON短期看省事长期是灾难。我的做法是封装一个薄客户端把版本号、认证头、超时、重试都收进去业务层只调client.submit_job(...)、client.get_nodes()这样的方法。这样当 Slurm 升级、API 版本变化时只需要改客户端一处。20.02.3 到后续版本的接口变动不小这层封装能帮你把迁移成本降到最低。6.2 监控场景下的轮询频率用 REST 做集群监控时轮询频率要控制好。slurmrestd 每次请求都会去slurmctld取数据频率太高会给控制器带来额外压力。我的经验是节点和分区状态 30 秒一次足够作业列表 10 到 15 秒一次单个作业状态按需查询。如果集群规模大考虑用slurmdbd的历史数据接口做聚合而不是频繁打实时接口。6.3 日志与审计slurmrestd 的访问日志默认比较简单建议在它前面放一层反向代理由代理记录完整的请求日志和响应码。这样既能做审计又能在出问题时快速定位是哪个调用方、哪个接口出的错。token 在日志里要做脱敏这一点前面强调过不再重复。6.4 这一版值不值得用在生产说实话20.02.3 的 slurmrest 功能还比较基础接口覆盖面和稳定性都不如后来的版本。如果你的集群短期内不打算升级用它做只读查询和简单提交是可行的但如果要做复杂的作业编排、精细的权限控制建议评估升级到更新的 Slurm 版本。我个人的做法是在 20.02.3 上先用 REST 把监控和简单自动化跑通积累经验等升级窗口到了再平滑迁移到新接口。这样既不耽误当前需求也为后续演进留了余地。最后分享一个我在调试时常用的小技巧把scontrol token和curl包成一个 shell 函数每次调用自动申请新 token 并带上正确的头省去手动复制的麻烦。这个函数我放在~/.bashrc里调试效率提升非常明显。函数大概长这样slurm_api() { local path$1; shift local token token$(scontrol token usernameroot lifespan600 | cut -d -f2) curl -s -H X-SLURM-USER-TOKEN: $token \ -H X-SLURM-USER-NAME: root \ $ http://127.0.0.1:6820/slurm/v0.0.36/${path} }用的时候slurm_api ping、slurm_api nodes就行token 每次自动刷新不用担心过期。这个习惯帮我在排查认证类问题时省了大量时间推荐你也试试。