内容
# 集群作业 — 终端仪表盘
[](https://github.com/Dafidofff/cluster-job-monitor/actions/workflows/ci.yml)
[](https://codecov.io/gh/Dafidofff/cluster-job-monitor)
[](LICENSE)
[](https://www.python.org/)
📖 **[文档](https://dafidofff.github.io/cluster-job-monitor/)** — 扩展参考(配置、代理概述、MCP、开发)。
一个只读终端仪表盘,显示您在多个集群和桌面上的SLURM作业,提供统一视图。它通过SSH连接到每个主机,运行`squeue --me`,并呈现一个实时的、颜色编码的概述。
**适用于编程代理:** 一个`--overview`调用(命令行接口 **或** [MCP](#agent-overview))返回每个集群和分区中,有多少CPU/GPU空闲 — 按GPU类型细分,并显示单个节点上最大的空闲块 — 以及您的排队/运行作业和近似排队时间。参见[代理概述](#agent-overview)。
**设计为只读:** 唯一运行的命令是`squeue`和`sinfo`(SLURM主机)和`nvidia-smi` + `ps`(非SLURM GPU主机)。没有代码路径可以取消或提交作业。它使用**您的**现有SSH配置和密钥 — 不会暴露新的内容,没有服务器,没有存储的秘密。

*使用合成演示数据进行实时TUI (`--demo`).*
## 目录
- [安装](#install) — `pip install cluster-job-monitor`
- [快速查看(无需集群)](#quick-look-no-clusters-needed) — 使用合成数据尝试
- [真实设置](#real-setup) — 指向您的集群
- [代理概述](#agent-overview) — 空闲CPU/GPU,GPU类型,排队时间(命令行接口 + MCP)
- [按键](#keys) — TUI按键绑定
- [布局](#layout) · [开发](#development) · [许可证](#license)
## 安装
```bash
pip install cluster-job-monitor # 仪表盘 + 命令行接口
pip install "cluster-job-monitor[mcp]" # 还包括MCP服务器(见下文)
```
这会安装`cluster-jobs`命令。优先使用虚拟环境:
```bash
python3 -m venv .venv && source .venv/bin/activate
pip install cluster-job-monitor
```
> 如果`pip install`失败,出现SSL / “ssl模块不可用”错误,您的默认`python3`未启用OpenSSL。请使用Homebrew的Python:`/opt/homebrew/bin/python3 -m venv .venv`.
从源码安装?`pip install -e ".[mcp,dev]"`并使用`cluster-jobs`。旧入口点`python run.py …`和`python mcp_server.py`仍然作为薄壳有效。
## 快速查看(无需集群)
```bash
cluster-jobs --demo # 实时TUI,合成数据
cluster-jobs --once --demo # 打印一次合成快照并退出
```
## 真实设置
1. **确保每个集群都是一个可非交互式访问的SSH别名。** 在`~/.ssh/config`中,例如:
```sshconfig
Host mycluster
HostName login.mycluster.example.edu
User myuser
# 重用一个连接,以便每30秒轮询一次,速度快且无需重新身份验证:
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 10m
```
测试它:`ssh mycluster "squeue --me --noheader | head"`应该立即返回,无需密码提示。
2. **从模板创建您的配置:**
```bash
cp clusters.example.json clusters.json
```
编辑`clusters.json` — 每个主机一个条目。`ssh`是`~/.ssh/config`中的别名;为运行该工具的机器设置`"local": true`(无需SSH)。添加`"minimized": true`以启动一个折叠的集群(见下文)。`clusters.json`被git忽略。
**非SLURM GPU盒?** 为该主机添加`"scheduler": "gpu"`。它运行`nvidia-smi` + `ps`并显示每个GPU进程的一行 — 运行名称(来自`--name`/`--run-name`/`--experiment`参数,否则为脚本名称),使用的GPU内存,运行时间,拥有者 — 以及GPU利用率/内存线。通过任何登录shell(fish,csh,...)工作。
3. **运行它:**
```bash
cluster-jobs # 使用./clusters.json
cluster-jobs --config ~/my-clusters.json
```
(从源码安装仍然可以使用`python run.py …`。)
## 代理概述
对于需要决定**在哪里**启动作业的编程代理(或脚本),有一个一次性概述,回答一个调用:
- 您在每个集群和分区中有多少**排队/运行**的作业,
- 还有多少**CPU和GPU空闲** — 按GPU类型细分(a100/h100/...)并显示单个节点上最大的空闲块 —
- 以及一个**近似排队时间**:SLURM对您的待处理作业的预计启动时间,加上每个分区的预提交等待估计。
```bash
cluster-jobs --overview # 人类可读的能力表
cluster-jobs --overview --json # 机器可读的JSON(适用于代理)
cluster-jobs --overview --demo --json # 使用合成数据尝试
```
这是工具运行`sinfo`和集群范围的`squeue`的唯一地方(仍然只读)。所有这些都包含在与`squeue --me`相同的SSH往返中,因此概述是一个主机连接。JSON格式:
```json
{
"generated_at": 1718900000.0,
"clusters": [
{
"name": "Snellius", "ok": true, "error": null, "kind": "slurm",
"my_jobs": { "running": 2, "pending": 1 },
"my_pending_jobs": [
{ "jobid": "8123460", "name": "sweep-7", "partition": "gpu_a100",
"est_start": "2026-06-30T03:00:00" }
],
"free": { "cpus": 224, "gpus": 14 },
"capacity": { "cpus": 768, "gpus": 48 },
"partitions": [
{
"name": "gpu_a100", "my_running": 1, "my_pending": 2,
"cpus": { "free": 0, "alloc": 512, "total": 512 },
"gpus": {
"free": 0, "alloc": 32, "total": 32,
"by_type": { "a100": { "free": 0, "alloc": 32, "total": 32 } },
"max_free_per_node": 0
},
"is_default": false,
"nodes": { "idle": 0, "mixed": 0, "alloc": 8, "other": 0, "total": 8 },
"queue": {
"pending": 11, "running": 8,
"soonest_free_sec": 9300,
"wait_estimate": ">=2h35m (11 queued)"
}
}
]
}
]
}
```
注意:
- `cpus.free`是`sinfo`的*idle* CPU计数(已经排除下线/耗尽的节点);`gpus.free`(和`by_type`/`max_free_per_node`)仅在可用节点上计数(空闲/混合/已分配)。`max_free_per_node`告诉您多GPU作业是否适合一个节点。
- 在分区之间共享的节点计入每个分区的总数,但**仅一次**计入集群级别的`free`/`capacity`总数。`is_default`标志默认分区(无针对性`SBATCH`作业落在该分区)。
- `my_running`/`my_pending`和`my_pending_jobs[].est_start`来自`squeue --me`(`est_start`是SLURM的回填估计,在计算完成之前为`null`)。分区的`queue`块来自集群范围的`squeue`(所有用户)。
- `queue.wait_estimate`是一个**提示,不是承诺**:当GPU空闲时为`immediate`,否则为`~<t>`/`>=<t>`,源自即将完成的运行作业(`soonest_free_sec`)和待处理深度。它是乐观的 — 它不模拟调度器优先级 — 因此将其视为“大致估算”。
### 作为MCP工具
相同的概述通过MCP公开,以便代理可以调用它,原生调用,无需新的集群访问:
```bash
pip install "cluster-job-monitor[mcp]" # 安装MCP SDK
# 注册到Claude Code(将CLUSTER_MONITOR_CONFIG指向您的配置):
claude mcp add cluster-monitor \
-e CLUSTER_MONITOR_CONFIG=/abs/path/to/clusters.json \
-- cluster-jobs-mcp
```
(`cluster-jobs-mcp`随 `[mcp]` 额外功能一起安装。等效项:
`python -m cluster_job_monitor.mcp_server`,或`python /abs/path/to/mcp_server.py`
从源码安装。)
它提供两个工具:
| 工具 | 返回 |
|--------------------|----------------------------------------------------------------|
| `cluster_overview` | 上述JSON — 空闲CPU/GPU + 您的作业,按集群和分区 |
| `my_jobs` | 仅您的作业,按集群(跳过`sinfo`,更轻) |
## 按键
| 键 | 操作 |
|---------|-------------------------------------------------|
| `r` | 现在刷新 |
| `f` | 循环状态筛选(全部 → 运行 → ...) |
| `c` | 循环集群筛选 |
| `p` | 循环分区筛选 |
| `/` | 按作业名称搜索(输入应用) |
| `1`–`9` | 折叠/展开具有该数字的集群 |
| `m` | 折叠/展开**所有**集群 |
| `esc` | 清除所有筛选 |
| `q` | 退出 |
每个集群显示一个编号(`1 ▾ Snellius`);按它折叠该集群到一行摘要(`▸`),再次展开。启动集群折叠,在`clusters.json`中添加`"minimized": true`。
自动刷新间隔是配置中的`refresh_seconds`(默认为30)。
## 布局
```
cluster-job-monitor/
cluster_job_monitor/ # 可导入的包(pip-installable)
__init__.py # 公共API:Job, Host, Partition, Snapshot, collect, …
cli.py # 入口点(--once, --overview, --json, --demo, --config)
collector.py # UI无关:SSH + squeue/sinfo -> 快照数据类
mcp_server.py # MCP包装器,公开容量概述给代理
tui/app.py # 文本应用(实时循环,筛选,键绑定)
tui/render.py # Rich renderables(TUI,--once,--overview共享)
tui/sample.py # 合成快照,用于--demo
run.py # 兼容性包装 -> cluster_job_monitor.cli:main
mcp_server.py # 兼容性包装 -> cluster_job_monitor.mcp_server:main
clusters.example.json # 配置模板(复制到clusters.json)
pyproject.toml # 打包(hatchling)+ pytest/coverage配置
```
`cluster_job_monitor/collector.py` **没有第三方依赖**,返回一个`Snapshot`,其`.to_dict()`是JSON-ready — 那是未来Web/手机仪表盘的接口(将字典推送到身份验证端点并在浏览器中渲染),无需更改收集器。直接导入:`from cluster_job_monitor import collect, build_overview, load_config`.
## 开发
```bash
pip install -e ".[dev,mcp]" # 可编辑安装,测试 + MCP依赖
pytest # 运行测试套件
pytest --cov --cov-report=term-missing # 含覆盖率(~94%)
```
测试位于`tests/`,模拟SSH/`subprocess`,因此它们在任何地方运行 — 无需集群访问。CI([GitHub Actions](.github/workflows/ci.yml))在Python 3.10-3.12上运行它们,并向Codecov报告覆盖率。
## 许可证
[MIT](LICENSE) © David R Wessels. 您可以自由使用、修改和重新分发它;只需保留版权声明和许可证文本。