跳转至

cgroup限制slurm申请资源

在集群上,有极少部分用户作业管理系统提交作业申请的核数和输入文件的核数不一致,特别是输入文件写的核数大于作业申请的核数,这样容易使节点CPU过载。这种情况需要在Slurm作业系统启用cgroup插件控制核数资源。

Cgroup模式

slurm支持cgroup的2种模式,一种是传统模式(cgroup v1),一种是统一模式(cgroup v2)。在RHEL/Rocky 8系统中,默认启用v1,在RHEL/Rocky 9中默认启用v2。

# 判断系统中Cgroup的模式
mount |grep cgroup
# 如果输出cgroup2,代表使用统一模式
# 如果输出tmpfs,代表使用传统模式

在RHEL/Rocky 8系统中启用Slurm的cgroup插件

slurm.conf和cgroup.conf配置文件设置

[root@quantum ~]# grep -i cgroup /etc/slurm/slurm.conf 
TaskPlugin=task/cgroup,task/affinity
ProctrackType=proctrack/cgroup
JobAcctGatherType=jobacct_gather/cgroup

[root@quantum ~]# cat /etc/slurm/cgroup.conf
CgroupAutomount=yes  # 在24.05版本后,这个参数废弃了
ConstrainCores=yes
ConstrainSwapSpace=yes
AllowedSwapSpace=0
ConstrainRAMSpace=yes
AllowedRAMSpace=95
slurm.conf和cgroup.conf按上面配置好后,用户通过srun和sbatch提交作业的核数会被限制到申请的核数。

启用pam_slurm_adopt插件

如果还要限制salloc+ssh这种运行作业的核数限制,以及防止用户在用sbatch提交后再ssh到分配的节点用命令提交其他任务,需要启用pam_slurm_adopt插件模块。
在slurm.conf中必须添加PrologFlags=contain,这将设置一个"extern"步骤来追踪ssh发起的进程。
在计算节点/etc/pam.d/sshd的最后面添加1行:

account    required     pam_slurm_adopt.so
这样就可以限制ssh到计算节点发起进程的CPU核数。
如果想对某些user放行,不管是在计算节点上有没有作业,都可以ssh到计算节点上。
account    sufficient   pam_listfile.so item=user sense=allow onerr=fail file=/path/to/allowed_users_file
account    required     pam_slurm_adopt.so
把要放行的user放在/path/to/allowed_users_file文件里,每个user一行。

在RHEL/Rocky 9系统中启用Slurm的cgroup插件

slurm.conf和cgroup.conf配置文件设置

[root@quantum ~]# egrep -i 'cgroup|PrologFlags' /etc/slurm/slurm.conf
TaskPlugin=task/cgroup,task/affinity
ProctrackType=proctrack/cgroup
JobAcctGatherType=jobacct_gather/cgroup
PrologFlags=Alloc,Contain

[root@quantum ~]# cat /etc/slurm/cgroup.conf
CgroupAutomount=yes  # 在24.05版本后,这个参数废弃了
CgroupMountpoint=/sys/fs/cgroup
CgroupPlugin=cgroup/v2
ConstrainCores=yes
ConstrainDevices=yes
ConstrainRAMSpace=yes
ConstrainSwapSpace=yes
slurm.conf和cgroup.conf按上面配置好后,用户通过srun和sbatch提交作业的核数会被限制到申请的核数。

启用pam_slurm_adopt插件

跟RHEL/Rocky 8系统中启用pam_slurm_adopt插件一样,但配置好了,怎么也限制不了ssh发起进程的核数。
在RHEL/Rocky 9系统,cgroup默认挂载是v2,/sys/fs/cgroup/system.slice/slurmstepd.scope为slurm的cgroup路径;而sshd的cgroup路径为/sys/fs/cgroup/system.slice/sshd.service,并没有包含到slurm的cgroup路径中,且sshd及bash的PID也没包含在slurm的cgroup路径下的cgroup.procs中。最终导致限制不了用户经ssh发起进程的核数。
如果只是想使用salloc占有和使用资源,可在slurm.conf配置文件中加上LaunchParameters=use_interactive_step,这样使用salloc时将自动启用Interactive步骤发起srun进程,在分配的节点上启动终端。也就是这时跟使用srun --pty bash差不多,直接进入分配的节点。只不过srun是用的extern步骤,而salloc是用的interactive步骤。

PAM执行脚本绑定用户进程PID

脚本思路分析

在Cgroup v2模式下,通过上面的方法,只要不用ssh进入节点发起作业进程,都是能限制核数资源的。但只要从管理/登录节点ssh到作业分配的节点,这种ssh进程如果再使用命令就限制不了核数资源。
为了解决用户经ssh发起进程的CPU核数资源限制问题,我们需要把ssh进入到节点的shell进程绑定到slurm的cgroup中。
思路:只要在slurm.conf配置文件中加入PrologFlags=contain,就会有extern步骤,那我们就需要把ssh进入到节点的当前shell进程及父进程PID绑定到extern步骤的cgroup.procs里面。但cgroup.procs的权限是root,不可能管理员每次都用root账号手动把PID写进去。所以这里,我们就借用PAM的pam_exec.so来执行脚本,且可以用root权限执行。

绑定用户ssh进程PID的脚本

bind_sshd_to_slurm_cgroup.sh
#!/bin/bash

# 清理环境变量
unset PATH LD_LIBRARY_PATH
export PATH=/bin:/usr/bin

# 获取当前登录用户(通过PAM_USER环境变量)
USER_NAME=${PAM_USER:-$(id -un)}
USER_ID=$(id -u "$USER_NAME")

# 如果是 root 或系统用户 (UID < 1000),直接退出
if [ "$USER_ID" -lt 1000 ]; then
  exit 0
fi

# 获取当前节点的主机名
NODE_NAME=$(hostname)

# 获取用户在当前节点上的所有作业信息,并按 CPU 核数降序排序
# squeue 输出格式:JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
JOB_INFO=$(squeue -u $USER_NAME -h -o "%i %N %C" |grep "$NODE_NAME" |sort -k3,3nr)

# 检查是否有匹配的作业
#if [ -z "$JOB_INFO" ]; then
#  echo "No active jobs found for user $USER_NAME on node $NODE_NAME"
#  exit 1
#fi

# 获取CPU核数最多的作业 ID
TARGET_JOB_ID=$(echo "$JOB_INFO" |head -n1 |awk '{print $1}')
MAX_CPUS=$(echo "$JOB_INFO" |head -n1 |awk '{print $3}')

# 构建cgroup路径
CGROUP_PATH="/sys/fs/cgroup/system.slice/slurmstepd.scope/job_$TARGET_JOB_ID/step_extern/user/task_0"

# 检查路径是否存在
if [ ! -d "$CGROUP_PATH" ]; then
  echo "Cgroup path $CGROUP_PATH does not exist!"
  exit 1
fi

# 将当前shell的PID及父进程PID绑定到cgroup
echo $(awk '/PPid/ {print $2}' /proc/$$/status) >> "$CGROUP_PATH/cgroup.procs"
echo $$ >> "$CGROUP_PATH/cgroup.procs"

# 记录日志
LOG_FILE="/var/log/slurm-cgroup-bind.log"
echo "$(date): Bound user $USER_NAME (UID=$USER_ID) to cgroup $CGROUP_PATH for job $TARGET_JOB_ID with $MAX_CPUS CPUs" >> $LOG_FILE
上面的USER_NAME不能直接用id -un来获得,因为是root权限执行,那得到的USE_NAME就是root,要获得普通用户的USE_NAME,需要用$PAM_USER变量才行。
把脚本放在/usr/local/bin下,并赋予可执行权限chmod 711 /usr/local/bin/bind_sshd_to_slurm_cgroup.sh

PAM配置

/etc/pam.d/sshd
#--保留文件原来的内容--
account    required     pam_slurm_adopt.so
session    optional     pam_exec.so /usr/local/bin/bind_sshd_to_slurm_cgroup.sh
这里其实不使用pam_slurm_adopt.so也行,但需要把PAM执行脚本中“检查是否有匹配的作业”的条件段的注释打开,且把optional改为required,一样能阻止用户ssh到没作业的节点。
如果不用pam_slurm_adopt.so,用户ssh到没有作业的节点,会提示:
/usr/local/bin/bind_sshd_to_slurm_cgroup.sh failed: exit code 1
这种提示不够优雅,所以我加上了pam_slurm_adopt.so,这样用户ssh到没有作业的节点,会提示:
Access denied by pam_slurm_adopt: you have no active jobs on this node
需要注意的是,在脚本中echo以及>&2甚至> /dev/$(tty)这些,都不能把消息传递给用户登录的shell终端,因为PAM阶段执行的脚本通常是在用户shell启动之前执行的,现在是阻止了shell登录,所以消息传递不到。

验证

阻止用户ssh到没有他/她作业的节点,并提示消息

[userA@quantum ~]$ qa
 JOBID NAME           USER ST         TIME TIME_LIMIT   NODES   CPUS    PARTITION NODELIST(REASON)
[userA@quantum ~]$ ssh cu01
Access denied by pam_slurm_adopt: you have no active jobs on this node
Connection closed by 10.1.255.1 port 22
salloc申请1核作业,然后ssh到分配节点,在shell终端用命令行提交个2核CPU的作业
[userA@quantum ~]$ salloc -N1 -n1
salloc: Granted job allocation 28
salloc: Nodes cu01 are ready for job
[userA@quantum ~]$ qa
 JOBID NAME           USER ST         TIME TIME_LIMIT   NODES   CPUS    PARTITION NODELIST(REASON)
    28 interactive   userA  R         0:07 UNLIMITED        1      1       public cu01
[userA@quantum ~]$ ssh cu01
Last login: Mon Feb 24 22:48:50 2025 from 10.1.255.1
[userA@cu01 ~]$ stress-ng --cpu 2 --timeout 120 --metrics-brief
然后马上在用分配节点的root账号,top命令查看CPU占用,
[root@cu01 ~]# top -n 1
top - 14:29:40 up 3 days, 41 min,  2 users,  load average: 0.92, 0.34, 0.12
Tasks: 182 total,   3 running, 179 sleeping,   0 stopped,   0 zombie
%Cpu(s): 50.0 us,  3.1 sy,  0.0 ni, 46.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   3625.4 total,   2249.9 free,    675.1 used,   1007.7 buff/cache
MiB Swap:   4096.0 total,   4096.0 free,      0.0 used.   2950.3 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                                    
  25417 userA     20   0   46524   6248   4096 R  50.0   0.2   0:03.01 stress-ng-cpu                                                              
  25418 userA     20   0   46524   6248   4096 R  50.0   0.2   0:03.01 stress-ng-cpu

# 从top命令可看出,用户提交了2核CPU作业,但是由于salloc申请的1核CPU,受cgroup限制,所以每个stress-ng线程只能使用50%,总和为1核。

[root@cu01 ~]# pstree -p |egrep '^systemd|slurm|sshd'
systemd(1)─┬─ModemManager(870)───{ModemManager}(927)
           ├─slurmd(25153)
           ├─slurmstepd(5404)
           ├─slurmstepd(25164)─┬─sleep(25168)
           │                   ├─{slurmstepd}(25165)
           │                   ├─{slurmstepd}(25166)
           │                   ├─{slurmstepd}(25167)
           │                   ├─{slurmstepd}(25336)
           │                   └─{slurmstepd}(25341)
           └─sshd(14330)─┬─sshd(25238)───sshd(25256)───bash(25257)───pstree(25421)
                         └──sshd(25337)───sshd(25359)───bash(25360)───stress-ng(25416)─┬─stress-ng-cpu(25417)
                                                                                       └─stress-ng-cpu(25418)

# 从上面的PID树,可看出,sshd发起的进程PID并没有包含到slurmstepd步骤里

[root@cu01 ~]# cat /sys/fs/cgroup/system.slice/slurmstepd.scope/job_28/step_extern/user/task_0/cgroup.procs 
25168
25337
25359
25360
25416
25417
25418

# 通过PAM执行脚本,把sshd(25337), sshd(25359), bash(25360)以及其子进程PID都写入extern步骤user作业的cgroup.procs里

[root@cu01 ~]# systemctl status slurmstepd.scope 
● slurmstepd.scope
     Loaded: loaded (/run/systemd/transient/slurmstepd.scope; transient)
  Transient: yes
     Active: active (running) since Sat 2025-02-22 13:52:41 CST; 3 days ago
      Tasks: 14
     Memory: 347.4M
        CPU: 2h 18min 50.281s
     CGroup: /system.slice/slurmstepd.scope
             ├─job_28
             │ └─step_extern
             │   ├─slurm
             │   │ └─25164 "slurmstepd: [28.extern]"
             │   └─user
             │     └─task_0
             │       ├─25168 sleep 100000000
             │       ├─25337 "sshd: userA [priv]"
             │       ├─25359 "sshd: userA@pts/0"
             │       ├─25360 -bash
             │       ├─25416 stress-ng --cpu 2 --timeout 120 --metrics-brief
             │       ├─25417 stress-ng --cpu 2 --timeout 120 --metrics-brief
             │       └─25418 stress-ng --cpu 2 --timeout 120 --metrics-brief
             └─system
               └─5404 /usr/sbin/slurmstepd infinity
# slurmstepd.scope也包含了用户sshd,bash以及命令行进程,说明核数资源是受限制的
# 所有核数资源受slurmstepd.scope/job_$jobid/cpuset.cpus控制
[root@cu01 ~]# cat /sys/fs/cgroup/system.slice/slurmstepd.scope/job_28/cpuset.cpus
0
# 如果是提交2核,cpuset.cpus内容就是0-1。
sbatch提交后台作业,然后ssh到分配得节点,这个PAM执行脚本也是能控制sshd,bash以及命令行进程的。验证方法与上面的salloc+ssh是一样的。

参考

  1. Control Group v2 plugin
  2. pam_slurm_adopt
  3. ChatGPT, DeepSeek, 通义千问
本文阅读量  次
本站总访问量  次