1. 引言

随着现在深度学习越发的流行,超级计算工作站也逐渐流行了起来。曾经我们远程使用的服务器可能都是单机的,给一个ip地址,一个端口号,一个用户名和一个密码,我们就直接ssh上去了。

但是,这种只适合少量人的单机场景,现在一般的机构都会构建集群,动不动就是8*8卡的集群,如果再使用非常原始的用户登录,不仅出现大量的资源浪费,还会由于资源需要自己主动抢占而出现肢体冲突的情况,身边很多要好的朋友都会由于资源的恶性竞争而渐行渐远。

因此,再管理更大的资源的时,必须需要一套行之有效的管理系统,才能够保证一切的顺利进行。不过,但凡管理,都是有管理成本的。什么时候需要构建管理系统,取决于管理成本<优化收益。当我们原生的生态出现了矛盾和冲突,需要全局统筹的时候,这时候管理系统就必须落实了。

因此,我们逐渐接触到了看似更加复杂,但是更加有效和长远的基于HPC场景的集群管理系统。通过我实际搜索和调阅相关资料,目前主流的集群任务调度系统主要有4个:LSF,SGE,PBS和Slurm。

2. 背景(四大集群介绍)

LSF是一个比较主流的任务调度系统,包括深圳和苏州的超算中心都是使用的LSF系统,包括Spectrum LSF, PlatformLSF和OpenLava。我接触到的都是PlatformLSF。

grpc集群 hpc集群管理_运维


SGE则包括UGE和SGE,本来是开源的,但是被Oracle收购后就变成了闭源了。

grpc集群 hpc集群管理_运维_02


PBS则包括OpenPBS,PBS PRO 和Moab/TORQUE,可能由于比较老了吧,目前我没有见到了。

grpc集群 hpc集群管理_bash_03


最后介绍我自己亲自使用的Slurm系统,熟悉了系统以后,上手的感觉确实还不错,当然也是唯一一个开源的系统,这意味着我们可以自己构建属于自己的集群(如果有这么多资源的话)。

grpc集群 hpc集群管理_grpc集群_04

下面是四个集群管理系统的比较。可以看到,由于目前GPU需求量越来越大,因此我们目前常见的主要是LSF(曙光)和Slurm(天河)。

grpc集群 hpc集群管理_grpc集群_05

3. slurm入门

3.1 slurm安装

尽管slurm是一个开源项目,我们每个人都可以试一把,不过考虑到我们每个人可支配资源的缘故,这里我们不讲述slurm的系统搭建,感兴趣的同学可以看看《ubuntu22.04安装slurm》,《ubuntu系统下SLURM集群》,《在ubuntu环境下搭建slurm(2022年9月)》,《2台搭建slurm系统》等。

这里主要是简要介绍如何快速使用slurm以及给出相关参考文献。

3.2 slurm简介

slurm既然也是系统,必然执行系统的一个重要功能,那就是资源的调度和管理,与普通的操作系统不同的是,它更多的是面向作业任务和硬件资源分配的。也就是说,它更适合针对科研工作而进行的管理。现实中,我们的很多高精尖的实验室也是实行类似的管理制度,毕竟僧多粥少的情况在我们这是存在的并将长期存在的。那么它的终极目标就来了:尽自己最大可能将手上的硬件资源分配到即将到来的作业上,使得由多台高性能计算机(HPC)能够充分发挥自己的能力的同时,还保证所有作业都能够以一种公平和谐的状态运行。

当然,这些都不重要了。我们想了解的是如何使用。

3.3 slurm的使用

好了,现在假设我们已经登录到了一个slurm上了,我们该做些什么呢?我觉得就三步:第一步观察周围环境,第二步采取行动,第三步,看看自己的行为如何。

下图的蓝色部分基本上就是我们用户关注的一些常用命令了。

grpc集群 hpc集群管理_grpc集群_06

3.3.1 观察周围环境(查阅目前集群状态)

命令1:sinfo (查看集群运行状态)
就会出现如下的形式:

(base) [xiazh@login ~]$ sinfo
PARTITION     AVAIL  TIMELIMIT  NODES  STATE NODELIST
CPU-Large*       up 20-00:00:0     3    mix   cnode[220,231,236]
CPU-Large*       up 20-00:00:0     5   alloc  cnode[164-169]
CPU-Small        up 15-00:00:0     2    mix   cnode[220,231]

在其默认的输出中,partition 表示分区名、avail 表示分区状态(up 可用,down 不可用),timelimit 表示分区可供最大运行时长,nodes 表示节点数,state 表示节点运行状态,nodelist 表示分区包含的节点列表。

其中我们需要关注的主要是 state 和 partition ,若 state 中显示 idle 表示节点处于空闲状态,可接收新的作业;显示 allocated 表示节点已经分配了一个或者多个作业且所有核心用满,在作业释放前不能再被分配作业;显示 mix 状态为使用部分核心,仍可以被分配作业;显示 drain 状态表示对应节点已经下线;显示 drng 表示已下线但仍有作业在运行。我们一般会选择将作业提交到 state=idle 所对应的分区(partition),这样可以减少排队时间。

命令2:squeue(查看任务运行状态)
如果上面sinfo是一个集群概览,那么squeue则是看作业队列的详情,例如:

(base)[xiazh@login]$ squeue
  JOBID   PARTITION     NAME     USER    ST     TIME    NODES  NODELIST(REASON)
    55       cpu        test    xiazh    PD      0:00      1   (PartitionTimeLimit)
    54       cpu        test    xiazh     R      0:15      1       cnode220

其中JOBID表示任务ID 编号,PARTITION表示作业所在队列(分区),NAME表示任务名称,USER为用户,ST为作业状态,TIME 为已运行时间,NODES 表示占用节点数,NODELIST(REASON)为任务运行的节点列表或者原因说明。另外,状态列中R-Runing(正在运行),PD-PenDing(资源不足,排队中),CG-COMPLETING(作业正在完成中),CA-CANCELLED(作业被人为取消),CD-COMPLETED(作业运行完成),F-FAILED作业运行失败,NF-NODE_FAIL节点问题导致作业运行失败,PR作业被抢占,S作业被挂起,TO作业超时被杀。除此之外,使用 squeue 配合不同参数可以过滤显示的内容,以便能看到你感兴趣的结果。某些参数可以相互组合。

如果需要看特定用户的话,只需要加上参数-u <name>即可。

命令3:sacct(查看自己的历史作业)
上面的任务只能查看到运行的和待运行的任务,而sacct则是能够看到自己之前执行的作业情况,包括完成的和失败的,例如:

[liuhy@admin playground]$ sacct
       JobID    JobName  Partition    Account  AllocCPUS      State ExitCode
------------ ---------- ---------- ---------- ---------- ---------- --------
104                bash        gpu       root          1  COMPLETED      0:0
140                test        cpu       root         12  COMPLETED      0:0
141                test        cpu       root         12 CANCELLED+      0:0
141.batch         batch                  root         12  CANCELLED     0:15
141.extern       extern                  root         12  COMPLETED      0:0
142               lllll        cpu       root          1     FAILED      2:0
142.extern       extern                  root          1  COMPLETED      0:0
142.0             lllll                  root          1     FAILED      2:0


3.3.2 运行程序

运行程序有三种方式,交互式作业、批处理作业和实时分配作业(前两种的结合体),这里主要使用第一种和第二种方式。

交互式作业(srun
这种方式直接使用srun命令获得相关资源,获得之后就会像我们之前单机登录一样使用即可,不过它的作业时间比较短(默认最长2天),只适合跑较小的作业或者调试使用。需要注意的是,一旦关闭终端了,这个资源就会被释放了,即使使用了nohup。
一个例子如下:
srun --job-name=do_not_kill --gres=gpu:4 -w pgpu06 -p p-V100 -c 24 --pty bash 这里说是,我们执行一个工作名称为do_not_kill,然后申请4个gpu(–gres=gpu:4), 在p-V100分区上的pgpu06节点上运行(-w pgpu06 -p p-V100),申请CPU数量为24个(-c 24),最后使用bash和交互式环境执行(-pty bash)。要注意的是这里的命令如同bash命令一样,参数前如果是-表示该参数是简写,–表示该参数是长写,长短写可能会表示同一个功能,例如-w和–nodelist是一个作用。

批处理作业(sbatch)
这种方式则是当任务需要长时间运行且自己已经调试成功了,只需要执行即可。这种执行方式其实比较古老,但是非常的严谨。
一个常用的slurm执行文件run.slurm内容如下:

#!/bin/bash
#SBATCH -J example
#SBATCH -p p-V100
#SBATCH -N 1
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=6
#SBATCH --gres=gpu:1
time=$(date "+%m%d-%H%M")
python run.py

然后只需要sbatch run.slurm即可,其中的参数和srun也比较像。

3.3.3 反思

这里是我们普通用户最容易忽略的地方,要知道上面的介绍都是建立在非常顺利的情况下,那么如果运行过程中有问题了该怎么办。一方面,可以参考一些资料如《slurm作业系统中常见错误》,对应一下自己的状态,另一方面,要学会利用搜索引擎。我经常被问到一些问题,我自己搜索的时候,答案就在第一个,但是提问者就经常问不到。我问,你都是搜索的啥?经常得到的答案是,在搜索引擎里都是直接问:“为什么XXX报错,为什么XXX不运行了,为什么XXX和我想得不一样”诸如此类的句子,我想无论是度娘还是谷哥应该都无法给出答案,就算是问真正的人,你也要继续解释现状才行。这和去医院问诊是一样的。

最后,给出slurm的中文指南,更加详细的部分可以在手册中查阅。