抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

提交 GPU 作业之前,需要确认各分区的空闲卡数。常规流程依次包括连接 VPN、SSH 登录、执行 scontrol 与 squeue,并对输出人工统计。Slurm GPU Widget 将上述流程封装为一个后台采集脚本,以 Übersicht 桌面卡片和 SwiftBar 菜单栏两种形式常驻显示各分区的占用情况;单次刷新耗时约 0.7–1.3 s,默认刷新周期为 15 s。项目以 MIT 协议开源:Yu-Xiang-Yang/slurm-gpu-widget。

1 背景与动机

GPU 集群通常以分区(partition)为单位组织资源,不同分区的 GPU 型号、节点数量与负载差异显著。提交作业前,需要回答以下三个问题:

  • 哪个分区仍有空闲 GPU;
  • 单个节点上最多有多少空闲 GPU,该数值决定多卡作业能否立即启动;
  • 自身作业的运行时长、时间限制与排队状态。

对于需经 VPN 接入的集群,获取上述信息的常规流程存在三方面不便:

  • 流程冗长:依次完成 VPN 连接、SSH 登录、命令执行三个步骤,每次查询均需重复;
  • 原始输出不含汇总值:scontrol show node 按节点逐行输出,GPU 总数位于 Gres 字段,已分配数位于 AllocTRES 字段,需解析后累加;
  • 节点状态需剔除:处于 DOWN、DRAIN 等状态的节点不可调度,若计入总量将高估可用资源。

Slurm GPU Widget 的目标是将该流程自动化,并以常驻界面呈现结果,使资源状态无需登录集群即可读取。

2 功能概述

桌面小组件界面(模拟数据):分区占用与本人作业的显存、已运行时间

图 1 Übersicht 桌面卡片界面(模拟数据,由仓库 mock/ 数据与前端源码渲染,启用 SHOW_MY_JOBS=1 与 JOB_GPU_USAGE=1)。上部为各分区的运行/排队/本人作业数、GPU 与内存占用条及单节点最大空闲卡数;下部“我的作业”列出作业 ID、名称、已运行时间/时限,以及各 GPU 的显存占用与利用率。

主要功能如下:

功能 说明
VPN 检测与自动连接 通过 AppleScript 读取指定 Tunnelblick 配置的状态;非 CONNECTED 时发起连接,并轮询至多 CONNECT_TIMEOUT 秒
单次 SSH 往返 scontrol show node -o 与 squeue 在同一非交互会话中执行,复用持久连接
分区级聚合 GPU 已用/总数、CPU 已用/总数、空闲与不可用节点数、运行与排队作业数、本人作业数
MIG 识别 MIG 切分的 GPU(如 gpu:2g.20gb:12)以 MIG 实例数而非物理卡数计量,并加以标注
内存与型号 分区与节点级主机内存、可配置的 GPU 型号标签;可选通过 nvidia-smi 读取单卡型号、显存与利用率
本人作业列表 作业 ID、名称、已运行时间/时限、分区、节点与 GPU 数;启用 JOB_GPU_USAGE=1 后附各 GPU 的显存占用与利用率
节点列表(可选) 在各分区下列出节点名称、状态及单节点 GPU/CPU 占用
双语界面 中文或英文,可显式指定或随 macOS 系统语言
可拖动卡片 桌面卡片可通过标题拖动,位置在刷新后保持,双击恢复默认位置

3 系统架构

项目由一个采集脚本与两个前端组成,数据流如下:

Tunnelblick(VPN 状态检测 / 自动连接)
        │
        ▼
cluster-status.sh ── ssh(BatchMode, ControlMaster)──▶ 登录节点:scontrol / squeue
        │  本地 awk 聚合
        ├──── JSON ─────────────▶ Übersicht 桌面卡片
        └──── --swiftbar-stream ▶ SwiftBar 菜单栏
前端 呈现位置 安装方式
Übersicht 桌面半透明卡片,每个分区一块 brew install --cask ubersicht
SwiftBar 菜单栏条目(如 GPU free 14/32),下拉菜单按分区展开 brew install --cask swiftbar

两个前端调用同一采集脚本 cluster-status.sh,二者可单独或同时安装。采集脚本提供四种调用方式:

参数 输出
(无) 单个 JSON 对象,供 Übersicht 解析
--swiftbar SwiftBar 插件格式
--swiftbar-stream 同上,作业已运行时间编码为占位符,供 SwiftBar 流式插件逐秒替换;该插件实际调用此模式
--connect 仅触发 Tunnelblick 连接;对应 SwiftBar 菜单中的手动连接项

JSON 结构是采集层与展示层之间的接口约定,替换任一侧不影响另一侧。

4 数据采集方法

采集流程分为五个步骤:

  1. VPN 状态:osascript -e 'tell application "Tunnelblick" to get state of first configuration where name = "…"'。
  2. 数据获取:ssh -o BatchMode=yes $SSH_HOST "scontrol show node -o; squeue -h -o '%P|%T|%u'",两条命令共用一次 SSH 往返。
  3. GPU 明细(可选,GPU_DETAIL=1):登录节点并行对各 GPU 节点执行 nvidia-smi --query-gpu=index,name,memory.used,memory.total,utilization.gpu,单节点超时由 GPU_DETAIL_TIMEOUT 限定。在启用 pam_slurm_adopt 的集群上,仅用户有运行作业的节点接受连接,其余节点回退为 Slurm 给出的统计值。
  4. 本人作业:squeue -u $USER -t R 提供作业 ID、名称、已运行时间、时限、节点与每节点 GPU 数。
  5. 本地聚合:由 awk 完成,规则见下。

聚合规则:

  • CPU 使用量取自 CPUAlloc/CPUTot;
  • GPU 总数由 Gres 字段解析,支持 gpu:a100:8(S:0-1)、gpu:4、gpu:2g.20gb:12,gpu:1g.20gb:4 等格式;已分配数取自 AllocTRES 中的 gres/gpu=N(数量为零时该字段缺省);
  • Partitions 字段按逗号拆分,隶属多个分区的节点在各分区内分别计数,全局 GPU 总量中仅计一次;
  • 处于 DOWN、DRAIN、FAIL、MAINT、NOT_RESPONDING 状态的节点不计入可用容量;
  • 主机内存使用量按 RealMemory − FreeMem 计算,原因是未对内存调度的集群上 AllocMem 恒为 0。

4.1 单节点最大空闲卡数

输出中的 gpu_max_free_node 表示单个节点上空闲 GPU(或 MIG 实例)的最大数目,该值给出可立即启动的单节点作业规模上限。图 1 中 gpu 分区空闲 10/16,但单节点最多空闲 8 卡;gpu-v100 分区空闲 0/4,单节点最多空闲 0 卡。全局空闲数与单节点空闲数需结合判读:前者反映总量,后者决定多卡作业能否一次性获得所需节点。

4.2 作业 GPU 使用情况

JOB_GPU_USAGE=1 时,对每个运行中的作业(至多 8 个)在其既有资源分配内执行 srun --jobid=<id> --overlap -N1 -n1 nvidia-smi …。在 ConstrainDevices=yes 的集群上,该命令仅能看到作业持有的 GPU,无需额外的索引映射,也不会申请新的资源。该路径不受 pam_slurm_adopt 限制,但每次查询会在 sacct 中记录为一个作业步(<jobid>.0、<jobid>.1 等),因此默认关闭,并由 JOB_GPU_INTERVAL(默认 30 s)限制查询频率,使小组件产生的作业步数量有界。

图 1 下部为该功能的显示效果:作业 123456(train_llm,2 块 GPU)已运行 1:23:45,时限为 2 天;GPU0 显存占用 59.8/80 GB、利用率 97%,GPU1 显存占用 39.3/80 GB、利用率 88%。显存占用条按占比着色(低于 60% 为绿色,60%–90% 为黄色,90% 及以上为红色),已运行时间在两次集群查询之间由本地逐秒递增。

5 刷新机制与性能

组件 周期 说明
Übersicht,集群数据 15 s 由 index.jsx 顶部的 REFRESH_SECONDS 设定
SwiftBar,集群数据 15 s 由 swiftbar/cluster.sh 中的 REFRESH_SECONDS 设定(流式插件,文件名中不含周期)
作业已运行时间 1 s 本地按 elapsed_s + (now − epoch) 递增,并以 limit_s 为上限,不产生集群查询
单作业 GPU 显存/利用率 30 s 仅 JOB_GPU_USAGE=1 时有效

性能方面采取三项措施:

  • 连接复用:SSH 启用 ControlMaster=auto 与 ControlPersist=300,复用连接可省去握手开销,源码注释给出的量级为约 1 s 降至约 0.3 s;单次完整刷新耗时约 0.7–1.3 s。
  • 查询合并:节点与队列信息在同一会话内一次获取。
  • 重叠抑制:采集脚本以目录锁(mkdir)防止并发运行;上一次采集(如 VPN 或 SSH 响应较慢)尚未结束时,直接返回上一次的结果。残留超过 90 s 的锁视为进程异常退出所致,将被清除。

6 安装与配置

6.1 前提条件

  1. 免密 SSH:小组件在后台运行,无法响应交互提示,须采用密钥认证。密钥带口令时,需在 ~/.ssh/config 中加入 UseKeychain yes 与 AddKeysToAgent yes;跳板机可通过 ProxyJump 声明。
  2. 非交互 VPN:Tunnelblick 的凭据须存入 macOS 钥匙串。需要一次性口令的 VPN 无法自动连接,此时应设置 AUTO_CONNECT=0。
  3. 自动化权限:首次运行时 macOS 询问是否允许 Übersicht 或 SwiftBar 控制 Tunnelblick,自动重连需授予该权限。

6.2 安装步骤

安装任一前端并启动一次(以生成其插件目录),随后执行:

git clone https://github.com/Yu-Xiang-Yang/slurm-gpu-widget.git
cd slurm-gpu-widget
bash install.sh
open -e ~/.config/cluster-widget/config

编辑配置后验证采集脚本,输出 JSON 即表明配置有效:

~/.config/cluster-widget/cluster-status.sh
~/.config/cluster-widget/cluster-status.sh --swiftbar

重复执行 install.sh 会更新脚本与小组件,但不会覆盖已有的配置文件。

6.3 配置项

配置文件位于 ~/.config/cluster-widget/config(模板为 config.example),命令行给出的环境变量优先于文件设置。常用配置项如下:

配置项 默认值 说明
CLUSTER_NAME 空 显示于标题与菜单栏的集群名称
TB_CONFIG 空 Tunnelblick 配置名;为空时跳过 VPN 检测,直接 SSH 连接
SSH_HOST 空 SSH 目标,建议使用 ~/.ssh/config 中的 Host 别名
AUTO_CONNECT 1 VPN 断开时是否自动发起连接
CONNECT_TIMEOUT 30 等待 VPN 连接的最长时间(s)
SSH_TIMEOUT 8 SSH 连接超时(s)
PARTITIONS 空 分区白名单(逗号分隔);为空时显示全部
GPU_ONLY 1 1 仅显示 GPU 分区;0 增加 CPU 分区与 CPU 占用条
SHOW_NODES 0 1 在各分区下列出节点
GPU_MODELS 空 各分区的 GPU 型号标签,如 gpu=NVIDIA A100 80GB;compute2=NVIDIA V100 32GB;Gres 不含型号时需要设置
GPU_DETAIL 0 1 通过 nvidia-smi 读取各 GPU 的型号、显存与利用率
SHOW_MY_JOBS 1 1 列出本人运行中的作业,仅使用 squeue,不产生作业步
JOB_GPU_USAGE 0 1 经 srun --overlap 读取各作业的 GPU 显存与利用率
UI_LANG auto 界面语言:zh、en 或 auto

临时查看全部分区及节点明细时,可在命令行覆盖配置,不影响配置文件:

GPU_ONLY=0 SHOW_NODES=1 PARTITIONS=gpu,compute ~/.config/cluster-widget/cluster-status.sh --swiftbar

分区规模较大(约 100 个节点)时,SHOW_NODES=1 宜与 PARTITIONS 联用,以限制卡片高度。

7 输出格式

默认模式输出的 JSON 对象结构如下(节选):

{
  "lang": "en", "cluster": "Demo", "time": "13:58:32",
  "vpn": "CONNECTED", "error": "",
  "gpu_used": 10, "gpu_total": 20,
  "partitions": [{
    "name": "gpu", "nodes": 3, "idle_nodes": 1, "down_nodes": 1,
    "cpu_used": 48, "cpu_total": 128, "gpu_used": 6, "gpu_total": 16,
    "gpu_max_free_node": 8, "running": 2, "pending": 1, "mine": 0, "mig": false,
    "model": "NVIDIA A100-SXM4-80GB", "mem_used": 1236992, "mem_total": 3072000
  }]
}

字段约定如下:

  • 内存数值单位为 MiB;
  • node_list 仅在 SHOW_NODES=1 时出现;
  • 各节点的 gpus 数组仅在 GPU_DETAIL=1 且该节点接受查询时非空;
  • SHOW_MY_JOBS=1 时,顶层对象附加 my_jobs 数组,每个作业含 elapsed_s、limit_s(UNLIMITED 记为 −1),顶层的 epoch 记录采集时刻,供本地计时使用。

8 无集群环境下的测试

仓库的 mock/ 目录提供合成的 scontrol 与 squeue 输出,可在没有集群的环境中验证采集与前端渲染:

MOCK_DIR=mock bash cluster-status.sh
MOCK_DIR=mock SHOW_NODES=1 CLUSTER_NAME=Demo bash cluster-status.sh --swiftbar

图 1 即由模拟数据渲染而成:采集脚本以 MOCK_DIR=mock、USER=testuser、SHOW_MY_JOBS=1、JOB_GPU_USAGE=1 与 PARTITIONS=gpu,gpu-v100 运行,输出的 JSON 交由仓库中的 Übersicht 前端源码渲染,因此图中的作业名称、显存与时间均为 mock/ 目录中的合成数值,不对应真实集群。

9 设计取舍

设计 依据
全程非交互(BatchMode=yes) 后台进程无法响应口令提示,须以密钥认证为前提
单次 SSH 往返与连接复用 降低单次刷新耗时与登录节点负载
重查询默认关闭 GPU_DETAIL 与 JOB_GPU_USAGE 开销较大,后者还会在 sacct 中留下作业步,故需显式启用并限频
作业计时本地递增 每秒更新显示而不增加集群查询
单一采集脚本、JSON 作为接口 两个前端共用同一数据源,避免解析逻辑重复
安装器不覆盖配置 升级脚本时保留用户设置
不可用节点剔除、多分区节点全局去重 使空闲数反映实际可调度的容量

10 常见问题

现象 原因与处理
install.sh 报告未找到任何前端 应用从未启动过,插件目录尚未生成;启动一次后重新运行
Übersicht 桌面无显示 其渲染服务(端口 41416)未启动;退出并重新打开 Übersicht,可用 curl http://127.0.0.1:41416/ 验证
提示 ssh failed 未配置密钥登录或主机别名有误;执行 ssh -o BatchMode=yes $SSH_HOST true 复现
VPN 保持断开 钥匙串中无凭据,或自动化权限被拒绝(系统设置 → 隐私与安全性 → 自动化)

11 向其他调度器的扩展

采集脚本中仅远程命令与 awk 解析部分与 Slurm 相关。PBS 对应 pbsnodes -a 与 qstat,LSF 对应 bhosts 与 bqueues。替换这两处后,JSON 结构与两个前端均无需改动。

12 项目地址

GitHub:https://github.com/Yu-Xiang-Yang/slurm-gpu-widget(MIT 协议,含中英文 README)。

评论