目录

目录

Debian 12 部署 Slurm 集群:高可用、GPU 与多租户实战

目录

当团队需要把分散的计算资源统一管理,无论是要管科研课题组的 GPU 服务器、跑生物信息的批量分析,还是给 AI 训练集群做算力调度,Slurm 都是常见选择。它在资源分配、公平调度和记账审计上已经过大规模验证。

Slurm 的文档很多,但从零搭一套生产可用的集群,还是要自己在官方手册、邮件列表和各种博客之间拼。这篇把单节点验证到多节点部署的过程串起来,重点补官方文档里讲得少的运维部分:cgroup v2 资源隔离的实际行为、slurmctld 主备切换怎么测试、节点健康检查脚本、多租户 QoS,以及学校场景里的 Lmod 和 LDAP/SSSD。

本文在以下环境做过实际验证:

  • 操作系统:Debian 12 (Bookworm),内核 6.1.x,cgroup v2(unified hierarchy)
  • Slurm 版本:Debian 官方仓库 22.05.8-4+deb12u3,安装命令见下文
  • 2026-11 实测规模:2 台 controller(primary/backup)+ 2 台计算节点,NFS 共享 StateSaveLocation,多节点调度、cgroup、记账、QoS、主备切换、Lmod、LDAP/SSSD 登录、CephFS 共享目录、pam_slurm_adopt 都实际跑过,结果见文末“实测环境与结果”
  • 文中 4 计算节点/32 节点属于扩展内容;GPU/GRES 用 PVE Intel GVT-g 实测,Slurm-web、Open OnDemand(Authentik OIDC)、Teleport(本地用户 + MFA)和 Prometheus/Grafana 告警链也都实际跑过。CephFS 一节用示例名 slurmfs,实际部署时把它换成你的文件系统名,cephx cap 也要跟着改

如使用 SchedMD 官方源安装 23.02+ 或 24.05+ 版本,部分参数名称可能有差异;25.05 起 select/cons_res 插件已移除,要改用 select/cons_tres。请对照对应版本的官方文档。

文章按部署、使用、多租户、运维、实测的顺序展开,篇幅较长;只看结果的读者可以直接跳到文末“实测环境与结果”。

  • 一个开源的作业调度与资源管理系统,广泛应用于 HPC 集群。
  • 核心组件:
    • slurmctld:控制器守护进程(管理队列、分配资源)。
    • slurmd:计算节点守护进程(执行任务)。
    • slurmdbd:计费/审计数据库守护进程(可选但推荐)。
    • munge:认证组件,所有节点共享同一 key。
  • 常用命令:sinfo、squeue、sacct、srun、sbatch、scontrol、sacctmgr。
  • 控制节点(Head/Controller):运行 munge、slurmctld(可选 slurmdbd 与数据库)。
  • 计算节点(Compute):运行 munge、slurmd。
  • 分区(Partition):队列的逻辑集合,例如 debug、batch。
  • 记账(Accounting):通过 slurmdbd + MariaDB/MySQL 收集作业、用户与项目统计。
  1. 单节点最小示例(便于学习/开发机):仅 slurmctld + slurmd + munge,无数据库。
  2. 小型生产:控制节点运行 slurmctld + slurmdbd + MariaDB,计算节点仅 slurmd。

下图展示一个典型的多节点 Slurm 集群拓扑,用户通过 Login Node 提交作业,slurmctld 调度至计算节点执行,所有节点共享存储以保证路径一致:

graph TD
    User["User SSH"] --> Login["Login Node<br/>Slurm Client + munged"]
    Login -->|"sbatch / srun"| CTLD["slurmctld<br/>Controller"]
    CTLD -->|"dispatch"| N1["node01 slurmd"]
    CTLD -->|"dispatch"| N2["node02 slurmd"]
    CTLD -->|"dispatch"| N3["node03 slurmd"]
    CTLD -->|"dispatch"| N4["node04 slurmd"]
    CTLD --- DBD["slurmdbd"]
    DBD --- DB[("MariaDB")]
    FS[("Shared Storage<br/>CephFS / NFS")] -.-> Login
    FS -.-> N1
    FS -.-> N2
    FS -.-> N3
    FS -.-> N4

关键设计要点:

  • 控制节点承载 slurmctld、slurmdbd 和 MariaDB,是集群的调度中枢;生产环境中建议与计算节点物理分离,避免用户作业影响调度器稳定性。
  • 登录节点只安装 Slurm 客户端工具和 munged,不运行 slurmd,不参与计算。
  • 所有节点共享同一 Munge Key;时间偏差超过 300 秒将导致认证失败。
  • 共享存储保证用户在 Login Node 上编写的脚本和数据在计算节点上路径一致。

把管理面、身份源、存储和计算节点放在一张图里,实际部署时通常会再拆管理网、存储网和计算网:

graph TB
    subgraph Users["用户 / 学生"]
        U["SSH / sbatch / srun"]
    end
    subgraph Login["登录节点"]
        L["slurm-client + munge + SSSD"]
    end
    subgraph Control["控制 / 管理节点"]
        C["slurmctld + slurmdbd + MariaDB"]
        LDAP["OpenLDAP / AD"]
    end
    subgraph Compute["计算节点"]
        N1["slurmd + cgroup v2 + Lmod"]
        N2["slurmd + cgroup v2 + Lmod"]
    end
    subgraph Storage["共享存储"]
        FS["CephFS / NFS / 并行文件系统"]
    end
    U --> L
    L -->|"提交 / 查询"| C
    C -->|"调度"| N1
    C -->|"调度"| N2
    L -.->|"LDAP/SSSD"| LDAP
    N1 -.->|"LDAP/SSSD"| LDAP
    N2 -.->|"LDAP/SSSD"| LDAP
    L --- FS
    N1 --- FS
    N2 --- FS

这张图是逻辑拓扑:生产环境建议管理网(Slurm/Munge/LDAP)和存储网(CephFS/NFS)分开,计算网(MPI/RDMA)按需再加一张网卡;不要把存储流量和管理流量混在同一条 1Gb 链路上。

一个典型的批处理作业从提交到完成的全过程:

sequenceDiagram
    participant U as User
    participant L as Login Node
    participant C as slurmctld
    participant D as slurmdbd
    participant N as Compute Node
    U->>L: sbatch job.sh
    L->>C: Submit Request
    C->>D: Log Submission
    C->>C: Backfill Schedule
    C->>N: Dispatch Job
    N->>N: Execute Prolog
    N->>N: Run User Job
    N->>N: Execute Epilog
    N->>C: Report Completion
    C->>D: Log Completion
    U->>L: sacct -j JOBID
    L->>D: Query Accounting
    D-->>U: Return Statistics

整个部署过程按依赖顺序推进:

flowchart LR
    A["OS Baseline<br/>NTP Kernel"] --> B["Munge<br/>Auth"]
    B --> C["MariaDB<br/>Database"]
    C --> D["slurmdbd<br/>Accounting"]
    D --> E["slurm.conf<br/>Core Config"]
    E --> F["cgroup.conf<br/>Isolation"]
    F --> G["slurmctld<br/>Controller"]
    G --> H["slurmd<br/>Compute"]
    H --> I["Validation<br/>sinfo srun"]

适用版本:Debian 12 (Bookworm) 或更新版本。不同版本包名可能略有差异,建议先用 apt search 确认。

  • 主机名与 /etc/hosts 解析正确(控制节点与计算节点互相可解析)。
  • 时间同步:建议安装 chrony 或启用 systemd-timesyncd,保证各节点时间一致。
  • 防火墙端口(如使用):
    • 6817/tcp:slurmctld
    • 6818/tcp:slurmd
    • 6819/tcp:slurmdbd(如启用)

建议使用 Chrony;所有节点均需配置,指向你的内网时间同步服务器 192.0.2.10。

安装并配置:

sudo apt update
sudo apt install -y chrony

# 备份原配置
sudo cp /etc/chrony/chrony.conf /etc/chrony/chrony.conf.bak

# 仅使用内网 NTP 源(可按需保留/删除其他 pool/server 条目)
sudo sed -i 's/^pool .*/# &/' /etc/chrony/chrony.conf
echo -e "server 192.0.2.10 iburst" | sudo tee -a /etc/chrony/chrony.conf

# 开放本机查询(可选,根据安全策略调整)
grep -q '^allow ' /etc/chrony/chrony.conf || echo 'allow 10.0.0.0/8' | sudo tee -a /etc/chrony/chrony.conf

sudo systemctl enable --now chrony
sleep 2
chronyc sources -v
chronyc tracking

如果使用防火墙,确保 UDP/123 放行(节点到 192.0.2.10,必要时双向)。

HPC 场景对进程数、文件描述符、网络缓冲区等有较高要求。建议在所有节点统一配置以下参数:

# 系统级 limits(/etc/security/limits.d/99-hpc.conf)
cat | sudo tee /etc/security/limits.d/99-hpc.conf >/dev/null <<'EOF'
*    soft    nofile    1048576
*    hard    nofile    1048576
*    soft    nproc     unlimited
*    hard    nproc     unlimited
*    soft    memlock   unlimited
*    hard    memlock   unlimited
*    soft    stack     unlimited
*    hard    stack     unlimited
root soft    nofile    1048576
root hard    nofile    1048576
EOF

# 内核参数(/etc/sysctl.d/99-hpc.conf)
cat | sudo tee /etc/sysctl.d/99-hpc.conf >/dev/null <<'EOF'
# 网络缓冲区 - MPI 和大规模并行通信需要
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 1048576
net.core.wmem_default = 1048576
net.ipv4.tcp_rmem = 4096 1048576 16777216
net.ipv4.tcp_wmem = 4096 1048576 16777216

# 连接追踪与 TIME_WAIT - 大量短连接场景
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15

# 虚拟内存 - 避免计算节点 OOM 前过度 swap
vm.swappiness = 10
vm.overcommit_memory = 0
vm.dirty_ratio = 40
vm.dirty_background_ratio = 10

# PID 上限 - 大规模并行任务需要
kernel.pid_max = 4194304

# inotify - 部分科学软件和存储客户端需要
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 8192
EOF

sudo sysctl --system

说明:

  • memlock unlimited 对于 InfiniBand RDMA 和大页内存(HugePages)场景是必需的;如果不使用 IB 或 HugePages,可适当缩小。
  • vm.swappiness = 10 在计算节点上通常足够;若节点无 swap 分区则此参数无效。
  • 以上参数要按实际硬件(内存大小、网卡带宽、存储类型)压测后调整。

在控制节点(只跑 slurmctld/slurmdbd,不跑作业):

sudo apt update
sudo apt install -y slurmctld slurm-client slurmdbd mariadb-server \
    munge libmunge2
# 如不做记账,可省略 slurmdbd/mariadb-server

如果控制节点也当计算节点(单节点示例),再补一个 slurmd:

sudo apt install -y slurmd

在计算节点:

sudo apt update
sudo apt install -y slurmd slurm-client munge libmunge2

注:实际包名以 apt search slurm 确认。slurm-wlm 是 meta 包,会把 slurmd、slurmctld、slurm-client 一起装上;按角色拆开装更干净。

控制节点误装 slurmd 时,它会因为 NodeName 里没有本机而报错,需要 systemctl disable --now slurmd。Debian 12 的 munge 包已经包含 munge、unmunge、remunge 和 mungekey,没有 munge-tools 这个包,也没有 create-munge-key。

在控制节点创建 key,并分发到所有节点;保持权限正确。

sudo /usr/sbin/mungekey --create --force   # Debian 12 的 munge 包提供的是 mungekey
sudo chown munge:munge /etc/munge/munge.key
sudo chmod 400 /etc/munge/munge.key

# 将 /etc/munge/munge.key 复制到每个计算节点同一路径,权限与属主一致

sudo systemctl enable --now munge
sudo systemctl status munge --no-pager

简单自检:

munge -n | unmunge
remunge

在控制节点:

sudo systemctl enable --now mariadb
sudo mysql -e "CREATE DATABASE slurm_acct_db;"
sudo mysql -e "CREATE USER 'slurm'@'localhost' IDENTIFIED BY 'strong_password';"
sudo mysql -e "GRANT ALL ON slurm_acct_db.* TO 'slurm'@'localhost'; FLUSH PRIVILEGES;"

编辑 /etc/slurm/slurmdbd.conf(若不存在则新建,属主建议 slurm 或 root,权限 600):

AuthType=auth/munge
DbdHost=controller-node
DbdPort=6819
SlurmUser=slurm
LogFile=/var/log/slurm/slurmdbd.log
PidFile=/run/slurmdbd.pid
StorageType=accounting_storage/mysql
StorageHost=localhost
StoragePort=3306
StorageUser=slurm
StoragePass=strong_password
StorageLoc=slurm_acct_db

DbdHost 是 slurmdbd 实际监听的地址。单机可以写 localhost;如果登录节点或备控制器也要查询记账,DbdHost 和 slurm.conf 里的 AccountingStorageHost 都要写控制节点主机名或 VIP,并放行 6819/tcp。用主机名时确认它解析到管理网 IP,而不是 Debian 默认的 127.0.1.1,否则 slurmdbd 只会监听回环。

启动 slurmdbd(先建好日志目录,否则 slurmdbd 打不开 LogFile):

sudo mkdir -p /var/log/slurm
sudo chown slurm:slurm /var/log/slurm
sudo systemctl enable --now slurmdbd
sudo systemctl status slurmdbd --no-pager

建议先用官方工具生成一个基础模板后再改:

# 在任意机器上生成配置样板(会打开浏览器),或使用交互式命令行向导
# https://slurm.schedmd.com/configurator.html

最小单节点示例(放到所有节点的 /etc/slurm/slurm.conf,文件相同;权限 644):

ClusterName=debian-cluster
SlurmctldHost=controller-node   # 控制节点主机名
SlurmUser=slurm
StateSaveLocation=/var/lib/slurm-llnl
SlurmdSpoolDir=/var/spool/slurmd
SlurmctldLogFile=/var/log/slurm/slurmctld.log
SlurmdLogFile=/var/log/slurm/slurmd.log
AuthType=auth/munge
ProctrackType=proctrack/cgroup
ReturnToService=2
SchedulerType=sched/backfill
SelectType=select/cons_res
SelectTypeParameters=CR_CPU_Memory
TaskPlugin=task/affinity,task/cgroup

# 推荐的优先级与计费策略(启用 QoS/Assoc 限额时常用)
PriorityType=priority/multifactor
PriorityWeightAge=1000
PriorityWeightFairshare=100000
PriorityWeightPartition=10000
PriorityWeightJobSize=0
AccountingStorageEnforce=associations,limits,qos

# Accounting(若启用 slurmdbd)
AccountingStorageType=accounting_storage/slurmdbd
AccountingStorageHost=controller-node
JobAcctGatherType=jobacct_gather/cgroup

# 节点与分区(按实际 CPU/Mem 修改)
NodeName=controller-node CPUs=8 RealMemory=32000 State=UNKNOWN
PartitionName=debug Nodes=ALL Default=YES MaxTime=INFINITE State=UP

多节点示例:

ClusterName=debian-cluster
SlurmctldHost=controller-node
SlurmUser=slurm
StateSaveLocation=/var/lib/slurm-llnl
SlurmdSpoolDir=/var/spool/slurmd
SlurmctldLogFile=/var/log/slurm/slurmctld.log
SlurmdLogFile=/var/log/slurm/slurmd.log
AuthType=auth/munge
ProctrackType=proctrack/cgroup
ReturnToService=2
SchedulerType=sched/backfill
SelectType=select/cons_res
SelectTypeParameters=CR_CPU_Memory
TaskPlugin=task/affinity,task/cgroup

PriorityType=priority/multifactor
PriorityWeightAge=1000
PriorityWeightFairshare=100000
PriorityWeightPartition=10000
PriorityWeightJobSize=0
AccountingStorageEnforce=associations,limits,qos

AccountingStorageType=accounting_storage/slurmdbd
AccountingStorageHost=controller-node
JobAcctGatherType=jobacct_gather/cgroup

NodeName=node[01-04] CPUs=16 RealMemory=64000 State=UNKNOWN
PartitionName=batch Nodes=node[01-04] Default=YES MaxTime=7-00:00:00 State=UP

创建需要的目录并设置权限(所有节点):

sudo mkdir -p /var/lib/slurm-llnl /var/spool/slurmd /var/log/slurm
sudo chown -R slurm:slurm /var/lib/slurm-llnl /var/spool/slurmd /var/log/slurm

初始化集群对象(sacctmgr 会读 /etc/slurm/slurm.conf,所以放在配置生成之后;slurmdbd 已经启动):

sudo sacctmgr add cluster debian-cluster

目标:提供一个供学生/科研用户 SSH 登录、编写脚本并提交作业的“登录/提交节点(login node)”,作业实际在计算节点运行,避免在登录节点上消耗大量资源。

2026-11 用一台独立 Debian 12 VM(slurm-build,10.31.0.229,2 vCPU / 4 GB)做了登录节点验证。它和控制器、计算节点分开,只装客户端和 munge,没有 slurmd;实测结果见下面的“独立登录节点实测”。

  • 与集群一致的 munge key(/etc/munge/munge.key)。
  • Slurm 客户端工具即可(不需要 slurmd):
sudo apt update
# Debian 12 有独立的 slurm-client 包,不需要装 slurmd/slurmctld
sudo apt install -y slurm-client munge libmunge2

# 复制控制节点上的 /etc/slurm/slurm.conf 到登录节点(保持完全一致)
# 确保 SlurmctldHost 指向控制节点,NodeName 与 Partition 仅包含计算节点

# 登录节点上不运行 slurmd,避免执行作业
sudo systemctl disable --now slurmd 2>/dev/null || true
sudo systemctl enable --now munge

注意:这里的 slurm-client 是 Debian 12 仓库里的 22.05。本次集群已经升到 23.11.4,登录节点实际用的是同一版本编译的 /opt/slurm-23.11/bin;客户端和控制器不是同一协议版本时不要混用。生产上客户端和控制器版本要保持一致。

登录节点的主机名不要出现在 slurm.conf 的 NodeName 列表中;只定义计算节点。例如:

NodeName=node[01-04] CPUs=16 RealMemory=64000 State=UNKNOWN
PartitionName=batch Nodes=node[01-04] Default=YES MaxTime=7-00:00:00 State=UP

登录节点上的 AccountingStorageHost 要指向 slurmdbd 所在的控制节点(或 VIP),不能照抄控制节点的 localhost,否则 sacct 会连不上数据库。

测试机 slurm-build(10.31.0.229,2 vCPU / 4 GB)只作为登录/提交节点,同时兼作后面的 OOD 主机;它没有运行 slurmd,也没有出现在 slurm.conf 的 NodeName 里。

配置步骤:

  1. 复制 /etc/slurm/slurm.conf 和控制节点的 /etc/munge/munge.key,启动 munge。
  2. 登录节点上要有与控制器相同 UID 的 slurm 系统用户(本次是 64030)。没有这个用户时,客户端解析配置会报 Invalid user for SlurmUser slurm 并直接退出;它不需要 shell,也不需要家目录。
  3. 在 /etc/hosts 里补上控制节点、备控和计算节点的主机名,因为 SlurmctldHost 写的是短主机名。
  4. 统一用户 UID:登录节点和计算节点都建了 stu01(20002:20000),否则作业跨节点时会因为 uid 不一致找不到家目录或输出文件。

第一次测试时 munge 已由 apt 安装后自动启动,覆盖 munge.key 再执行 systemctl enable --now munge 不会重新加载密钥。控制器日志报 Munge decode failed,scontrol ping 显示两个控制器都 DOWN;执行 systemctl restart munge 后恢复。换 key 或同步 key 后这个细节很容易漏。

实测命令和输出(以 stu01 身份):

$ sinfo
PARTITION AVAIL  TIMELIMIT  NODES  STATE NODELIST
debug*       up   infinite      2   idle slurm-node[1-2]
classA       up    4:00:00      1   idle node-gpu

$ srun -A classa -n1 hostname
slurm-node1

$ sbatch -A classa --chdir=/tmp --wrap 'hostname'
Submitted batch job 74

$ sacct -j 74 --format=JobID,State,NodeList
JobID    State        NodeList
-------- ------------ -----------
74       COMPLETED    slurm-node1

sbatch 继承的是提交时的当前目录,不是 $HOME。第一次在 /home/user 下提交时作业失败:

[73.batch] error: Could not open stdout file /home/user/slurm-73.out: Permission denied

改成 cd ~ 或加 --chdir=/tmp 后正常。登录节点和计算节点本次没有挂共享 /home,所以输出放在 /tmp;生产上按下一节挂 NFS/CephFS,否则用户脚本、输出和家目录在节点间不可见。

为方便用户在登录节点编写脚本并在计算节点运行,建议共享 /home 与项目目录(NFS/SMB/分布式存储均可)。示例(仅供参考,按你现有存储实现):

# 服务器上 /etc/exports(示意)
/home 10.0.0.0/8(rw,sync,no_root_squash)

# 客户端(登录/计算节点)/etc/fstab(示意)
nfs-server:/home  /home  nfs  defaults,_netdev  0  0

no_root_squash 让计算节点的 root 在 NFS 上保留 root 权限,只在完全信任计算节点时使用。若保留 root_squash,Prolog 里以 root 执行 chown 会失败,需要改由用户自己创建目录或用其它机制。

Ceph 集群本身的部署步骤见 cephadm 部署 Ceph 集群;下面只讲 Slurm 节点如何挂载 CephFS。

你的后端是 Ceph,推荐直接挂载 CephFS(无需额外起一台 NFS VM、减少一层协议)。

请务必做到:登录/提交节点与所有计算节点用“相同的挂载路径”(例如统一都用 /home、/projects),以保证作业在任意节点看到的路径一致。

方式 A:直接挂载 CephFS(kernel cephfs)

  1. 在登录/提交节点与所有计算节点安装客户端工具:
sudo apt install -y ceph-common
  1. 在 Ceph 集群上为 HPC 场景创建一个只需最小权限的 cephx 账号(示例:client.hpc),并限制到需要的路径:

可在 Ceph 管理节点执行(按需调整 FS 名称、路径与 pool):

# 创建/获取密钥(示例,实际请根据 Ceph 版本与安全策略调整)
ceph auth get-or-create client.hpc \
    mon 'allow r' \
    mds 'allow r, allow rw path=/home, allow rw path=/projects' \
    osd 'allow rw tag cephfs data=slurmfs' \
    > /etc/ceph/ceph.client.hpc.keyring

将生成的 keyring 安全分发到所有登录/提交节点与计算节点:/etc/ceph/ceph.client.hpc.keyring(权限 600)。内核 CephFS 的 secretfile 要的是 key 本身,不是 keyring 文件,额外导出一份:

ceph auth get-key client.hpc | sudo tee /etc/ceph/ceph.client.hpc.secret
sudo chmod 600 /etc/ceph/ceph.client.hpc.secret

如节点上没有 ceph.conf,也可在 fstab 里直接写监视器 IP 列表。

实测:osd 的 tag cephfs data= 要写 CephFS 名称(本文示例为 slurmfs),不是数据池名。写错时 touch 可能成功,但写数据会报 Operation not permitted。另外 secretfile 需要节点安装 ceph-common(提供 mount.ceph),否则内核会把 secretfile 当成未知参数。

  1. 在所有节点写入 /etc/fstab(示例,使用监视器 IP 列表与 FS 名称 cephfs,挂载到统一的 /cephfs,然后通过 bind-mount 暴露 /home、/projects):
# 直接挂载 CephFS 根到 /cephfs(将 10.1.1.[1-3] 替换为你的 MON 地址;fs=slurmfs 换成你的 CephFS 名称)
10.1.1.1,10.1.1.2,10.1.1.3:/  /cephfs  ceph  name=hpc,secretfile=/etc/ceph/ceph.client.hpc.secret,fs=slurmfs,_netdev,noatime  0  2

# 将 CephFS 子目录绑定到标准路径,保证路径一致
/cephfs/home     /home      none  bind  0  0
/cephfs/projects /projects  none  bind  0  0

也可以直接把 CephFS 挂到 /home(需确保 CephFS 上已有 /home 目录):

10.1.1.1,10.1.1.2,10.1.1.3:/  /home  ceph  name=hpc,secretfile=/etc/ceph/ceph.client.hpc.secret,fs=slurmfs,_netdev,noatime,ms_mode=secure  0  2

挂载生效:

sudo systemctl daemon-reload || true
sudo mkdir -p /cephfs /home /projects
sudo mount -a

注意事项:

  • 路径统一:所有节点使用一致的挂载点(/home、/projects 等)。
  • 账号统一:配合前文的 SSSD/LDAP/SSO,保证 UID/GID 在所有节点一致,否则权限会错乱。
  • 网络与时间:存储流量建议走独立网络;Chrony 已配置对齐时间,避免分布式文件系统报错。
  • 高可用:CephFS 客户端可连接多个 MON,建议写多个 MON 地址;生产可结合多网卡与负载隔离。
  • PAM limits:限制每用户进程数/内存,防止误占。
sudo tee /etc/security/limits.d/login-node.conf >/dev/null <<'LIM'
* soft nproc 4096
* hard nproc 8192
* soft memlock 64
* hard memlock 64
LIM
  • 如需更强约束,可用 systemd resource control(例如将用户会话置于特定 slice)。

在登录节点执行以下命令,确认调度发生在计算节点:

# 查看分区/节点
sinfo

# 交互分配一个 shell(应被分配到 node01/02 等计算节点)
srun -n1 --mem=1G --pty bash -l -c 'hostname; sleep 1'

# 提交批作业
sbatch --wrap 'echo run on $(hostname); sleep 2'
squeue -u $USER

若发现作业在登录节点执行,检查:

  • 登录节点是否误启动了 slurmd(应停用)。
  • slurm.conf 的 NodeName/Partition 是否包含了登录节点(应移除)。

Debian 12 默认使用 cgroup v2(unified hierarchy)。Slurm 22.05+ 原生支持 cgroup v2,无需额外设置兼容模式。

确认当前系统使用的 cgroup 版本:

stat -fc %T /sys/fs/cgroup
# 输出 cgroup2fs 表示 cgroup v2;tmpfs 表示 cgroup v1

/etc/slurm/cgroup.conf:

CgroupMountpoint=/sys/fs/cgroup

# CPU 核绑定 - 防止作业间 CPU 争抢
ConstrainCores=yes

# 内存限制 - 超出分配值的进程将被 OOM Kill
ConstrainRAMSpace=yes

# 设备隔离 - 配合 GRES 限制 GPU 等设备访问
ConstrainDevices=yes

# Swap 控制 - 把作业 cgroup 的 swap 限额压到 0
ConstrainSwapSpace=yes
AllowedSwapSpace=0

说明:

  • ConstrainCores=yes 通过 cgroup 的 cpuset 控制器将作业绑定到分配的 CPU 核上。对科学计算和 MPI 应用,核绑定可避免 NUMA 跨节点访问带来的性能损失。
  • ConstrainRAMSpace=yes 启用内存硬限制。分配的 --mem 值将严格通过 cgroup memory controller 执行,超出的进程被 Kill。这是生产环境中防止单个作业耗尽节点内存的关键保护。
  • ConstrainSwapSpace=yes 配合 AllowedSwapSpace=0 把作业 cgroup 的 swap 限额压到 0;它不阻止内核换出其它进程,必要时再配合 MemorySwappiness。
  • 如需在 cgroup v2 下使用内存压力统计(memory.pressure),需确保内核启用 PSI(Pressure Stall Information),Debian 12 默认已开启。

本文测试的 22.05 + cgroup v2 环境验证了 CPU 和内存限制;ConstrainDevices、ConstrainSwapSpace 在 cgroup v2 下的行为会随 Slurm 版本和内核变化,GPU 设备隔离要在有 GPU 的节点上单独验证。

/etc/slurm/gres.conf(仅设备节点上):

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

slurm.conf 中对应添加:

GresTypes=gpu
NodeName=node01 Gres=gpu:2 CPUs=32 RealMemory=128000 State=UNKNOWN

控制节点:

sudo systemctl enable --now munge
sudo systemctl enable --now slurmctld
# 如启用记账: slurmdbd 已在上文启动

计算节点:

sudo systemctl enable --now munge
sudo systemctl enable --now slurmd

变更配置后:

sudo scontrol reconfigure   # 控制节点执行,使变更生效

scontrol reconfigure 只重读一部分参数。实测 AccountingStorageEnforce、HealthCheckProgram/HealthCheckInterval、SlurmctldHost 这类改动需要重启 slurmctld(必要时重启 slurmd)才生效。

单控制器在 slurmctld 宕机时会导致整个集群无法调度新作业(已运行的作业不受影响)。生产环境建议部署主备 slurmctld 实现自动故障转移。

graph TD
    subgraph HA["slurmctld HA Pair"]
        Primary["controller-01<br/>slurmctld Primary"]
        Backup["controller-02<br/>slurmctld Backup"]
    end
    SharedFS[("StateSaveLocation<br/>Shared Filesystem")]
    Primary --- SharedFS
    Backup --- SharedFS
    Primary -->|"Active"| Nodes["Compute Nodes"]
    Backup -.->|"Standby Failover"| Nodes
    DBD["slurmdbd"] --- Primary
    DBD -.-> Backup

在 slurm.conf 中定义两个 SlurmctldHost,第一个为主,第二个为备:

SlurmctldHost=controller-01(192.0.2.10)
SlurmctldHost=controller-02(192.0.2.11)

关键要求:

  • 共享 StateSaveLocation:主备节点必须能访问同一个 StateSaveLocation 路径(通常为 NFS 或 CephFS 挂载点)。当主节点故障时,备节点通过该路径恢复调度器状态。
  • 相同配置文件:所有节点(包含主备控制器、计算节点、登录节点)的 slurm.conf 内容完全一致。
  • Munge Key 一致:主备控制器与所有节点共享同一 Munge Key。
  • 两台控制器均运行 slurmctld:备节点的 slurmctld 启动后自动进入 standby 状态,不会产生调度冲突。
  • 故障检测超时:默认 SlurmctldTimeout=120 秒。实测从停掉主节点到备节点接管约 2 分 46 秒;调小前先确认共享存储和心跳稳定,避免误切换。
  • slurmdbd 地址:主备控制器的 AccountingStorageHost 要指向同一个 slurmdbd 地址(或 VIP),不要两台都写 localhost,否则备节点接管后记账会失败。slurmdbd.conf 里的 DbdHost 也要写其它节点能访问到的地址,只写 localhost 时 6819 端口只在回环上可达。
# 在主备两台控制器上都启用 slurmctld
sudo systemctl enable --now slurmctld

# 查看配置里的主备地址是否可达
scontrol ping
# 输出示例:
# Slurmctld(primary) at controller-01 is UP
# Slurmctld(backup)  at controller-02 is UP

scontrol ping 显示的是 SlurmctldHost 配置里的主/备角色和可达性,不是当前 active 角色。备节点接管后,它仍然会按配置显示为 backup,不要用这行输出判断谁在干活。

# 在主节点模拟故障
sudo systemctl stop slurmctld   # controller-01

# 默认 SlurmctldTimeout=120 秒,备节点要等主节点超时后才接管。
# 实测从 stop 到备节点日志出现 "Running as primary" 约 2 分 46 秒。
# 可以调小 SlurmctldTimeout,但不要小于心跳和共享存储恢复所需时间。
sleep 180

# 看日志确认备节点是否接管(比 scontrol ping 可靠)
sudo journalctl -u slurmctld -n 30 --no-pager | grep -E 'Running as primary|standby'

# 提交测试作业确认调度正常
srun -n1 hostname

# 恢复原主节点
sudo systemctl start slurmctld  # controller-01
# 实测 controller-01 重启后会重新成为 primary,controller-02 退回 standby;
# 如果希望备节点保持 primary,需要在恢复前手动接管或调整启动顺序。

slurmctld 主备测过之后,数据库层也搭了一套:三个 MariaDB 10.11 + Galera 4 实例,HAProxy 做入口,slurmdbd 主备各连同一个集群。

三个实例都跑在 slurm-auth(10.31.0.243)上,端口 3307/3308/3309。这是为了在现有资源里验证 Galera 和 slurmdbd 故障转移;生产应把三个实例分散到三台机器,并给 HAProxy 配 keepalived/VIP。

每个实例的 my.cnf 关键项(以 3307 为例):

[mysqld]
port=3307
bind-address=127.0.0.1
wsrep_on=ON
wsrep_provider=/usr/lib/galera/libgalera_smm.so
wsrep_cluster_name=slurm_galera
wsrep_cluster_address=gcomm://127.0.0.1:4567,127.0.0.1:4577,127.0.0.1:4587
wsrep_node_name=galera1
wsrep_node_address=127.0.0.1:4567
wsrep_provider_options="gmcast.listen_addr=tcp://127.0.0.1:4567;ist.recv_addr=127.0.0.1:4568"
wsrep_sst_method=rsync
wsrep_sst_receive_address=127.0.0.1:4444
binlog_format=ROW
innodb_autoinc_lock_mode=2

另外两个实例把端口改成 3308/3309,wsrep_node_address、ist.recv_addr 和 wsrep_sst_receive_address 也要成对改开。这里踩过一次坑:ist.recv_addr 和 wsrep_sst_receive_address 用了同一个端口,第二个节点启动时报 Failed to open IST listener ... Address already in use,SST 失败;把 SST 端口单独分出来后恢复正常。

HAProxy 只做 TCP 转发:

frontend galera
    bind 0.0.0.0:3306
    default_backend galera_nodes

backend galera_nodes
    option tcp-check
    server galera1 127.0.0.1:3307 check inter 2s fall 2 rise 2
    server galera2 127.0.0.1:3308 check inter 2s fall 2 rise 2
    server galera3 127.0.0.1:3309 check inter 2s fall 2 rise 2

把原来的 slurm_acct_db 导出后导入 Galera,slurmdbd 改成连 HAProxy:

# /etc/slurm/slurmdbd.conf
DbdHost=controller-01
DbdBackupHost=controller-02
StorageHost=10.31.0.243
StoragePort=3306
StorageUser=slurm
StorageLoc=slurm_acct_db

slurm.conf 里同时指定主备 DBD:

AccountingStorageHost=controller-01
AccountingStorageBackupHost=controller-02

实测结果:

  • 三个实例的 wsrep_cluster_size 都是 3,wsrep_local_state_comment=Synced;导入后 slurm_acct_db 里的 slurm-lab_job_table 有 75 条记录,sacct 能查到升级前后的历史作业。
  • systemctl stop galera@1 后集群剩 2 个节点,HAProxy 把连接切到 3308/3309;此时提交作业 76,sacct 正常记账。systemctl start galera@1 后它通过 SST 重新加入,集群回到 3 个节点。
  • 主 slurmdbd 停掉后,slurmctld 按 AccountingStorageBackupHost 连到 controller-02 上的备份 slurmdbd;提交作业 77,sacct -j 77 仍能查到记录。主 slurmdbd 重新启动后恢复。
  • 备份 slurmdbd 的 DbdHost 必须写它自己的主机名,不能写 IP:写 DbdHost=10.31.0.228 时启动直接报 This host not configured to run SlurmDBD。主机名解析到 IPv6 时再加 DbdAddr=10.31.0.228,用 IPv4 建立连接。
  • 备份节点要装 libmariadb3,并确保 /run/slurmdbd.pid 对 slurm 用户可写;本次一开始备份 slurmdbd 就因为这个 pidfile 权限和缺库起不来。
  • MariaDB 会提示 Database settings not recommended values: innodb_buffer_pool_size innodb_lock_wait_timeout。实验环境没调,生产要按 DBD 的并发量设置,否则 slurmdbd 重启或积压时容易等锁。

slurmdbd 短暂不可用时,slurmctld 会在本地缓存记账消息,恢复后回放;缓存有上限,长时间失联仍可能丢记录。Galera 解决的是数据库节点故障,不替代对 slurmdbd 进程和队列的监控。不用 Galera 的话,也可以参考 MySQL 双主部署 的做法;本文选 Galera 是因为多主写入和成员自动管理更直接。

如果启用了 AccountingStorageEnforce=associations(本文配置启用了),先创建默认账户并关联当前用户,否则 srun 会报 Invalid account or account/partition combination specified:

sudo sacctmgr -i add account users Description="Default account" Organization=local
sudo sacctmgr -i add user "$USER" Account=users
sinfo
scontrol ping
srun -N1 -n1 hostname

cat > sleep.sh <<'EOF'
#!/usr/bin/env bash
#SBATCH -J test
#SBATCH -p debug
#SBATCH -n 1
#SBATCH -o out.%j
echo "hello from $(hostname)"
sleep 5
EOF

sbatch sleep.sh
squeue -u $USER

以下示例帮助你在完成安装后进一步验证调度、资源、QoS 与记账是否工作正常。

# 申请 1 个任务、1 个 CPU、内存 1G,打开交互式 shell(默认分区/默认 QoS)
srun -n1 -c1 --mem=1G --pty bash -l
hostname && echo "CPU: $(nproc)" && exit

预期:进入一个 shell,hostname 输出为分配到的节点;退出后资源释放。

cat > job_simple.sh <<'EOF'
#!/usr/bin/env bash
#SBATCH -J hello
#SBATCH -o hello.%j.out
echo "Hello from $(hostname)"
sleep 3
EOF

sbatch job_simple.sh
squeue -u $USER

预期:作业完成后生成 hello..out,包含 hostname 输出。

cat > job_multinode.sh <<'EOF'
#!/usr/bin/env bash
#SBATCH -J multi
#SBATCH -N 2               # 2 台节点
#SBATCH -n 4               # 4 个任务
#SBATCH -o multi.%j.out
srun -N2 -n4 bash -lc 'echo HOST=$(hostname) TASK=$SLURM_PROCID'
EOF

sbatch job_multinode.sh

预期:输出中能看到 4 行,不同任务的 HOST 可能分布在 2 台节点。

如使用 MPI(OpenMPI/MPICH),通常直接 srun <mpi-program>,由 Slurm 的 PMI/PMIx 启动任务;要用 mpirun 时,先确认 MPI 编译时带 Slurm 支持,避免两层启动器打架。

# 申请超过节点内存尝试(应被拒绝或 Pending)
sbatch --mem=1000G -n1 -J too-much-mem --wrap 'sleep 1'

# 申请指定 CPU 核数与绑定
sbatch -n1 -c4 -J cpu4 --wrap 'echo CPU=$(nproc); lscpu | grep -E "^CPU\(s\)|Thread|Core"'

预期:第一条应排队 Pending 或被拒;第二条在 out 文件中看到绑定情况。

# 使用 short QoS,限制墙时 4 小时(根据你的配置)
sbatch -J qos-short --qos=short --time=01:00:00 --wrap 'sleep 10'

# 若分区限制 MaxTime=04:00:00,提交超过限制应被拒
sbatch -p debug --time=10:00:00 --wrap 'sleep 10'

预期:超过分区 MaxTime 的作业通常在提交时被拒绝。

QoS 的 MaxWall、MaxTRESPU、MaxJobsPerUser 超限一般不是提交被拒,而是作业进入 PENDING,Reason 显示 QOSMaxWallDurationPerJobLimit、QOSMaxCpuPerUserLimit、QOSMaxJobsPerUserLimit 等。用 scontrol show job <jobid> 或 sacct -j <jobid> --format=JobID,State,Reason 查看。

如果节点是 NVIDIA,gres.conf 通常写 File=/dev/nvidia0;这次的测试环境只有 Intel iGPU,用 PVE 的 GVT-g 给计算节点挂了一个 vGPU。

手边没有 NVIDIA GPU,本文没有实测 /dev/nvidia*、NVIDIA 驱动、DCGM 和 nvidia-smi 路径;下面的实测只覆盖 Intel GVT-g。NVIDIA 的写法来自 Slurm 文档,保留作参考,不作为本文的验证结果。

如果要在 PVE 上做 NVIDIA 直通,可参考 PVE GPU 透传实战;这条路径和 GVT-g 不同。

PVE 宿主机(i3-6100 + HD 530)需要在内核命令行打开 GVT-g:

i915.enable_gvt=1 i915.enable_guc=0

确认 mdev 类型和可用实例:

ls /sys/bus/pci/devices/0000:00:02.0/mdev_supported_types/
cat /sys/bus/pci/devices/0000:00:02.0/mdev_supported_types/i915-GVTg_V5_4/available_instances

给 VM 挂 mdev(PVE 9.2):

qm set <vmid> -hostpci0 0000:00:02.0,mdev=i915-GVTg_V5_4

guest 侧要注意 Debian cloud 镜像默认的 cloud-amd64 内核不带 i915,lspci 能看到 HD 530,但 /dev/dri 不出现。装 linux-image-amd64 并用 generic 内核启动后:

sudo apt install -y linux-image-amd64 intel-opencl-icd clinfo clpeak
lspci -nnk -s 00:10.0   # 8086:1912, Kernel driver in use: i915
ls -l /dev/dri           # card1 + renderD128

renderD128 是 root:render 0660,跑 GPU 作业的用户要在 render 组里:

sudo usermod -aG render <user>

组信息要在控制节点和计算节点一致,改完重启 slurmd,否则作业里的 clinfo 会因为拿不到 render 组而看不到设备。

Slurm 侧:

# slurm.conf
GresTypes=gpu
NodeName=node-gpu CPUs=2 RealMemory=1800 Gres=gpu:1 State=UNKNOWN
AccountingStorageTRES=gres/gpu

# /etc/slurm/gres.conf(只放 GPU 节点)
NodeName=node-gpu Name=gpu File=/dev/dri/renderD128

重启 slurmctld 和 slurmd 后,sinfo -o "%N %G" 应显示 gpu:1。提交:

srun -w node-gpu --gres=gpu:1 -n1 bash -lc \
  'echo $SLURM_GPUS_ON_NODE; clinfo -l; clpeak --compute-sp'

实测 SLURM_GPUS_ON_NODE=1,sacct 的 AllocTRES 里有 gres/gpu=1。cgroup.conf 开着 ConstrainDevices=yes 时,不带 --gres 的作业虽然还能 ls /dev/dri,但 clinfo 返回 0 个 platform;带 --gres 的作业才能拿到 OpenCL 设备。

VA-API 在 GVT-g 下没跑通:iHD 和 i965 都在初始化时报 DRM_IOCTL_I915_GEM_APERTURE failed / get chip id failed,所以这里用 OpenCL 测,不把 VA-API 当成已验证能力。glmark2-drm 也没法在无 X 的 guest 里选到 card1,最后用的是 clpeak。

基准数据和混合负载见“实测环境与结果”。

# 查看最近作业的账户/用量
sacct -u $USER --starttime today --format=JobID,JobName,Account,User,Partition,AllocTRES,Elapsed,State

# 管理视角(root 或有权限):
sacctmgr show assoc format=Cluster,Account,User,Partition,FairShare
sacctmgr show qos format=Name,Priority,MaxWall,MaxJobsPU,MaxTRESPU

预期:记录能正常写入,关联、QoS 策略生效。

sinfo -R                 # 查看调度器理由(Reason)
scontrol show nodes      # 节点详细状态
scontrol show partition  # 分区配置

若有异常,结合 /var/log/slurm/slurmctld.log 与各节点的 slurmd.log 排查;必要时 scontrol reconfigure 使配置重新加载。

目标:用户通过 SSO 认证后,能直接 SSH 登录到共享的登录节点(login node),然后在该节点上使用 srun/sbatch 提交作业。

这里有两种做法:

  1. Authentik LDAP Outpost + SSSD(推荐简洁、原生 Linux 体验)
  2. Teleport + Authentik OIDC/SAML(带审计、短期证书、零信任风格;OIDC/SAML 需要 Teleport Enterprise)

选择哪种取决于你是否偏好基于 LDAP/SSH 公钥的传统 Linux 账户,还是希望要更强的会话审计与自动到期证书。

Authentik 这条路径已实际部署 2024.12.3,并用它的 LDAP Outpost 完成 SSSD 认证、Slurm-web 登录和 OOD 的 OIDC 登录。Teleport 18.10 社区版也部署了,本地用户 + MFA + Web 终端可用;但 OIDC connector 在社区版返回 Enterprise-only,详见下文。

前提:在 Authentik 中配置好 LDAP Outpost(提供 LDAP 目录和可选 SSH 公钥属性),向内网暴露 LDAP(s)。

在登录节点(Debian)安装并配置 SSSD:

sudo apt update
sudo apt install -y sssd sssd-ldap libnss-sss libpam-sss

sudo cp /etc/sssd/sssd.conf /etc/sssd/sssd.conf.bak 2>/dev/null || true
cat | sudo tee /etc/sssd/sssd.conf >/dev/null <<'SSSD'
[sssd]
services = nss, pam, ssh
config_file_version = 2
domains = authentik

[domain/authentik]
id_provider = ldap
auth_provider = ldap
chpass_provider = ldap
ldap_uri = ldaps://authentik-ldap.internal:636
ldap_search_base = dc=example,dc=internal
ldap_default_bind_dn = uid=binduser,ou=system,dc=example,dc=internal
ldap_default_authtok = BIND_USER_PASSWORD
ldap_tls_reqcert = demand
cache_credentials = True
enumerate = False
ldap_user_ssh_public_key = sshPublicKey
access_provider = ldap
ldap_access_filter = (memberOf=cn=slurm-users,ou=groups,dc=example,dc=internal)
SSSD

sudo chmod 600 /etc/sssd/sssd.conf
sudo systemctl enable --now sssd

启用 NSS/PAM 集成,让系统识别 LDAP 用户并允许登录:

# Debian 用 pam-auth-update 管理 PAM;不要覆盖 common-session
sudo pam-auth-update --enable mkhomedir

sudo sed -i 's/^#\?\s*UsePAM.*/UsePAM yes/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?\s*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?\s*PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
sudo systemctl reload ssh || sudo systemctl restart ssh

上面的 PasswordAuthentication yes 只在确实需要 password 登录时打开;如果使用 SSH 公钥或 Teleport,保持 no 更安全。

可选:只允许某个组登录(与上文 ldap_access_filter 保持一致),例如:

echo 'AllowGroups slurm-users' | sudo tee -a /etc/ssh/sshd_config
sudo systemctl reload ssh

要点:

  • 将 sssd.conf 中的 ldap_uri、bind DN、BaseDN、证书与组名替换成你 Authentik 的实际配置;若使用自签 CA,安装 CA 到 /usr/local/share/ca-certificates/ 并 update-ca-certificates。
  • 若使用公钥登录,可让 LDAP 暴露 sshPublicKey 属性。SSSD 只提供 sss_ssh_authorizedkeys 查询接口,不会自动写 authorized_keys;sshd 还要配置 AuthorizedKeysCommand /usr/bin/sss_ssh_authorizedkeys 和 AuthorizedKeysCommandUser nobody。
  • 建议把登录节点限制为只跑轻量任务,在 slurm 中配置为提交入口,不计入计算资源。

2026-11 在 10.31.0.243 上部署 Authentik 2024.12.3(Docker 静态包,PostgreSQL/Redis 用 Debian 原生包),bootstrap 管理员账号 akadmin,创建学生用户 stu01 和 students 组。

Authentik 登录页:

Authentik 登录页

图:Authentik 登录页。

管理后台用户列表里能看到 stu01,最后一次登录就是刚才通过 Slurm-web 的 LDAP 认证:

Authentik 用户列表:stu01 最后一次登录来自 Slurm-web

图:Authentik 管理后台用户列表,stu01 的最后登录来自 Slurm-web。

Slurm-web 改用 Authentik LDAP 后,stu01 登录成功:

Slurm-web 用 Authentik 的 stu01 登录后的 Dashboard

图:Slurm-web 使用 Authentik 的 stu01 登录。

关键配置和实测中的问题(Provider/Outpost 在 Authentik 管理后台的 Applications 里创建):

  • LDAP Provider 的 base_dn 设为 dc=example,dc=edu,bind_mode=direct、search_mode=direct、mfa_support=false。
  • authorization_flow 要指向 default-authentication-flow,不是 default-provider-authorization-*。指向授权 flow 时,LDAP bind 会走到未认证用户不适用的 flow,日志报 FlowNonApplicableException,客户端只看到 Invalid credentials。
  • LDAP Outpost 用 ghcr.io/goauthentik/ldap:2024.12.3,环境变量 AUTHENTIK_HOST、AUTHENTIK_TOKEN(outpost token),监听 3389/6636。
  • SSSD 用 LDAPS 6636 + ldap_tls_reqcert=never(测试自签证书);ldap_id_use_start_tls=false 在这个 Authentik 版本上仍会尝试 START TLS,最后改用 LDAPS。
  • Authentik 的 uid 是哈希,登录名在 cn/sAMAccountName;SSSD 里要 ldap_user_name = cn,否则 getent 拿到的是哈希。
  • Slurm-web gateway 的 LDAP 配置改成 Authentik:user_base=ou=users,...、group_base=ou=groups,...、bind_dn=cn=akadmin,...、user_name_attribute=cn、restricted_groups=students。

服务启动方式(镜像可以提前 docker load,不依赖在线拉取):

docker run -d --name authentik-server --network host \
  --env-file /opt/authentik/env -v /opt/authentik/media:/media \
  ghcr.io/goauthentik/server:2024.12.3 server
docker run -d --name authentik-worker --network host \
  --env-file /opt/authentik/env -v /opt/authentik/media:/media \
  ghcr.io/goauthentik/server:2024.12.3 worker

# token 用 LDAP Outpost 详情里的服务账号 token
docker run -d --name authentik-ldap --network host \
  -e AUTHENTIK_HOST=http://10.31.0.243:9000 \
  -e AUTHENTIK_TOKEN=... \
  ghcr.io/goauthentik/ldap:2024.12.3

SSSD 关键配置:

[domain/authentik]
id_provider = ldap
auth_provider = ldap
ldap_uri = ldaps://10.31.0.243:6636
ldap_tls_reqcert = never
ldap_default_bind_dn = cn=akadmin,ou=users,dc=example,dc=edu
ldap_default_authtok = ...
ldap_user_search_base = ou=users,dc=example,dc=edu
ldap_group_search_base = ou=groups,dc=example,dc=edu
ldap_user_object_class = posixAccount
ldap_group_object_class = posixGroup
ldap_user_name = cn
ldap_user_uid_number = uidNumber
ldap_user_gid_number = gidNumber
ldap_user_gecos = displayName
ldap_user_home_directory = homeDirectory
ldap_id_mapping = false
ldap_access_filter = (objectClass=posixAccount)

实测 getent passwd stu01 返回 uid/gid 2005,pamtester login stu01 authenticate 成功;Authentik 用户列表里也能看到这次登录时间。测试环境用 HTTP、bootstrap 口令和自签证书,生产要换 TLS、正式 CA 和 credential管理;LDAP bind 也不要用 akadmin,换成只读服务账号并限制搜索范围。

Teleport 18.10 社区版装在 slurm-auth(10.31.0.243)上,auth 和 proxy 用同一个进程,TLS 用自签证书;登录节点 slurm-build 以 node 角色加入集群。

配置步骤:

  1. 在 Teleport 建角色 slurm-login,允许 user 和 stu01 登录,节点标签放开。
  2. 用 tctl users add 生成邀请链接;本地用户必须设置密码并绑定 TOTP。社区版不能用 second_factor: off 关掉 MFA,tctl 会直接报 cannot disable multi-factor authentication。
  3. 登录节点安装 Teleport,用 tctl tokens add --type=node 生成的 token 注册为 node。
  4. 浏览器登录 Teleport,在 Resources 里打开 slurm-build 的 Web 终端。

关键命令:

# Teleport 服务端
teleport start --config /etc/teleport.yaml

# 建角色和用户
tctl users add labadmin --roles=slurm-login --logins=user,stu01

# 生成节点 token,然后在登录节点启动 node 服务
tctl tokens add --type=node --ttl=1h
teleport start --roles=node --token=<token> \
  --auth-server=10.31.0.243:3025 --insecure

--insecure 只用于自签证书的实验环境;生产要换成正式证书并去掉这个参数。节点注册后,tctl nodes ls 能看到:

Host         ID                  Public Address   Labels      Version
------------ ------------------- ---------------- ----------- ---------
slurm-build  d09e2fe6-e501-42... 10.31.0.229:3022 role=login  18.10.0

Web 资源页和 Web 终端:

Teleport Resources:slurm-build 已注册为 SSH 节点

图:Teleport Web 资源页里的登录节点。

Teleport Web 终端:stu01 在 slurm-build 上执行 sinfo

图:通过 Teleport Web 终端登录 slurm-build;sinfo 返回两个计算节点。

Teleport 默认把 node 会话录到 /var/lib/teleport/log/ 下,本次会话目录约 76 KB,说明录制已经开启;没有打开播放器逐帧检查。

OIDC 的限制:在社区版执行 tctl create 创建 OIDC connector 时返回:

ERROR: OIDC is only available in Teleport Enterprise

所以 Authentik 作为 Teleport 的 OIDC IdP 这条路径没有在社区版跑通,本文的 Teleport 实测是本地用户 + TOTP + Web 终端。要用 Authentik 做 Teleport 的 SSO,需要 Teleport Enterprise,或者选一个仍把 OIDC 放在社区版的旧版本并自行验证。

无论用哪种方式,登录节点统一身份后,用户直接在该节点使用 sacct/squeue/srun/sbatch 即可与 Slurm 交互;配合前文的 AccountingStorageEnforce=associations,limits,qos,可确保只有已关联的用户/账户/QoS 能够正常提交作业。

本节实际部署了 Slurm-web 3.3.0 和 Open OnDemand 4.0.11。Slurm-web 用 LDAP 账号登录,Open OnDemand 用 Authentik OIDC 登录,两套都看到了真实的节点和作业数据。

想在浏览器查看作业、队列、节点状态并进行基本管理,常见的 Web 前端和门户有以下几类:

Slurm-web v3 要求 Slurm >=23.02;本次在 23.11.4 上实测,22.05 不能直接用这个版本。下面先给通用做法,实际配置见下面的“Slurm-web 3.3.0 实测”。

Slurm 提供 REST API(slurmrestd),社区有基于该 API 的 Web UI(常见实现:Slurm-web、Slurm UI)。

步骤概览(控制节点):

sudo apt install -y slurmrestd

# 生成或放置 slurmrestd 运行所需的凭据(基于 Munge 或 Basic/OAuth 反向代理)
# 简单起见,建议放在内网并在反向代理层做认证(如 Nginx+OIDC/Authentik)。

# 以 Unix Socket 运行(推荐与反代配合)
sudo mkdir -p /run/slurm
sudo chown slurm:slurm /run/slurm
sudo tee /etc/systemd/system/slurmrestd.service >/dev/null <<'UNIT'
[Unit]
Description=Slurm REST API Service
After=network.target slurmctld.service

[Service]
User=slurm
Group=slurm
ExecStart=/usr/sbin/slurmrestd unix:/run/slurm/slurmrestd.sock
Restart=on-failure

[Install]
WantedBy=multi-user.target
UNIT

sudo systemctl daemon-reload
sudo systemctl enable --now slurmrestd.service

如果发行版的 slurmrestd 自带 socket activation,不要同时启用它的 .socket 和这里手动绑定的 service,两者会抢同一个 socket 路径。Nginx 以 www-data 运行时,还要让这个用户能读写 /run/slurm/slurmrestd.sock(例如把 www-data 加入 slurm 组,或单独调整 socket 属组)。

然后:

  • 在 Nginx/Caddy 前面挂一个反向代理,将 /api/ 转发到该 Unix Socket。
  • 在反向代理层做认证(推荐与前文的 Authentik OIDC 结合),并将用户身份映射为 HTTP 头给 slurmrestd。
  • 部署 Slurm-web 前端(参考其项目文档)以调用 /api/。

注意:不同的 Slurm-web 前端项目配置略有差异;建议选一个维护活跃、与当前 Slurm 版本兼容的项目。

示例:用 Nginx 反代 slurmrestd(Unix Socket)并用 oauth2-proxy 对接 Authentik OIDC

  1. 安装 oauth2-proxy(示例用二进制方式,替换版本/路径):
sudo useradd -r -s /sbin/nologin oauth2-proxy || true
sudo mkdir -p /etc/oauth2-proxy
curl -L -o /tmp/oauth2-proxy.tar.gz https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v7.6.0/oauth2-proxy-v7.6.0.linux-amd64.tar.gz
tar xzf /tmp/oauth2-proxy.tar.gz -C /tmp
sudo install -m 0755 /tmp/oauth2-proxy-v7.6.0.linux-amd64/oauth2-proxy /usr/local/bin/oauth2-proxy

sudo tee /etc/oauth2-proxy/oauth2-proxy.cfg >/dev/null <<'CFG'
provider = "oidc"
oidc_issuer_url = "https://auth.example.com/application/o/your-authentik/"
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"
cookie_secret = "RANDOM_32B_BASE64=="
email_domains = ["*"]
upstreams = ["file:///dev/null"]
reverse_proxy = true
pass_authorization_header = true
set_xauthrequest = true
skip_provider_button = true
redirect_url = "https://slurm.example.com/oauth2/callback"
whitelist_domains = [".example.com"]
CFG

sudo tee /etc/systemd/system/oauth2-proxy.service >/dev/null <<'UNIT'
[Unit]
Description=oauth2-proxy
After=network-online.target

[Service]
User=oauth2-proxy
Group=oauth2-proxy
ExecStart=/usr/local/bin/oauth2-proxy --config=/etc/oauth2-proxy/oauth2-proxy.cfg
Restart=on-failure

[Install]
WantedBy=multi-user.target
UNIT

sudo systemctl daemon-reload
sudo systemctl enable --now oauth2-proxy
  1. Nginx 站点(/api 直连 slurmrestd 的 Unix Socket,整站走 oauth2-proxy 的 auth_request):
server {
    listen 80;
    server_name slurm.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name slurm.example.com;
    ssl_certificate     /etc/ssl/certs/your.crt;
    ssl_certificate_key /etc/ssl/private/your.key;

    # 与 oauth2-proxy 集成
    location = /oauth2/auth {
        proxy_pass       http://127.0.0.1:4180/oauth2/auth;
        proxy_set_header X-Original-URI $request_uri;
        proxy_set_header X-Real-IP      $remote_addr;
        proxy_set_header X-Scheme       $scheme;
        proxy_set_header X-Auth-Request-Redirect $request_uri;
    }

    location /oauth2/ {
        proxy_pass       http://127.0.0.1:4180;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
    }

    # 保护整个站点
    auth_request /oauth2/auth;
    error_page 401 = @error401;

    location @error401 {
        return 302 /oauth2/start?rd=$scheme://$http_host$request_uri;
    }

    # 前端静态资源(将 root 指向你的 Slurm-web 前端构建目录)
    root /var/www/slurm-web;
    index index.html;

    # 将 /api 转发到 slurmrestd 的 Unix Socket
    location /api/ {
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_pass http://unix:/run/slurm/slurmrestd.sock:/;
    }
}

说明:

  • oauth2-proxy 的 issuer_url、client_id/secret、redirect_url 需在 Authentik 应用中创建并替换。
  • 如需在 Slurm 内部识别用户身份,可在前端调用 slurmrestd 时附带映射的用户名(参考所用前端项目文档);同时将 slurmrestd 仅暴露在本机 Unix Socket,减少越权风险。

2026-11 在 23.11.4 集群上实际部署:Rackslab APT 源安装 slurm-web-agent/slurm-web-gateway 3.3.0,slurmrestd 用 23.11 源码,gateway 接 OpenLDAP。

登录页:

Slurm-web 登录页

图:Slurm-web 登录页。

用 student1(LDAP,Students 组)登录后的 Dashboard:

Slurm-web Dashboard:2 个节点、4 个核、1 个运行作业,登录用户为 Student One

图:LDAP 用户登录后的 Dashboard。

Jobs 页能看到 --gres=gpu:1 的作业处于 RUNNING:

Slurm-web Jobs:student1 的 GPU 作业 RUNNING

图:Jobs 列表里的 GPU 作业。

作业详情里能看到提交命令的 --gres=gpu:1,Requested 和 Allocated 都有 gres: 1:

Slurm-web 作业详情:提交命令带 –gres=gpu:1,Requested/Allocated 都有 gres:1

图:作业详情中的 --gres=gpu:1 和 gres 分配。

实测中的几个问题:

  • slurmrestd 用默认 local auth,不要加 -a rest_auth/jwt。v3 的 agent 通过 Unix socket 以 slurm 用户连接;加 JWT 后页面会报 Unsupported Content-Type for slurmrestd response ... text/plain。
  • Slurm-web 3.3 和 Debian 的 python3-racksdb 0.7 不兼容,agent 会报 Property types is required in schema for object Schema_content;测试里用 RacksDB 0.5 的 Python 包和 0.5 schema,并给 agent 配 PYTHONPATH 和最小 racksdb.yml。
  • gateway 的 LDAP 配置用 uri=ldap://...、user_base、group_base、bind_dn、bind_password、restricted_groups=students;生产应换成 LDAPS 并保护 bind 口令。
  • agent 和 gateway 默认跑 Flask 开发服务器,生产要换 WSGI/HTTPS 反代。
  • 如果启用了 AuthAltTypes=auth/jwt,JWT key 要同步到主备 slurmctld;测试里 ctl2 缺少 /var/spool/slurm/jwt_hs256.key 时直接 fatal,备控制器起不来。

实际配置(节选):

# /etc/slurm-web/agent.ini
[service]
cluster=slurm-lab

[slurmrestd]
socket=/run/slurmrestd/slurmrestd.socket
version=0.0.39

[racksdb]
schema=/opt/racksdb05/schema/racksdb.yml
db=/var/lib/racksdb/racksdb.yml
# /etc/slurm-web/gateway.ini
[service]
interface=0.0.0.0

[ui]
host=http://10.31.0.225:5011

[agents]
url=http://localhost:5012

[authentication]
enabled=yes

[ldap]
uri=ldap://10.31.0.225
user_base=ou=people,dc=example,dc=edu
group_base=ou=groups,dc=example,dc=edu
bind_dn=cn=admin,dc=example,dc=edu
bind_password=...
restricted_groups=students

slurmrestd 的 unit 实测:

[Service]
User=slurm
Group=slurm
Environment=SLURMRESTD_SECURITY=disable_user_check
ExecStart=/usr/sbin/slurmrestd unix:/run/slurmrestd/slurmrestd.socket

源码安装时把 /usr/sbin/slurmrestd 换成实际 prefix 下的路径(测试用 /opt/slurm-23.11/sbin/slurmrestd)。

Open OnDemand 4.0.11 装在 slurm-build(10.31.0.229)上,系统是 Debian 12,Slurm 适配器直接调用 /opt/slurm-23.11/bin。登录走 Authentik 的 OIDC,Dashboard 和 Active Jobs 都能看到真实作业。

安装(Debian 12 / bookworm):

wget -O /tmp/ondemand-release-web_4.0.0-bookworm_all.deb \
  https://apt.osc.edu/ondemand/4.0/ondemand-release-web_4.0.0-bookworm_all.deb
sudo apt install /tmp/ondemand-release-web_4.0.0-bookworm_all.deb
sudo apt update
sudo apt install ondemand libapache2-mod-auth-openidc
sudo a2enmod ssl
sudo a2enmod auth_openidc

本次 OOD 用 HTTPS 443 + 自签证书做实验,生产要换正式证书。/etc/ood/config/ood_portal.yml:

servername: "10.31.0.229"
port: 443
ssl:
  - 'SSLCertificateFile "/etc/ssl/certs/ood.crt"'
  - 'SSLCertificateKeyFile "/etc/ssl/private/ood.key"'
auth:
  - "AuthType openid-connect"
  - "Require valid-user"
oidc_uri: "/oidc"
oidc_provider_metadata_url: "http://10.31.0.243:9000/application/o/ood/.well-known/openid-configuration"
oidc_client_id: "YOUR_CLIENT_ID"
oidc_client_secret: "YOUR_CLIENT_SECRET"
oidc_remote_user_claim: "preferred_username"
oidc_scope: "openid profile email"
user_map_match: ".*"

在 Authentik 里创建 OAuth2/OpenID Provider 时,重定向 URI 要填 https://10.31.0.229/oidc,不是 /oidc/callback。OOD 的 oidc_uri 本身就充当回调路径;实测 OOD 发出的 redirect_uri 是 /oidc,如果 Provider 里填 /oidc/callback,两边对不上,登录会失败。这一步和很多 OIDC 示例不一样。

Slurm 集群配置 /etc/ood/config/clusters.d/debian.yml:

v2:
  metadata:
    title: "Debian Slurm"
  login:
    host: "10.31.0.229"
  job:
    adapter: "slurm"
    cluster: "slurm-lab"
    bin: "/opt/slurm-23.11/bin"

应用配置:

sudo /opt/ood/ood-portal-generator/sbin/update_ood_portal
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl restart apache2

OOD 的本地用户要和 OIDC 里的 preferred_username 对上。本次 stu01 在 OOD 主机上是本地用户(20002:20000),Authentik 的 preferred_username 也是 stu01,映射成功。

Open OnDemand 4.0.11 Dashboard:stu01 通过 Authentik OIDC 登录

图:Open OnDemand 4.0.11 的 Dashboard,登录用户为 stu01。

提交作业后,Active Jobs 能看到作业号、用户、账户、队列和状态:

Open OnDemand Active Jobs:Job Composer 提交的 job 80 已完成

图:OOD Active Jobs 读取 slurm-lab 集群;job 80 通过 Job Composer 提交并完成。

Job Composer 在 HTTP 下会返回 422 InvalidAuthenticityToken:myjobs 的 session cookie 配置是 secure: Rails.env.production?,HTTP 页面里浏览器不会保存这个 Secure cookie,POST 时 CSRF 校验失败。切到 HTTPS 后 Cookie 正常保存,创建 workflow 和提交都成功。默认模板创建出的目录里没有脚本,本次手动放了 main_job.sh(带 #SBATCH -A classa),点 Submit 后 job 80 完成。

Open OnDemand Job Composer:job 80 提交成功

图:OOD Job Composer 提交 main_job.sh 后,job 80 进入队列。

这套监控在一台独立 VM(slurm-auth,10.31.0.243)上跑:Prometheus 2.42.0、Alertmanager 0.25.0、Grafana 13.3.1;Slurm 指标用 SckyzO 的 slurm_exporter 1.8.6,节点指标用 Debian 的 prometheus-node-exporter。

先说选型。本文早先写的 ubccr/slurm-exporter 通过 slurmrestd 的 REST API 取数,最新版按 Slurm REST API v0.0.44 生成客户端;测试集群是 Slurm 23.11.4,REST API 到 v0.0.39,直接使用会打不中版本。最后改用 sckyzo/slurm_exporter:它直接调用 sinfo、squeue、sdiag 等命令,不依赖 REST API 版本,Debian 12 上不需要额外编译。

在控制节点安装 exporter:

curl -L -o /tmp/slurm_exporter.tar.gz \
  https://github.com/sckyzo/slurm_exporter/releases/download/v1.8.6/slurm_exporter-1.8.6-linux-amd64.tar.gz
tar -xzf /tmp/slurm_exporter.tar.gz -C /tmp
sudo install -m 755 /tmp/slurm_exporter /usr/local/bin/slurm_exporter

systemd 单元:

[Unit]
Description=Prometheus Slurm exporter
After=network.target munge.service

[Service]
User=slurm
Group=slurm
ExecStart=/usr/local/bin/slurm_exporter \
  --slurm.bin-path=/opt/slurm-23.11/bin \
  --web.listen-address=0.0.0.0:9341
Restart=on-failure

[Install]
WantedBy=multi-user.target

Prometheus 抓取配置:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - /etc/prometheus/rules/slurm-lab.yml

alerting:
  alertmanagers:
    - static_configs:
        - targets: ["127.0.0.1:9093"]

scrape_configs:
  - job_name: "slurm-exporter"
    static_configs:
      - targets: ["10.31.0.225:9341"]
  - job_name: "node"
    static_configs:
      - targets:
          - "10.31.0.225:9100"
          - "10.31.0.226:9100"
          - "10.31.0.227:9100"

告警规则(测试时把 for 缩到 30 秒,生产可以按需放宽):

groups:
  - name: slurm-lab
    rules:
      - alert: SlurmNodeDown
        expr: sum(slurm_nodes_down + slurm_nodes_drain) > 0
        for: 30s
        labels:
          severity: warning
        annotations:
          summary: "Slurm 节点 DOWN 或 DRAIN"
          description: "{{ $value }} 个节点不可用。"

Alertmanager 接一个 webhook:

route:
  receiver: webhook
  group_wait: 5s
  group_interval: 10s
  repeat_interval: 1m

receivers:
  - name: webhook
    webhook_configs:
      - url: "http://127.0.0.1:9099/alerts"
        send_resolved: true

实测触发:

scontrol update NodeName=node-gpu State=DRAIN Reason=alert-pipeline-test

约 30 秒后 Prometheus 的 SlurmNodeDown 进入 firing,Alertmanager 把通知 POST 到 webhook;恢复节点:

scontrol update NodeName=node-gpu State=RESUME

webhook 收到的 firing 和 resolved 记录都能在 /var/log/alertmanager-webhook.log 里看到。这里也踩到一个坑:先试的是 State=DOWN,但 ReturnToService=2 会让节点在重新注册后自动回到 IDLE,告警来不及稳定触发;改用 DRAIN 后状态保持住,测试才成立。

Prometheus 的告警页:

Prometheus:SlurmNodeDown 处于 firing

图:Prometheus 的 Alerts 页面,SlurmNodeDown 正在 firing。

Grafana 导入 exporter 项目自带的 01-slurm-overview.json,数据源选 Prometheus:

Grafana:Slurm 集群概览,2 个节点、1 个运行作业、1 个 DRAIN

图:Grafana 的 Slurm Cluster Overview;测试时 GPU 节点处于 DRAIN。

Debian 的 prometheus-alertmanager 包不带 Web UI,访问 9093 只会看到“The Debian package … does not include a web application”。要么用 amtool/HTTP API,要么换官方二进制;本文用 API 和 webhook 日志验证告警链,没有把 Alertmanager UI 截图当作结果。

以下示例展示如何用 sacctmgr 配置账户(项目)、用户关联与 QoS 限额。需启用 slurmdbd 与 AccountingStorageEnforce=associations,limits,qos。

  • Account(项目):用于汇总计费/配额的逻辑单位,如 projA。
  • User:系统用户与 Account 关联后才能提交(在 enforce=associations 启用时)。
  • QoS:定义资源与时间等约束,可分配给用户/账户/分区。
# 新建账户(项目)
sudo sacctmgr add account projA Description="Project A" Organization=orgA

# 关联用户到账户(如已有系统用户 alice、bob)
sudo sacctmgr add user alice Account=projA
sudo sacctmgr add user bob   Account=projA

# 可选:设置 Fairshare 权重(影响调度优先级公平性)
sudo sacctmgr modify account projA set fairshare=100
sudo sacctmgr modify user alice set fairshare=50
sudo sacctmgr modify user bob   set fairshare=50

sudo sacctmgr show assoc format=Cluster,Account,User,Partition,FairShare

常见字段:

  • MaxJobsPerUser、MaxJobs、MaxSubmitJobs:并行/排队作业数限制。
  • MaxWall、MaxTRESPU、MaxTRESPA:对资源类型的限额(如 cpu、mem、gres/gpu)。
  • GrpCPUs、GrpNodes、GrpWall:以账户为维度的聚合限制。

示例 1:创建两个 QoS

normal 可能是 Slurm 自动创建的默认 QoS。先 sacctmgr show qos 确认;已存在就用 modify,不存在再用 add。对已存在的 QoS 执行 add 会报 “Nothing new added”,限制也不会写进去。

# 一个短作业 QoS:最多 4 小时,每用户最多并行 10 个作业
sudo sacctmgr add qos short  MaxWall=04:00:00 MaxJobsPerUser=10 Priority=100

# normal 已存在时,用 modify 设置:最多 2 天,每用户最多 3 个作业,最多 64 CPU
sudo sacctmgr modify qos normal set MaxWall=2-00:00:00 MaxJobsPerUser=3 MaxTRESPU=cpu=64 Priority=50

sudo sacctmgr show qos format=Name,Priority,MaxWall,MaxJobsPU,MaxTRESPU

QoS 的时间/资源限制要生效,AccountingStorageEnforce 必须包含 limits,改完要重启 slurmctld(scontrol reconfigure 不一定能刷新这部分)。

示例 2:为账户/用户指定 QoS 默认与可用列表

# 账户 projA 可用 QoS:short、normal;默认使用 normal
sudo sacctmgr modify account projA set qos=normal,short defaultqos=normal

# 若需更细化到用户层:
sudo sacctmgr modify user alice set qos=short,normal defaultqos=short
sudo sacctmgr modify user bob   set qos=normal

示例 3:账户级聚合资源限制(避免项目独占集群)

# 限制 projA 聚合最多同时占用 256 CPU、4 台节点、总墙时 3 天
sudo sacctmgr modify account projA set GrpCPUs=256,GrpNodes=4,GrpWall=3-00:00:00

22.05 里 GrpCPUs=256 会落到 GrpTRES=cpu=256;直接写 GrpTRES=cpu=256 也可以。

GPU(GRES)相关 QoS 限额:

# 限制每用户最多占用 2 张 GPU(假设 GRES 名称为 gpu)
sudo sacctmgr modify qos normal set MaxTRESPU=gres/gpu=2

# 或者为账户维度设置聚合 GPU 上限 8 张
sudo sacctmgr modify account projA set GrpTRES=gres/gpu=8

可以将 QoS 与分区策略配合,例如:

# 在 slurm.conf 中设置分区配额/限制(示意)
PartitionName=debug Nodes=ALL Default=YES MaxTime=04:00:00 State=UP QOS=short

注意:有的站点更倾向不在分区上固定 QOS,而是仅通过 sacctmgr 赋予用户/账户 QoS 列表,以获得更灵活的控制。

sudo scontrol reconfigure
sudo sacctmgr show qos -n
sudo sacctmgr show assoc -n

学校或实验室的典型需求是:每个班级/项目一个 account,学生用 LDAP/SSSD 登录,作业按 account、QoS、partition 隔离,软件环境用 Lmod 管理,存储按用户/项目做配额。Slurm 不提供 LDAP 或 module 系统,这几块要组合起来。

# 每个班级/项目一个 account
sudo sacctmgr -i add account classA Description="Class A" Organization=school
sudo sacctmgr -i add account classB Description="Class B" Organization=school

# 学生关联到 account;一个用户可以属于多个 account
sudo sacctmgr -i add user alice Account=classA
sudo sacctmgr -i add user bob Account=classB

# 公平调度权重
sudo sacctmgr -i modify account classA set fairshare=100
sudo sacctmgr -i modify account classB set fairshare=50

# 账户级资源上限:classA 最多 64 CPU,classB 最多 32 CPU
sudo sacctmgr -i modify account classA set GrpTRES=cpu=64
sudo sacctmgr -i modify account classB set GrpTRES=cpu=32

Slurm 的 account 名大小写不敏感,实际存储会转成小写;AllowAccounts=classA 在 scontrol show partition 里会显示为 classa,不影响匹配。

按班级划分分区,限制可用 account:

# slurm.conf
PartitionName=classA Nodes=node[01-02] AllowAccounts=classA MaxTime=04:00:00 State=UP
PartitionName=classB Nodes=node[03-04] AllowAccounts=classB MaxTime=08:00:00 State=UP

AllowAccounts / AllowGroups 的拒绝不一定发生在 sbatch 提交阶段。实测把 -A classB 的作业提交到 classA 分区时,sbatch 仍然返回 job id,随后作业以 Job's account not permitted to use this partition (...) 结束或被拒绝。排错时看 scontrol show job 的 Reason,不要只看提交返回值。

如果分区 MaxTime 比 Slurm 默认作业时长小,学生脚本又没写 --time,作业会 Pending PartitionTimeLimit(实测)。给学生用的分区要么设置 DefaultTime,要么在作业模板里固定 #SBATCH --time=...。

按班级/项目出用量报表:

# 按账户+用户统计某个月的 CPU 分钟
sreport cluster AccountUtilizationByUser Start=2026-11-01 End=2026-12-01

# 看公平份额和当前用量
sshare -a

Debian 12 自带 lmod(8.6.x),可以直接装:

sudo apt install -y lmod

把 modulefiles 放在共享目录(多节点必须一致),例如 /opt/modulefiles/hello/1.0.lua:

graph LR
    J["学生作业"] --> M["module load"]
    M --> MF["modulefiles<br/>gcc / openmpi / cuda"]
    MF --> SW["共享软件目录 /opt"]
    SW --> C["计算节点"]
help([[hello 1.0]])
whatis("Name: hello")
whatis("Version: 1.0")
prepend_path("PATH", "/opt/hello/1.0/bin")
setenv("HELLO_VERSION", "1.0")

登录节点和计算节点都加一行,让 Lmod 找到本地 modulefiles:

echo 'module use /opt/modulefiles' | sudo tee /etc/profile.d/zz-local-modules.sh

作业里通过 login shell 加载:

srun -N2 -n2 bash -lc 'module load hello/1.0; hello; echo VERSION=$HELLO_VERSION'

实测输出:

hello 1.0 on slurm-node2
hello 1.0 on slurm-node1
VERSION=1.0
VERSION=1.0

Lmod 的几个实践点:

  • modulefiles 放在共享存储或每个节点用配置管理同步,避免节点之间模块版本不一致。
  • 作业脚本用 bash -lc 或显式 source /etc/profile.d/lmod.sh,非登录 shell 不会自动加载 module 函数。
  • 需要固定默认环境时用 LMOD_SYSTEM_DEFAULT_MODULES 或 module save;学生自己 module load 的栈不要写进系统级默认。
  • modulefile 里避免写死节点本地路径;软件装在共享路径,或用 module use 指向共享 modulefiles。

LDAP/SSSD 解决的是“系统用户登录、UID/GID、家目录、SSH 公钥”;Slurm 的 Account/User 关联存在 slurmdbd 的数据库里,是另一套东西。两者要分别配置,再做同步。登录节点的 Authentik LDAP/Teleport 接法见前文“接入单点登录”,这里补多租户相关的 UID/GID、家目录和 Slurm association。

graph LR
    subgraph Identity["身份源"]
        LDAP["LDAP / AD<br/>用户 / 组 / UID/GID / SSH key"]
    end
    subgraph Linux["Linux 节点"]
        SSSD["SSSD<br/>nss + pam + ssh"]
        LOGIN["登录节点 / sshd"]
        COMPUTE["计算节点 / slurmd"]
    end
    subgraph Slurm["Slurm 控制面"]
        DBD["slurmdbd / MariaDB<br/>account association"]
        CTLD["slurmctld<br/>account / QoS / partition"]
    end
    LDAP --> SSSD
    LDAP --> SYNC["同步脚本<br/>LDAP 班级 -> sacctmgr"]
    SSSD --> LOGIN
    SSSD --> COMPUTE
    LOGIN -->|"提交 / 查询"| CTLD
    SYNC --> DBD
    DBD --- CTLD

Debian 12 上安装 SSSD 和 PAM 模块:

sudo apt install -y sssd sssd-ldap libnss-sss libpam-sss libpam-slurm-adopt
sudo pam-auth-update --enable mkhomedir

Debian 没有 authselect(那是 RHEL 系的工具),也不要用 echo ... > /etc/pam.d/common-session 覆盖 PAM 配置;用 pam-auth-update 或 pam-auth-update --enable mkhomedir。

SSSD 的 LDAP 配置示例:

[sssd]
services = nss, pam, ssh
config_file_version = 2
domains = school

[domain/school]
id_provider = ldap
auth_provider = ldap
ldap_uri = ldaps://ldap.example.edu:636
ldap_search_base = dc=example,dc=edu
ldap_default_bind_dn = uid=binduser,ou=system,dc=example,dc=edu
ldap_default_authtok = BIND_USER_PASSWORD
ldap_tls_reqcert = demand
ldap_id_mapping = false
cache_credentials = True
enumerate = False
ldap_user_ssh_public_key = sshPublicKey
access_provider = ldap
ldap_access_filter = (memberOf=cn=students,ou=groups,dc=example,dc=edu)

要点:

  • ldap_id_mapping = false 仅适用于 LDAP 里已经有 POSIX UID/GID 的情况;如果没有,保持默认映射,并确保所有节点用同一个 SSSD 配置,否则同一用户在登录节点和计算节点上的 UID 可能不同。
  • ldap_access_filter 用 memberOf 时,OpenLDAP 要先加载 memberof overlay 并维护组属性;普通 OpenLDAP 没有这个 overlay,可以先用 (objectClass=posixAccount) 验证登录,再换组成员过滤。
  • 家目录放在共享存储,配合 pam_mkhomedir;否则学生第一次登录的节点和作业节点看到的 /home 不一致。
  • 如果 LDAP 提供 SSH 公钥,ldap_user_ssh_public_key 让 SSSD 能查到 key;sshd 还要配 AuthorizedKeysCommand /usr/bin/sss_ssh_authorizedkeys(AuthorizedKeysCommandUser nobody),否则公钥不会自动生效。没有 LDAP 公钥时用配置管理分发 authorized_keys。
  • 计算节点加 libpam-slurm-adopt,并在 /etc/pam.d/sshd 里加 account required pam_slurm_adopt.so;同时 slurm.conf 要设 PrologFlags=Contain,否则作业没有 extern step,有作业的用户也会被拒绝。session 栈里还要禁用 pam_systemd,否则会话会被 systemd 放进自己的 scope,不在作业 cgroup 里。实测无作业拒绝、有 batch 作业时 SSH 会话进入 job_<id>/step_extern cgroup,细节见“安全加固”一节。

新生开户不要在每台节点上 useradd,直接在 LDAP 里加 posixAccount 条目。下面是一个最小 LDIF(uidNumber 和 gidNumber 要和学校的号段规划一致):

dn: uid=stu01,ou=people,dc=example,dc=edu
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: stu01
cn: Student 01
sn: 01
givenName: Student
uidNumber: 20002
gidNumber: 20000
homeDirectory: /home/stu01
loginShell: /bin/bash
mail: [email protected]
userPassword: {SSHA}...

userPassword 用 slappasswd -s '初始口令' 生成后填进去,再用管理员 DN 导入:

LDAPTLS_REQCERT=never ldapadd -x -H ldaps://ldap.example.edu:636 \
  -D cn=admin,dc=example,dc=edu -W -f stu01.ldif

-W 会交互式提示 bind 口令,不要把管理员口令写进脚本或 shell history。LDAPTLS_REQCERT=never 只用于自签证书的测试环境;生产环境把 CA 装到系统信任库,去掉这个变量。

LDAP 里加完后,配置了 SSSD 的节点 getent passwd stu01 就能看到用户;第一次 SSH 登录时 pam_mkhomedir 会按 /etc/skel 建家目录。2026-11 的测试里新建 stu01(uid 20002)后没有在任何节点执行 useradd,直接用 LDAP 密码 SSH 登录控制节点成功,家目录自动创建为 stu01:students。

如果 getent 暂时看不到新用户,可能是 SSSD 缓存了之前的失败查询;执行 sudo sss_cache -E 或等缓存过期再试。

计算节点要看到同一个家目录,仍然需要共享存储(NFS 或 CephFS);测试集群最初只在登录节点建了家目录,srun 到 node1 时就报了 couldn't chdir to /home/stu01,回退到 /tmp。

Slurm 没有内置的 LDAP 后端,LDAP 用户不会自动变成 sacctmgr association。常见做法是用脚本从 LDAP/教务系统导出“用户-班级”关系,再调用 sacctmgr 同步:

# 示例:从 CSV(user,account)同步 association;已存在时会提示,可按需忽略
while IFS=, read -r user account; do
    sudo sacctmgr -i add user "$user" Account="$account"
done < users.csv

实测:LDAP 里刚建完 stu01 时直接 srun 会报 Invalid account or account/partition combination specified(因为启用了 AccountingStorageEnforce=associations);执行 sacctmgr -i add user stu01 Account=classA 后立即可以提交。生产环境把“LDAP 建用户”和“sacctmgr 加 association”放进同一个开户脚本,离校时两处都删。

2026-11 用 OpenLDAP + LDAPS 自签证书在测试集群上做了端到端验证:LDAP 里新建用户后,getent passwd、pamtester login(LDAP password认证)、SSH 登录、家目录自动创建、srun/sbatch 和 sacct 都通过。测试环境为了省事用了 ldap_tls_reqcert = never;生产环境换成 demand 并安装正式 CA 证书。

Slurm 的 QoS 只限制 CPU、内存、GPU 和作业数,不限制磁盘。学生场景要单独做:

  • 家目录/项目目录放共享存储(CephFS、NFS 或并行文件系统),用文件系统配额或项目配额限制用量。
  • scratch 目录按作业或用户定期清理,不要和家目录混在一起。
  • 配额告警接入监控;配额满了作业会以各种“看起来像 Slurm 的问题”失败,排错时先看 df -h、quota 或 CephFS quota。
  1. LDAP/教务系统创建用户和组,确认 UID/GID、家目录、SSH 公钥。
  2. sacctmgr 创建 account 和 association,设置 fairshare、QoS、GrpTRES。
  3. 共享存储创建家目录/项目目录,设置配额。
  4. 用测试账号跑 sinfo、srun、sbatch,确认分区和 account 权限。
  5. 离校时禁用 LDAP、移除 association、归档/清理家目录和配额。

Prolog 和 Epilog 是 Slurm 在每个作业启动前和结束后自动在计算节点上执行的脚本,用于环境准备与清理。这是生产环境中保证节点状态干净、防止作业间相互影响的关键机制。

在 slurm.conf 中配置:

Prolog=/etc/slurm/prolog.sh
Epilog=/etc/slurm/epilog.sh
# PrologSlurmctld 和 EpilogSlurmctld 在控制节点执行,用于集群级别的前后处理
# PrologSlurmctld=/etc/slurm/prolog_ctld.sh
# EpilogSlurmctld=/etc/slurm/epilog_ctld.sh
#!/usr/bin/env bash
# /etc/slurm/prolog.sh - 在计算节点上以 root 身份执行
set -euo pipefail

JOBID="${SLURM_JOB_ID:-unknown}"

# 为当前作业创建隔离的临时目录
JOB_TMPDIR="/tmp/slurm-job-${JOBID}"
mkdir -p "${JOB_TMPDIR}"
chown "${SLURM_JOB_UID}:${SLURM_JOB_GID}" "${JOB_TMPDIR}"
chmod 700 "${JOB_TMPDIR}"

# 检查关键文件系统是否挂载
if ! mountpoint -q /home; then
    logger -t slurm-prolog "ERROR: /home not mounted for job ${JOBID}"
    exit 1
fi

# 检查 GPU 驱动状态(如有 GPU)
if command -v nvidia-smi &>/dev/null; then
    if ! nvidia-smi &>/dev/null; then
        logger -t slurm-prolog "ERROR: nvidia-smi failed for job ${JOBID}"
        exit 1
    fi
fi

logger -t slurm-prolog "Prolog completed for job ${JOBID} user ${SLURM_JOB_USER}"
exit 0
#!/usr/bin/env bash
# /etc/slurm/epilog.sh - 在计算节点上以 root 身份执行
set -euo pipefail

JOBID="${SLURM_JOB_ID:-unknown}"

# 只清理本作业创建的临时目录
rm -rf "/tmp/slurm-job-${JOBID}" 2>/dev/null || true

# 作业进程由 proctrack/cgroup 负责清理;这里不要 pkill -9 -u,
# 那会误杀同一用户在节点上的其它作业。
logger -t slurm-epilog "Epilog completed for job ${JOBID}"
exit 0

注意:

  • Prolog 脚本返回非零值会导致作业无法在该节点运行,节点将被标记为 DRAIN 状态。利用这一特性可以实现节点级别的准入检查。
  • 脚本权限必须为 755,属主 root:root。
  • 脚本内避免耗时操作(如网络请求),Prolog 执行时间过长会延迟作业启动。
  • 生产环境建议将 Prolog/Epilog 的日志写入 syslog 或独立日志文件,便于事后排查。
  • Epilog 只按作业 ID 清理;不要 pkill -u $SLURM_JOB_USER 或删除整个用户的 /dev/shm,那会误杀同一用户在节点上的其它作业。孤儿进程交给 proctrack/cgroup 处理。
  • Prolog 里的 mountpoint -q /home 假设 /home 是共享挂载;如果家目录就在根分区,这项检查会误判,改成检查你的实际挂载点或删掉。
  • Prolog 里用 SLURM_JOB_UID:SLURM_JOB_GID,不要用 user:user。LDAP 用户的主组可能是 students 而不是同名组;实测写成同名组会 chown 失败,Prolog 失败并把节点 DRAIN。

Slurm 支持定期在计算节点上执行健康检查脚本。注意它只负责运行脚本,不会因为脚本返回非零就自动 DRAIN;节点状态要由脚本自己调用 scontrol update 来改。在 slurm.conf 中配置:

HealthCheckProgram=/etc/slurm/health_check.sh
HealthCheckInterval=300
HealthCheckNodeState=ANY
  • HealthCheckInterval=300:每 5 分钟执行一次检查。
  • HealthCheckNodeState=ANY:对所有状态的节点都执行检查;也可设为 IDLE 仅检查空闲节点。

修改 HealthCheckProgram/HealthCheckInterval 后,实测需要重启 slurmctld 才能让新配置生效;scontrol reconfigure 在这组参数上不一定够。

#!/usr/bin/env bash
# /etc/slurm/health_check.sh
set -euo pipefail

HOSTNAME=$(hostname -s)
ERRORS=()

# 1. 检查关键文件系统
for mp in /home /projects; do
    if [[ -d "${mp}" ]] && ! mountpoint -q "${mp}" 2>/dev/null; then
        ERRORS+=("${mp} not mounted")
    fi
done

# 2. 检查磁盘空间(根分区使用率超过 95% 视为异常)
for mp in / /tmp; do
    usage=$(df --output=pcent "${mp}" 2>/dev/null | tail -1 | tr -d '% ')
    if [[ -n "${usage}" ]] && (( usage > 95 )); then
        ERRORS+=("${mp} disk usage ${usage}%")
    fi
done

# 3. 检查内存(可用内存低于 512MB 视为异常)
avail_kb=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
if (( avail_kb < 524288 )); then
    ERRORS+=("available memory ${avail_kb}KB too low")
fi

# 4. 检查 GPU 状态(如有)
if command -v nvidia-smi &>/dev/null; then
    if ! nvidia-smi --query-gpu=gpu_bus_id --format=csv,noheader &>/dev/null; then
        ERRORS+=("nvidia-smi query failed")
    fi
    # 检查不可纠正的 ECC 错误
    ecc_errors=$(nvidia-smi --query-gpu=ecc.errors.uncorrected.aggregate.total \
        --format=csv,noheader,nounits 2>/dev/null | awk '{s+=$1}END{print s}')
    if [[ -n "${ecc_errors}" ]] && (( ecc_errors > 0 )); then
        ERRORS+=("GPU uncorrected ECC errors: ${ecc_errors}")
    fi
fi

# 5. 检查 Munge 认证
if ! munge -n 2>/dev/null | unmunge &>/dev/null; then
    ERRORS+=("munge auth failed")
fi

# 6. 检查网络连通性(到控制节点)
if ! timeout 3 bash -c 'echo > /dev/tcp/controller-node/6817' 2>/dev/null; then
    ERRORS+=("cannot reach slurmctld on port 6817")
fi

if (( ${#ERRORS[@]} > 0 )); then
    reason=$(IFS='; '; echo "${ERRORS[*]}")
    # 关键:HealthCheckProgram 不会自动 DRAIN,必须显式调用 scontrol
    scontrol update NodeName="${HOSTNAME}" State=DRAIN Reason="${reason}"
    logger -t slurm-health "UNHEALTHY ${HOSTNAME}: ${reason}"
    echo "UNHEALTHY: ${reason}"
    exit 1
fi

exit 0

示例假设 /home、/projects 是独立挂载点;如果它们在根分区,mountpoint 会失败并把节点 DRAIN,按实际挂载点改或删掉这段。

脚本返回非零值本身不会改变节点状态;上面的示例在返回前显式执行了 scontrol update NodeName=... State=DRAIN,节点才会被移出调度池。修复问题后需手动恢复:

sudo scontrol update NodeName=node01 State=RESUME Reason=""

这一节是管理员每天会用的操作。下面的命令在 Debian 12 + Slurm 22.05 的测试集群上跑过,节点名、分区名按你的环境替换。

# 把节点从调度池里拿出来,写清楚原因
sudo scontrol update NodeName=node01 State=DRAIN Reason="kernel upgrade"

# 看节点状态和原因
sinfo -R
scontrol show node node01

# 等节点上的作业跑完(急事可以 scancel/requeue)
squeue -w node01

# 维护完成,恢复调度
sudo scontrol update NodeName=node01 State=RESUME Reason=""

实测 State=DRAIN 后新作业不会再落到该节点,RESUME 后恢复 IDLE。节点如果因为 Prolog 失败或健康检查失败自己进了 IDLE+DRAIN,先看 Reason 再处理。

sudo scontrol update PartitionName=debug State=DOWN
sudo scontrol update PartitionName=debug State=UP

分区 DOWN 后不再接受新作业,已经在跑的作业不受影响,适合做整个分区的维护窗口。

# 看队列和等待原因
squeue
squeue -t PD -o "%.10i %.9P %.12j %.8u %.2t %.10M %.20R"

# 挂起、恢复、重排、取消
sudo scontrol hold <jobid>
sudo scontrol release <jobid>
sudo scontrol requeue <jobid>
sudo scancel <jobid>
sudo scancel -u <user>

实测对运行中的作业执行 hold 后,状态还是 RUNNING,Reason=JobHeldAdmin;release 后恢复;requeue 后重新排队,可能显示 PENDING BeginTime。普通用户只能取消自己的作业。

sudo sacctmgr show assoc format=Cluster,Account,User,QOS,GrpTRES
sudo sacctmgr add user <user> Account=<account>
sudo sacctmgr remove user where name=<user> account=<account>
sudo sacctmgr modify account <account> set GrpTRES=cpu=64

毕业/离校时先移除 association,再禁用 LDAP;不要只删系统用户,Slurm 里可能还留着 association。

新增计算节点:

  1. 装 slurmd slurm-client munge libmunge2,同步 /etc/slurm/slurm.conf 和 munge key。
  2. 在 slurm.conf 的 NodeName / PartitionName 里加节点。
  3. scontrol reconfigure;结构性变更可能需要重启 slurmctld 和 slurmd。
  4. systemctl enable --now slurmd,用 sinfo 确认节点变成 IDLE。

下线节点:先 DRAIN,等作业排空,systemctl stop slurmd,再从配置里删掉,scontrol reconfigure。不要直接关机了事。

Slurm 升级不是 apt upgrade 之后重启就完事,slurmdbd 的数据库 schema、slurmctld 的状态文件、slurmd 的版本兼容都要考虑。下面是一个保守的维护窗口流程。

  1. 提前通知用户,把分区 DOWN 或节点 DRAIN,停止新作业。
  2. 让运行中的作业跑完;急事可以 scontrol requeue 或 scancel。
  3. 备份 slurmdbd 数据库、/etc/slurm、/etc/munge/munge.key 和 StateSaveLocation。
  4. 升级 slurmdbd,启动并确认 schema 升级成功。
  5. 升级 slurmctld,再升级 slurmd;顺序不要反。
  6. 用 sinfo、scontrol ping、srun 验证,恢复分区和节点。
# 先备份数据库和配置
sudo mysqldump --single-transaction slurm_acct_db | gzip > /backup/slurm-acct-$(date +%F).sql.gz
sudo tar czf /backup/slurm-etc-$(date +%F).tar.gz /etc/slurm /etc/munge/munge.key

# 控制节点
sudo apt update
sudo apt install --only-upgrade slurmdbd slurmctld slurm-client
sudo systemctl restart slurmdbd
sleep 3
sudo systemctl restart slurmctld

# 计算节点
sudo apt install --only-upgrade slurmd slurm-client
sudo systemctl restart slurmd

版本兼容按 SchedMD 的升级说明来:slurmdbd 先于 slurmctld,slurmctld 先于 slurmd。不确定时先在一套测试集群上升级。

apt install --only-upgrade 只在同一个大版本内安全。跨大版本时,先看 slurmdbd 数据库里的转换版本:

sudo mysql -N -e "select version from convert_version_table" slurm_acct_db

Debian 12 的 22.05.8 是 12。新版 slurmdbd 启动时只接受 MIN_CONVERT_VERSION 以上的旧库;25.05.9 的 MIN_CONVERT_VERSION 是 14,直接拿 22.05 的库启动会 fatal:

fatal: Database schema is too old for this version of Slurm to upgrade.

中间要经过 23.11(CONVERT_VERSION=14,MIN_CONVERT_VERSION=11):22.05(v12) → 23.11(v14) → 25.05(v16)。每一跳只启动 slurmdbd,让它把 schema 转完,再停掉换下一跳的二进制。转换前备份数据库;schema 升级不可逆,回滚只能恢复备份。实测这三步的 convert_version_table 分别是 12、14、16,23.11 和 25.05 的 slurmdbd 日志都打出了 Conversion done: success!。Debian 12 仓库只有 22.05,23.11 需要从源码或其它仓库取;测试里是在单独的构建 VM 上编译的。

SelectType=select/cons_res 在 25.05 已经移除,升级前改成 select/cons_tres;SelectTypeParameters=CR_CPU_Memory 可以保留。slurmctld 读不懂旧的 StateSaveLocation 时会拒绝启动,日志里能看到 incompatible version, got 9728 need >= 10240 <= 11008;确认没有运行中作业后,用 slurmctld -i 忽略读不懂的状态文件(-c 则是完全不恢复)。

# 在计算节点上验证 slurmd 能解析配置并打印节点信息
sudo /usr/sbin/slurmd -f /etc/slurm/slurm.conf -C

# 比较所有节点的配置是否一致
md5sum /etc/slurm/slurm.conf

22.05 没有 slurmctld -t 这种只检查配置的模式;改完配置先在测试节点或测试集群启动验证,或者 scontrol reconfigure 后看 slurmctld.log / slurmd.log。

  • 保留旧版本 .deb 或 apt 缓存,必要时 apt install slurm-wlm=<version>。
  • slurmdbd 的数据库 schema 升级通常不可逆;回滚前先确认有备份,最稳的是恢复数据库备份加旧版本二进制。
  • StateSaveLocation 也要一起备份,否则控制器回滚后可能读不懂新状态。

Slurm 自带的状态命令适合人工看,长期监控要接 Prometheus/Grafana。先分清三层:节点、调度器、作业和账户。

# 队列和节点
sinfo
sinfo -R
squeue -t PD -o "%.10i %.9P %.12j %.8u %.2t %.10M %.20R"
scontrol show node
scontrol show partition

# 调度器
sdiag
scontrol show daemons
sacctmgr show stats

# 作业和账户
sacct -a -X --format=JobID,JobName,User,Account,State,Elapsed,AllocTRES
sreport cluster AccountUtilizationByUser Start=2026-11-01 End=2026-12-01
sshare -a
sprio

实测 sdiag、sacctmgr show stats、scontrol show daemons 可用;scontrol show stats 在 22.05 不存在,会报 invalid entity:stats,不要写进监控脚本。

层面 指标 建议
slurmctld 进程、6817 端口、scontrol ping、sdiag 调度周期 控制器不可达、调度周期持续升高告警
slurmd 节点状态、sinfo -R、scontrol show node DOWN/DRAIN 突增告警
队列 pending 作业数、最老 pending 时间、Reason 分布 排队时间超过基线告警
记账 slurmdbd 6819、sacctmgr show stats、数据库磁盘 DBD 不可用、队列积压告警
节点 CPU、内存、磁盘、网络(node_exporter) 常规容量告警
GPU DCGM exporter:温度、ECC、显存 GPU 掉卡、ECC 告警
身份 LDAP/SSSD、munge、SSH 登录/认证失败突增告警
  • 节点指标用 node_exporter;GPU 用 dcgm-exporter(手边没有 NVIDIA 硬件,DCGM 未实测)。
  • Slurm 指标用 sckyzo/slurm_exporter(直接调 CLI)或 ubccr/slurm-exporter(走 REST,注意 API 版本);本集群的实测组合是前者 + node_exporter,接 Prometheus 和 Grafana,见“Web 界面”一节。
  • 告警用 Alertmanager 接 webhook;Debian 的 alertmanager 包不带 Web UI,可用 amtool 或 HTTP API。
  • 日志用 Promtail/Loki 或 Filebeat 收集 slurmctld.log、slurmd.log、slurmdbd.log,至少对 error / fatal 做告警。
  • 不要只盯 GPU 利用率;队列等待时间、节点 DRAIN 原因、slurmdbd 队列积压往往更早暴露问题。
sudo tail -f /var/log/slurm/slurmctld.log
sudo tail -f /var/log/slurm/slurmd.log
sudo tail -f /var/log/slurm/slurmdbd.log
sudo scontrol setdebug debug2   # 临时
sudo scontrol setdebug info     # 恢复

SlurmctldDebug / SlurmdDebug 长期开 debug2 会迅速放大日志;用 logrotate 或 journald 限制大小,排障完恢复 info。

Slurm 的配置文件要全节点一致,靠手工 scp 迟早会漂移。推荐把 /etc/slurm 放进 Git,用 Ansible 或脚本分发;密钥和数据库 password 不要进 Git。

  • slurm.conf、cgroup.conf、prolog.sh、epilog.sh、health_check.sh 放一个仓库。
  • gres.conf 可以按节点不同,单独模板;slurm.conf 保持完全一致。
  • munge.key、slurmdbd.conf 的 StoragePass、LDAP bind password 用 Ansible Vault、SOPS 或外部 credential管理,不提交明文。
# 所有节点比较 md5;不一致会被 slurmctld 警告
md5sum /etc/slurm/slurm.conf

# 计算节点验证 slurmd 能解析配置
sudo /usr/sbin/slurmd -f /etc/slurm/slurm.conf -C

# 配置变更后
sudo scontrol reconfigure
# 结构性变更(节点、分区、SlurmctldHost)重启 slurmctld/slurmd

slurmctld 日志里出现 “appears to have a different slurm.conf” 时,先 diff 配置文件;确认是有意为之再加 DebugFlags=NO_CONF_HASH,不要用这个参数掩盖漂移。

  1. 在 Git 里改配置,跑 slurmd -C 或测试集群验证。
  2. 备份当前 /etc/slurm 和数据库。
  3. 维护窗口分发到所有节点,scontrol reconfigure 或重启。
  4. 用 sinfo、scontrol ping、srun 验证;失败按备份回滚。
数据 路径/来源 频率 说明
slurm.conf 及相关配置 /etc/slurm/ 每次变更后 建议纳入版本控制(Git)
Munge Key /etc/munge/munge.key 初始化后一次 丢失后所有节点需重新分发
slurmdbd 数据库 MariaDB slurm_acct_db 每日 包含全部记账、用户关联、QoS
StateSaveLocation /var/lib/slurm-llnl/ 实时(共享 FS) slurmctld 重启时依赖此目录恢复状态
#!/usr/bin/env bash
# /etc/cron.daily/slurm-db-backup
set -euo pipefail

BACKUP_DIR="/backup/slurm"
KEEP_DAYS=30
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p "${BACKUP_DIR}"

# 逻辑备份(适合中小规模)
mysqldump --single-transaction --routines --triggers \
    slurm_acct_db | gzip > "${BACKUP_DIR}/slurm_acct_db_${DATE}.sql.gz"

# 备份配置文件
tar czf "${BACKUP_DIR}/slurm_config_${DATE}.tar.gz" \
    /etc/slurm/ /etc/munge/munge.key 2>/dev/null || true

# 清理过期备份
find "${BACKUP_DIR}" -name "*.gz" -mtime +${KEEP_DAYS} -delete

logger -t slurm-backup "Backup completed: ${BACKUP_DIR}/slurm_acct_db_${DATE}.sql.gz"
# 1. 先停掉控制器和记账服务
sudo systemctl stop slurmctld slurmdbd

# 2. 清空并恢复数据库(先确认备份文件可用)
sudo mysql -e "DROP DATABASE IF EXISTS slurm_acct_db; CREATE DATABASE slurm_acct_db;"
gunzip < /backup/slurm/slurm_acct_db_YYYYMMDD.sql.gz | sudo mysql slurm_acct_db

# 3. 恢复配置
sudo tar xzf /backup/slurm/slurm_config_YYYYMMDD.tar.gz -C /

# 4. 重启服务(按顺序)
sudo systemctl restart munge
sudo systemctl restart slurmdbd
sleep 3
sudo systemctl restart slurmctld

建议将 /etc/slurm/ 纳入 Git 管理:

cd /etc/slurm
sudo git init
# slurmdbd.conf 里有 StoragePass,不要提交;密钥文件同理
printf 'slurmdbd.conf\n*.key\n' | sudo tee .gitignore >/dev/null
sudo git add -A
sudo git commit -m "initial slurm config"

# 每次变更后
sudo git add -A && sudo git commit -m "description of change"
sudo git log --oneline

配合 Ansible/Puppet 等配置管理工具分发到所有节点,确保一致性。

  • 最小权限:登录节点不运行 slurmd;计算节点不开放 SSH 给普通用户(或仅在有运行作业时允许)。
  • 网络隔离:管理网络(Slurm 通信)与存储网络分离;如条件允许,计算网络(MPI)也独立。
  • 认证加固:Munge Key 权限 400,仅 munge 用户可读;定期轮换。

轮换 key 要在维护窗口做:先停 slurmctld 和各节点 slurmd,换完 key 再启动,避免运行中的作业认证失败。

# 在控制节点生成新 Key
sudo /usr/sbin/mungekey --force

# 分发到所有节点(使用配置管理工具或 scp)
for node in node01 node02 node03 node04 login-node; do
    sudo scp /etc/munge/munge.key "${node}":/etc/munge/munge.key
    ssh "${node}" 'sudo chown munge:munge /etc/munge/munge.key && sudo chmod 400 /etc/munge/munge.key && sudo systemctl restart munge'
done

# 最后重启控制节点 munge
sudo systemctl restart munge

可通过 Slurm PAM 模块限制只有在该节点上有运行作业的用户才能 SSH 登录。Debian 12 需要单独安装模块:

sudo apt install -y libpam-slurm-adopt

在计算节点的 /etc/pam.d/sshd 中添加:

account    required     pam_slurm_adopt.so

只加 PAM 这一行还不够。pam_slurm_adopt 在注册会话时找的是作业的 extern step;如果 slurm.conf 里没有启用容器,batch 作业只有 batch step,没有 extern step,模块会把正在跑作业的用户也当成“没有活动作业”拒绝。还需要:

ProctrackType=proctrack/cgroup
PrologFlags=Contain
LaunchParameters=use_interactive_step

Contain 会在作业分配时用 ProcTrack 插件在分配的节点上建作业容器,并隐式打开 Alloc。改完重启 slurmctld 和所有 slurmd,新提交的作业才会带 extern step。LaunchParameters=use_interactive_step 只影响 srun --pty 的交互式步骤,不能替代 Contain。

pam_systemd 会和 pam_slurm_adopt 冲突:登录时它把会话放进 user.slice/user-<uid>.slice/session-<n>.scope,模块即使放行,进程也不在作业 cgroup 里。按 Slurm 文档,要在 sshd 或 common-session 的 session 栈里注释掉它:

# /etc/pam.d/common-session
# session optional pam_systemd.so

如果需要 /run/user/$UID 或 linger,可以在 Prolog/Epilog 里对该用户执行 loginctl enable-linger / disable-linger,并自行导出 XDG_*。

生产环境建议把失败策略收紧,默认配置在模块自身出错时可能放行未受限会话:

account required pam_slurm_adopt.so action_adopt_failure=deny action_generic_failure=deny

模块版本要和 slurmd 一致。实测拿 Debian 的 22.05 pam_slurm_adopt.so 去连 25.05 的 slurmd,即使作业有 extern step 也会被判定“没有活动作业”;换成 25.05 源码 contribs/pam_slurm_adopt 编译的模块后才正常。升级 Slurm 时把 libpam-slurm-adopt 一起升级,或者从同版本源码编译。

实测(Debian 12,22.05.8 和 25.05.9):只加 PAM 时,无作业 SSH 会被拒绝,但有 batch 作业的用户也被拒绝;加上 PrologFlags=Contain 并重启后,scontrol show step <jobid> 能看到 <jobid>.extern。25.05 上保留 pam_systemd 时,SSH 能登录但 cgroup 是 user.slice/.../session-<n>.scope;注释掉 pam_systemd 后,会话落在 .../job_<id>/step_extern/user/task_0。作业结束后同一用户再 SSH 返回 Access denied by pam_slurm_adopt: you have no active jobs on this node。

文件/目录 权限 属主
/etc/munge/munge.key 0400 munge:munge
/etc/slurm/slurm.conf 0644 root:root
/etc/slurm/slurmdbd.conf 0600 slurm:slurm
/etc/slurm/cgroup.conf 0644 root:root
/etc/slurm/prolog.sh 0755 root:root
/etc/slurm/epilog.sh 0755 root:root
/etc/slurm/health_check.sh 0755 root:root
/var/lib/slurm-llnl/ 0755 slurm:slurm
/var/spool/slurmd/ 0755 slurm:slurm
/var/log/slurm/ 0755 slurm:slurm

在 slurm.conf 中可调整的关键调度参数:

# 调度器运行间隔和 backfill 策略
SchedulerParameters=default_queue_depth=128,bf_continue,bf_interval=30,bf_max_job_test=1200,bf_resolution=600,bf_window=4320

参数说明:

参数 默认值 建议值 说明
default_queue_depth 100 128 每次调度周期评估的作业数上限
bf_continue 禁用 启用 backfill 不因找到一个可运行作业就停止
bf_interval 30 30 backfill 调度间隔(秒)
bf_max_job_test 500 1200 backfill 单次最多尝试的作业数
bf_resolution 60 600 backfill 时间分辨率(秒)
bf_window 1440 4320 backfill 向前看的时间窗口(分钟)

当集群超过 50 个节点或并发作业数频繁超过 1000 时,控制节点需要适当调优:

# 增大消息超时(默认 10 秒)
MessageTimeout=30

# 批作业启动超时
BatchStartTimeout=30

# 作业完成后等待时间(秒,避免快速回收导致不一致)
CompleteWait=10

slurmdbd 的 MariaDB 在长期运行后表会膨胀,建议定期归档和优化:

# 归档超过 6 个月的作业记录。Events/Jobs/Steps 要显式写,
# 否则带 Purge* 的调用会删除数据而不归档。
sacctmgr archive dump Directory=/backup/slurm/archive/ Events Jobs Steps \
    PurgeEventAfter=6months \
    PurgeJobAfter=6months \
    PurgeStepAfter=6months

# 表名带集群前缀,先列出实际名字再优化(建议低负载时段执行)
mysql -N -e "SHOW TABLES FROM slurm_acct_db LIKE '%job_table';"
mysql -e "OPTIMIZE TABLE \`slurm_acct_db\`.\`debian-cluster_job_table\`, \`slurm_acct_db\`.\`debian-cluster_step_table\`;"

对于多路 CPU 服务器,NUMA 感知调度通常能给计算密集型作业带来性能收益:

# slurm.conf
SelectType=select/cons_tres
SelectTypeParameters=CR_Core_Memory
TaskPlugin=task/affinity,task/cgroup

在提交作业时使用绑定选项:

# 绑定到 socket(每个 NUMA 域)
srun --cpu-bind=socket -n4 ./my_app

# 绑定到核心
srun --cpu-bind=cores -n4 ./my_app
flowchart TD
    A["Problem Detected"] --> B{"scontrol ping OK?"}
    B -->|"No"| C["Check slurmctld service<br/>and logs"]
    B -->|"Yes"| D{"Node State?"}
    D -->|"DOWN/DRAIN"| E["scontrol show node<br/>check Reason field"]
    D -->|"IDLE but job pending"| F["Check partition state<br/>sinfo, scontrol show part"]
    D -->|"UNKNOWN"| G["Check slurmd + munge<br/>DNS/hosts resolution"]
    E --> H{"Munge OK?"}
    H -->|"No"| I["Verify munge.key<br/>time sync, permissions"]
    H -->|"Yes"| J["Check slurm.conf<br/>node definition match"]
    F --> K{"Resources available?"}
    K -->|"No"| L["Wait or adjust<br/>resource limits"]
    K -->|"Yes"| M["Check QoS/association<br/>sacctmgr show assoc"]

Munge 认证失败

# 检查 key 一致性(在两个节点分别执行,比较 md5)
md5sum /etc/munge/munge.key

# 检查权限
ls -la /etc/munge/munge.key
# 预期:-r-------- 1 munge munge

# 检查时间偏差(超过 300 秒会失败)
date +%s   # 在各节点比较 UNIX 时间戳

# 查看 munge 日志
journalctl -u munge --no-pager -n 50

节点状态异常(DOWN/DRAIN/UNKNOWN)

# 查看节点详情和原因
scontrol show node node01

# 常见原因及处理:
# "Not responding" -> 检查 slurmd 是否运行、网络是否通
# "Low RealMemory" -> slurm.conf 中 RealMemory 大于节点实际可用内存,需调小
# "Kill task failed" -> Epilog 或 cgroup 清理失败,检查 Epilog 脚本

# 恢复节点
sudo scontrol update NodeName=node01 State=RESUME Reason=""

作业持续 Pending 不运行

# 查看作业等待原因
scontrol show job <jobid> | grep Reason

# 常见 Reason:
# Priority        -> 有更高优先级的作业排队,等待即可
# Resources       -> 请求的资源当前不可用
# QOSMaxJobsPerUserLimit -> 超出 QoS 限制的并发作业数
# AssocMaxJobsLimit      -> 超出账户关联的作业限制
# InvalidAccount  -> 用户未关联到有效的 Account
# InvalidQOS      -> 用户无权使用请求的 QoS

# 检查用户关联
sacctmgr show assoc user=$USER format=Cluster,Account,User,QOS

记账/slurmdbd 不可用

# 检查 slurmdbd 服务状态
sudo systemctl status slurmdbd --no-pager

# 检查数据库连接
mysql -u slurm -p -e "SELECT 1;" slurm_acct_db

# 检查端口监听
ss -tlnp | grep 6819

# 查看 slurmdbd 日志
sudo tail -100 /var/log/slurm/slurmdbd.log

配置变更未生效

# 在控制节点执行配置重载
sudo scontrol reconfigure

# 如果 reconfigure 不够(如修改了 node 数量等结构性变更),需重启
sudo systemctl restart slurmctld

# 验证配置是否已加载
scontrol show config | grep '参数名'
组件 日志路径 说明
slurmctld /var/log/slurm/slurmctld.log 调度器主日志,首要排查对象
slurmd /var/log/slurm/slurmd.log 计算节点日志,作业执行问题排查
slurmdbd /var/log/slurm/slurmdbd.log 记账守护进程日志
munge /var/log/munge/munged.log 认证相关问题

调试时可临时提高日志级别:

# 临时调高 slurmctld 日志级别(不需要重启)
sudo scontrol setdebug debug2

# 恢复默认级别
sudo scontrol setdebug info

下面这组结果是 2026-11 在一套 PVE 9.2 集群上实际跑出来的。

节点 角色 配置
slurm-ctl slurmctld primary + slurmdbd + MariaDB 2 vCPU / 2 GB,Debian 12.6
slurm-ctl2 slurmctld backup + slurmdbd backup 2 vCPU / 2 GB,Debian 12.6
slurm-node1 slurmd 2 vCPU / 2 GB,Debian 12.6
slurm-node2 slurmd + GVT-g vGPU 2 vCPU / 2 GB,Debian 12.6
slurm-build 独立登录节点 + OOD 2 vCPU / 4 GB,Debian 12.6
slurm-auth Authentik + Teleport + Prometheus/Grafana + 3×Galera 2 vCPU / 3 GB,Debian 12.6

Slurm 初始版本是 Debian 12 仓库的 22.05.8-4+deb12u3,cgroup v2(cgroup2fs),主备共用 NFS 上的 StateSaveLocation,Munge 和 slurm.conf 全节点一致。跨版本升级另外在一台构建 VM 上编译了 23.11.4 和 25.05.9;Web UI 和 Authentik 联调在升级到 23.11.4 后进行。GPU 测试给 slurm-node2 挂了 GVT-g vGPU;登录节点/OOD 在 slurm-build;Authentik、Teleport、Prometheus/Grafana 和三个 Galera 实例在 slurm-auth。Galera 三个实例跑在同一台 VM 上,是为了在现有资源里验证故障转移,生产要分散到三台机器。

验证过的点:

  • 多节点调度:srun -N2 -n4 hostname 在 slurm-node1 / slurm-node2 上各跑到 2 个任务。
  • cgroup v2 内存限制:--mem=100M 的作业内尝试分配 300 MB,作业以 OUT_OF_MEMORY 结束,ExitCode=0:125。
  • CPU 绑定:srun -c1 内 Cpus_allowed_list: 0,-c2 为 0-1,说明 ConstrainCores=yes 生效。
  • 记账与报表:sacct 能查到已完成作业的 JobID、State、NodeList、Elapsed;sreport cluster AccountUtilizationByUser 和 sshare -a 能按账户输出用量/公平份额。
  • QoS:MaxWall、MaxTRESPU、MaxJobsPerUser 超限时作业进入 PENDING,Reason 分别为 QOSMaxWallDurationPerJobLimit、QOSMaxCpuPerUserLimit、QOSMaxJobsPerUserLimit;不是提交阶段直接报错。
  • limits 的必要性:只写 AccountingStorageEnforce=associations,qos 时,5 小时作业能绕过 4 小时的 QoS MaxWall;加上 limits 并重启 slurmctld 后才进入 Pending。
  • 主备切换:默认 SlurmctldTimeout=120,从停掉 primary 到 backup 日志出现 Running as primary 约 2 分 46 秒;scontrol ping 仍按配置显示 primary/backup,不能用来判断当前 active 角色。原 primary 重启后会重新成为 primary。
  • Prolog/Epilog:按 job ID 创建的 /tmp/slurm-job-<id> 在作业运行期间存在,Epilog 后删除;没有使用 pkill -u 这类会误杀同用户其它作业的写法。
  • 健康检查:脚本返回非零本身不会 DRAIN;脚本里显式执行 scontrol update NodeName=$(hostname -s) State=DRAIN Reason=... 后,节点才进入 IDLE+DRAIN。
  • Lmod:Debian 12 的 lmod 8.6.19 可用;srun -N2 -n2 bash -lc 'module load hello/1.0; hello' 在两个计算节点都加载到同一个 module。
  • 多租户:classA / classB account、AllowAccounts 分区、GrpTRES 限额都生效;错误 account 提交到分区后以 Job's account not permitted to use this partition (...) 结束,超过账户 GrpTRES 的作业 Pending,Reason 为 AssocGrpCpuLimit。
  • LDAP/SSSD:OpenLDAP + LDAPS 自签证书;在 LDAP 里新建 stu01(不在节点上 useradd),getent passwd、pamtester login(LDAP password认证)、SSH password登录、pam_mkhomedir 自动建家目录、srun/sbatch、sacct 全部通过。刚建完 LDAP 用户时 srun 报 Invalid account or account/partition combination specified,sacctmgr add user stu01 Account=classA 后正常。Prolog 用 SLURM_JOB_UID/GID 才适配 LDAP 的主组 students。
  • CephFS:测试集群的 CephFS(本文示例名 slurmfs)建 client.slurm,三个节点通过 fstab 挂载 /cephfs,srun -N2 跨节点写同一个目录成功。secretfile 需要装 ceph-common,OSD cap 用 tag cephfs data=<文件系统名>,写错时小文件能建、写数据报 EPERM。
  • GPU/GRES 与基准:PVE 9.2 的 Intel GVT-g 把 HD 530 以 i915-GVTg_V5_4 挂给 slurm-node2,Slurm 显示 Gres=gpu:1,--gres=gpu:1 作业的 AllocTRES 包含 gres/gpu=1;不带 --gres 时 clinfo 看不到 OpenCL 设备。clpeak、sysbench、mbw、stress-ng、HPCC 的实际数据和混合负载见“CPU、GPU 与混合负载基准”。
  • Slurm-web:Slurm 23.11.4 + Slurm-web 3.3.0,从 Rackslab 源安装,认证接 OpenLDAP;Dashboard、Jobs 和作业详情都能看到节点、作业和 --gres=gpu:1 的 Requested/Allocated,截图见“Web 界面”的“Slurm-web 3.3.0 实测”。
  • Authentik:Authentik 2024.12.3 的 LDAP Outpost 实测,SSSD 用 LDAPS + ldap_user_name=cn 能 getent 和 pamtester;Slurm-web 改指向 Authentik LDAP 后,stu01 登录成功,Authentik 用户列表能看到这次登录。关键坑是 LDAP Provider 的 authorization_flow 必须用 authentication flow,不能用 authorization flow。
  • 独立登录节点:slurm-build(10.31.0.229)只装客户端和 munge;stu01 执行 sinfo、srun -A classa -n1 hostname(返回 slurm-node1)、sbatch --chdir=/tmp(job 74 COMPLETED)正常。默认在 /home/user 提交时 job 73 因输出文件无权限失败;客户端版本要和控制器保持一致,本次用的是 23.11 的客户端。
  • Galera + slurmdbd 主备:3 个 MariaDB 10.11/Galera 4 实例 + HAProxy;停掉 galera@1 后集群剩 2 节点,job 76 仍能记账;恢复后回到 3 节点。停掉主 slurmdbd 后,slurmctld 走 AccountingStorageBackupHost,job 77 由备份 dbd 记录,sacct -j 77 可查。
  • Teleport:Teleport 18.10 CE,本地用户 labadmin 完成密码 + TOTP,slurm-build 以 node 角色注册;Web 终端以 stu01 登录并执行 sinfo,会话日志落在 /var/lib/teleport/log/。Teleport 18 CE 的 OIDC connector 返回 OIDC is only available in Teleport Enterprise,Authentik OIDC 没有跑通。
  • Open OnDemand:OOD 4.0.11 + Authentik OIDC,stu01 登录 Dashboard,Active Jobs 看到 Job Composer 提交的 job 80(COMPLETED)。Job Composer 在 HTTP 下因 Secure session cookie 不保存报 422,切到 HTTPS 后创建并提交成功;默认模板目录没有脚本,手动补了 main_job.sh。
  • Prometheus/Grafana/Alertmanager:SckyzO slurm_exporter 1.8.6 + node_exporter,Prometheus 2.42 + Alertmanager 0.25 + Grafana 13.3.1;DRAIN GPU 节点后 SlurmNodeDown firing,webhook 收到 firing/resolved,Grafana 概览显示 2 节点、1 个运行作业、1 个 DRAIN。State=DOWN 会因 ReturnToService=2 自动回 IDLE,测试改用 DRAIN。
  • pam_slurm_adopt:只加 PAM account 行时,有 batch 作业也会被拒绝;加上 PrologFlags=Contain 并重启后,作业出现 .extern step。25.05 上还必须用同版本编译的 PAM 模块,并禁用 pam_systemd,否则会话落在 session-<n>.scope 而不是 job_<id>/step_extern;作业取消后同一用户 SSH 返回“no active jobs”。
  • 跨版本升级:22.05.8 的 convert_version_table 是 12,直接启动 25.05.9 的 slurmdbd 报 Database schema is too old for this version of Slurm to upgrade.(25.05 的 MIN_CONVERT_VERSION=14)。用 23.11.4 的 slurmdbd 先转到 14,再用 25.05.9 转到 16。25.05 的 slurmctld 读 22.05 的 StateSaveLocation 报 assoc_usage state ... got 9728 need >= 10240 <= 11008,加 -i 放弃旧状态后启动,恢复 0 节点/0 作业;之后 sinfo、srun -N2 -n2、sbatch、sacct 正常,旧的记账记录还在。22.05 的 srun 连 25.05 控制器会报 Invalid Protocol Version 11008,客户端要一起升级;SelectType=select/cons_res 也要先改成 select/cons_tres,25.05 不再提供 cons_res 插件。
  • 日常管理:节点 DRAIN/RESUME、分区 DOWN/UP、scontrol hold/release/requeue 都验证过;hold 后 Reason=JobHeldAdmin,requeue 后显示 PENDING BeginTime。
  • 监控/配置:sdiag、sacctmgr show stats、scontrol show daemons、slurmd -C 可用;scontrol show stats 在 22.05 不存在。

测试工具和输入都来自 Debian 12 仓库或对应项目的标准输入,版本和参数固定:

  • clpeak 1.1.2:OpenCL 设备带宽、单/双/半精度和整数吞吐、kernel 延迟。
  • sysbench 1.0.20:CPU 质数计算、内存写带宽。
  • mbw:memcpy / dumb / mcblock 三种内存拷贝。
  • stress-ng 0.15.06:2 个 CPU stressor 的 bogo ops/s。
  • hpcc 1.5.0:HPL、STREAM、DGEMM、FFT 等 HPC Challenge 测试;输入用 /usr/share/doc/hpcc/examples/_hpccinf.txt 改成 N=2048, NB=64, P=2, Q=1,跑 2 个 MPI 进程。

CPU 单机结果:

测试 结果
sysbench CPU,1 线程 429.07 events/s
sysbench CPU,2 线程 797.57 events/s
sysbench memory,1 MiB block,10 GiB write 17085.28 MiB/s
mbw MEMCPY / DUMB / MCBLOCK 6245.9 / 9234.2 / 12643.6 MiB/s
stress-ng,2 CPU,20s 2589.90 bogo ops/s

HPCC(N=2048,2 MPI 进程):

项目 结果
HPL 3.501 Gflops
STREAM Copy / Scale / Add / Triad 17.97 / 14.18 / 15.62 / 17.16 GB/s
Single DGEMM 2.053 Gflop/s
Single FFT 3.302 Gflop/s

GVT-g vGPU(i915-GVTg_V5_4,512 MB aperture,23 个 compute units,1050 MHz):

项目 结果
clpeak 单精度 323.09 GFLOPS
半精度 638.76 GFLOPS
双精度 81.39 GFLOPS
整数 107.73 GIOPS
全局内存带宽 float / float4 18.43 / 19.04 GB/s
transfer write / read 10.01 / 9.12 GB/s
kernel launch latency 1209.86 us

多节点 CPU 和混合负载:

  • srun -N2 -n2 -c1 两个节点各跑 sysbench:slurm-node1 429.27 events/s,slurm-node2 433.06 events/s。
  • sbatch -c2 --gres=gpu:1 同时跑 sysbench(1 线程)和 clpeak --global-bandwidth --compute-sp:CPU 从单跑的 429.07 降到 233.46 events/s,GPU 仍有 322.68 GFLOPS、19.43 GB/s;作业的 AllocTRES 是 cpu=2,gres/gpu=1,mem=1800M,说明 CPU 和 GPU 资源都按请求分配了。
  • GPU-only 的 clpeak --all-tests 作业跑了 2 分 49 秒,sacct 的 AllocTRES 包含 gres/gpu=1;同一节点不带 --gres 的作业 clinfo 看不到 platform。

这些数字是 2 vCPU 虚拟机 + 512 MB vGPU 的小规模结果,HPL 的 N=2048 也远小于正式 HPL 调优规模,不能用它推断物理机的峰值;但用来验证“GRES 分配、设备隔离、CPU/GPU 并发时的相对变化”足够。

没有做端到端验证的部分:

  • NVIDIA GPU 路径:手边没有 NVIDIA 硬件,/dev/nvidia*、NVIDIA 驱动、DCGM 和 nvidia-smi 都没有实测,文章里的 NVIDIA 写法仅作参考。
  • Teleport 的 Authentik OIDC/SAML:Teleport 18.10 社区版返回 OIDC is only available in Teleport Enterprise;本地用户 + TOTP + Web 终端已测,OIDC 这条没跑通。
  • OOD 的交互式应用(Jupyter、VS Code 等)没有配置;Dashboard、Active Jobs 和 Job Composer 提交已测。