使用Slurm集群进行分布式图计算:对Github网络影响力的系统分析

本文分享自华为云社区《基于Slurm集群的分布式图计算应用实践:Github协作网络影响力分析》,作者:yd_263841138 。

1. 引言

Slurm(Simple Linux Utility for Resource Management)是一个针对小型Linux集群的开源、容错、高可扩展的集群管理及作业调度系统,它具备统一管理集群内的计算资源、管理任务调度、监控作业任务等功能,能够在并行程序开发、验证过程中省去对集群计算资源管理配置的冗杂过程,高效助力分布式并行程序开发。

Gemini 是一个轻量级分布式图计算框架,其是后续多款大规模图计算框架的原型模板。Gemini既支持单机运行,也可以分布式多机运行。而多机运行的话,Gemini给出了在Slurm上提交作业的运行方案。

因此,一方面出于对作业调度系统部署过程的实践学习,另一方面出于后续对大规模图计算框架预研方向上的需要,最终决定尝试在通过多台华为云弹性服务器构建简易计算集群,并安装部署Slurm作业调度系统。

2. Slurm部署

2.1. 配置信息

关于华为云服务器配置,我以按需计费模式开通了三台通用入门型弹性云服务器(ECS)及其弹性公网IP,详细信息如下表。

规格类型 主机名 镜像 处理器 内存 硬盘规格 内网地址
通用入门型 master CentOS 7.9 64bit 1vCPUs 1GiB 通用型SSD 40 GiB 192.168.101
通用入门型 slave1 CentOS 7.9 64bit 4vCPUs 4GiB 通用型SSD 40 GiB 192.168.100
通用入门型 slave2 CentOS 7.9 64bit 4vCPUs 4GiB 通用型SSD 40 GiB 192.168.102

根据slurm部署规划,我以处理器规格为1vCPUs的master作为控制节点,不承担计算任务;以两台处理器规格为4vCPUs的slave1/2作为计算节点,构成集群的全部计算资源。

2.2. 部署过程

Slurm的部署过程比较复杂,这里以其官网文档的"Super Quick Start"指南作为参考进行部署,主要步骤包括:时钟同步、Munge配置、Slurm配置等步骤。

2.2.1. 时钟同步

这里采用ntp作为集群的时钟同步工具,其重点是对/etc/ntp.conf配置文件相关参数的编辑。

集群内时钟服务器节点

集群内的时钟服务器需要确定本服务器的所参照的时钟标准。这里将原本的默认服务器改为国内服务器。

复制代码
#注释掉默认的外网时间服务器
# server 0.centos.pool.ntp.org iburst
# server 1.centos.pool.ntp.org iburst
# server 2.centos.pool.ntp.org iburst
# server 3.centos.pool.ntp.org iburst
# 添加中国国家授时中心服务器地址
server 0.cn.pool.ntp.org
server 1.cn.pool.ntp.org
server 2.cn.pool.ntp.org
server 3.cn.pool.ntp.org
# 添加允许上层时间服务器主动修改本机时间
restrict 0.cn.pool.ntp.org nomodify notrap noquery
restrict 1.cn.pool.ntp.org nomodify notrap noquery
restrict 2.cn.pool.ntp.org nomodify notrap noquery
restrict 3.cn.pool.ntp.org nomodify notrap noquery

修改完配置文件后,则先将当前服务器时钟与参照服务器进行对齐,接着开启ntp服务。

复制代码
sudo ntpdate cn.pool.ntp.org
sudo service ntpd start

集群内其他服务器节点

集群内其他待同步的服务器节点的/etc/ntp.conf配置需要确定集群内时钟服务器主机名或IP地址,并允许其时钟被时钟服务器所修改。

复制代码
# 从node01中同步时间
# server <YOUR CLOCK SERVER IP_ADDR/HOSTNAME>
server master
# 允许时间服务器修改本地时间
# restrict 时间服务器主机名或ip nomodify notrap noquery
restrict master nomodify notrap noquery

在完成配置文件的修改后,先将当前服务器时钟与时钟服务器的时钟进行对齐后,再启动ntp服务。

复制代码
#sudo ntpdate <YOUR CLOCK SERVER IP_ADDR/HOSTNAME>
sudo ntpdate master
sudo service ntpd start

2.2.2 配置Munge

参照Munge官网安装指南Installing from RPMs on Red Hat compatible systems 进行安装。

首先,下载munge(munge.0.5.15.tar.xz和munge.0.5.15.tar.xz.asc两个文件)。

接着,参考Installing from RPMs onRed Hat compatible systems的指导进行安装。具体地,

  • 先从tarball构建SRPM,并根据SRPM安装依赖:
复制代码
rpmbuild -ts munge-0.5.15.tar.xz
sudo dnf builddep SRPMS/munge-0.5.15-1.el9.src.rpm
  • 再验证安装下载的源码。下载dun.gpg,确保文件夹下由dun.gpg、munge-0.5.15.tar.xz和munge-0.5.15.tar.xz.asc三个文件。
复制代码
rpmbuild -tb --with=verify --with=check munge-0.5.15.tar.xz
  • 此时munge、munge-devel、munge-libs或更多二进制RPMs就会生成。然后通过二进制RPMs安装:
复制代码
sudo rpm --install --verbose \\
     RPMS/x86_64/munge-0.5.15-1.el9.x86_64.rpm \\
     RPMS/x86_64/munge-debugsource-0.5.15-1.el9.x86_64.rpm \\
     RPMS/x86_64/munge-devel-0.5.15-1.el9.x86_64.rpm \\
     RPMS/x86_64/munge-libs-0.5.15-1.el9.x86_64.rpm

munge不必获得root权限,但是需要具有对以下文件夹的所属:

  • ${sysconfdir}/munge
    [/etc/munge]
  • ${localstatedir}/lib/munge
    [/var/lib/munge]
  • ${localstatedir}/log/munge
    [/var/log/munge]
  • ${runstatedir}/munge
    [/run/munge]

创建munge密钥

复制代码
sudo -u munge ${sbindir}/mungekey --verbose
#如果失败,则需手动切到/usr/local/sbin目录下执行mungeky

生成密钥存在于*${sysconfdir}/munge/munge.key*下,需要把这个密钥拷贝集群内所有机器的此目录中。

最后,开启munge守护进程(systemd方式)

复制代码
# 开机自动启动
sudo systemctl enable munge.service
# 启动
sudo systemctl start munge.service
# 查看状态
sudo systemctl status munge.service
# 停止
sudo systemctl stop munge.service

2.2.3. 配置slurm

从官网下载slurm22xxxxx,

解压tar-ball: tar ---bzip -x -f slurm*tar.bz2

进入Slurm源码目录,键入 ./configure 配置(可带例如---prefix=和---sysconfdir=选项)

键入 make 编译Slurm

键入 make install 安装程序、软件和库等。

配置slurm.conf

主要配置SlurmUser、以及节点、分区信息。

复制代码
SlurmctldHost=master
ProctrackType=proctrack/linuxpro
# COMPUTE NODES
NodeName=slave2 Sockets=1 CPUs=4 CoresPerSocket=2 ThreadsPerCore=2 RealMemory=300 State=UNKNOWN NodeAddr=192.168.0.102
NodeName=slave1 Sockets=1 CPUs=4 CoresPerSocket=2 ThreadsPerCore=2 RealMemory=300 State=UNKNOWN NodeAddr=192.168.0.100
NodeName=master Sockets=1 Procs=1 CoresPerSocket=1 ThreadsPerCore=2 RealMemory=300 State=UNKNOWN NodeAddr=192.168.0.101
PartitionName=compute Nodes=slave1,slave2  Default=YES MaxTime=INFINITE State=UP
PartitionName=control Nodes=master Default=NO MaxTime=INFINITE State=UP

运行Slurm前,确保SlurmUser存在,相关目录(/var/spool/slurm/)存在,slurm.conf已拷贝到集群内所有机器同目录位置。

2.2.4. 测试运行

确保时间同步。

确保munge已开启。

控制节点

键入 slurmctld -D 显式运行,命令行打印运行信息。

计算节点

键入 slurmd -D 显式运行,命令行打印运行信息。

3. Github协作网络影响力分析

3.1. Gemini编程接口

Gemini图计算框架为用户设计自定义图应用算法提供了两个通用编程接口:process_vertices和process_edges。顾名思义,process_vertices即表示对顶点的遍历,用户需要另外提供顶点遍历时的自定义更新函数;process_edges即表示对边的遍历,用户需要另外提供自定义消息生成、规约函数。

3.1.1. process_vertices

process_vertices声明如下所示,其包含两个参数,分别是处理函数process以及活跃顶点集active。从使用角度而言,process即需要用户指明对顶点遍历时需要参照哪些数据、修改哪些数据;active的作用类似顶点开关,即在此轮顶点遍历中需要遍历哪些顶点。

复制代码
template<typename R>
  R process_vertices(std::function<R(VertexId)> process, Bitmap * active);

3.1.2. process_edges

process_edges的声明如下所示。由于Gemini在实际计算过程中会采用Sparse或Dense两种模式,因此这里需要分别提供Sparse和Dense的Signal和Slot方法(即发送信息、接收消息方法)。

复制代码
template<typename R, typename M>
  R process_edges(std::function<void(VertexId)> sparse_signal, 
                                    std::function<R(VertexId, M, VertexAdjList<EdgeData>)> sparse_slot, 
                                    std::function<void(VertexId, VertexAdjList<EdgeData>)> dense_signal, 
                                    std::function<R(VertexId, M)> dense_slot, 
                                    Bitmap * active, 
                                    Bitmap * dense_selective = nullptr) {

3.2. 预处理

这里通过Github日志信息来构建Github协作网络,以进一步分析得到用户、项目的影响力指标。因此,在具体分析计算之前,需要对原始数据进行抽取及预处理工作。

3.2.1. 日志抽取

针对Github日志数据的处理存在一些困难与挑战。比如,日志文件数据量大,难以处理;日志信息粒度极高,包含大量冗杂信息,需要进一步筛选。针对这些困难挑战,这里采取了相对应的处理措施。

首先,对日志文件按一定条目数进行批量切分,切分成大小均匀的一个个子文件再进行处理。如若不进行切分,那么倘若在处理程序执行过程中出现错误,则之前已处理的进度无法跟踪、处理好的数据只能舍弃再从头开始处理。

接着,再利用正则表达式结合python第三方库pandas对子文件信息进行处理。具体地,主要抽取仓库ID(repo_id)、开发者ID(actor_id)以及事件类型等字段。

3.2.2. 同构图转异构图

根据日志抽取得到的数据可构建出开发者与代码仓库之间的二部图,其分别以开发者、代码仓库为顶点,将各种事件抽象为二者之间的关系。而直接处理异构图本身是比较复杂的,因此这里将采取顶点降维的方式、将异构图分别转为仅含开发者或仅含代码仓库的同构图,分别求开发者之间的影响力指标与代码仓库之间的影响力指标。以转变为仅含代码仓库顶点的同构图为例,这里首先将开发者看作是"中继",即一个开发者会直接与若干代码仓库产生联系。这里则将开发者所直接联系的若干代码仓库之间用边相连、相联系起来,这样即可消除起"中继"作用的开发者顶点,进而转变为只包含代码仓库顶点的同构图。

3.2.3 适配Gemini数据要求

Gemini要求用户提供图中顶点个数总数作为程序参数,并且要求对顶点序号进行顺序连续重编号。因此,为了符合Gemini的要求,这里对图的顶点序号进行重新编号,并通过哈希表记录重新编号后的顶点与原顶点的映射关系。

3.3. 程序设计

PageRank算法最初作为互联网网页重要度的计算方法,能有效反映互联网中网页的影响力。同样,将PageRank算法借鉴到Github协作网络影响力分析中也可得到开发者、代码仓库的影响力指标。因此,在算法设计上,这里参考PageRank算法,利用Gemini框架进行程序设计与实现。

Gemini源码已包含一个PageRank的测试样例,这里基于测试样例进行修改调整,以适配Github协作网络分析任务。在PageRank实现过程中,就编程方面比较核心的即是边遍历(process_edges)的过程。

复制代码
graph->process_edges<int,double>(
      [&](VertexId src){
                // sparse_signal
        graph->emit(src, curr[src]);
      },
      [&](VertexId src, double msg, VertexAdjList<Empty> outgoing_adj){
                // sparse_slot
        for (AdjUnit<Empty> * ptr=outgoing_adj.begin;ptr!=outgoing_adj.end;ptr++) {
          VertexId dst = ptr->neighbour;
          write_add(&next[dst], msg);
        }
        return 0;
      },
      [&](VertexId dst, VertexAdjList<Empty> incoming_adj) {
                // dense_signal
        double sum = 0;
        for (AdjUnit<Empty> * ptr=incoming_adj.begin;ptr!=incoming_adj.end;ptr++) {
          VertexId src = ptr->neighbour;
          sum += curr[src];
        }
        graph->emit(dst, sum);
      },
      [&](VertexId dst, double msg) {
                // dense_slog
        write_add(&next[dst], msg);
        return 0;
      },
      active
    );

3.4. 程序运行

确认集群中Slurm系统正在运行,接着以提交作业的方式运行程序。

确认Slurm系统与资源状况

复制代码
sinfo

编译并提交作业

复制代码
make
srun -N 2 ./NewGemini/toolkits/pagerank ./NewGemini/github_data.dat 1000 8

4. 小结

本文实现了Slurm在弹性云服务器集群的安装与部署,整体流程较为简洁明了,但是在实际实施过程中仍会遇到些问题。对于Gemini框架的理解和使用仍比较局限。对此,根据整个部署过程下来后的心得体会,作如下几点小结:

  • 选择Centos镜像的话尽量选择Centos 7,否则Centos 8的话会遇到一些问题。
  • Munge的安装方法比较丰富,官网提供了git安装、RPM安装等多种安装方式。
  • 会出现缺少依赖、缺少文件夹、权限错误等问题,根据错误信息逐个解决即可。
  • Gemini对输入数据有一定要求,比如顶点需要顺序连续编号。并且如果当输入是带权图时,在写二进制文件时需额外注意。

这次实践比较简略,其目的主要实践学习作业调度软件在集群的部署过程,以及为分布式图计算应用提供初步NUMA资源架构环境,因而仅简要划分了控制节点与计算节点,并没有继续细分登录节点、部署数据库、考虑计费机制等问题。因此,在未来或需要继续深入思考以下几点内容:

  • 如何以实验场景、生产场景的标准部署Slurm。
  • 如何分析提交作业的程序性能、机器利用率。
  • 深入理解Gemini框架思想及接口,对照其他图计算框架继续学习。

参考资料

点击关注,第一时间了解华为云新鲜技术~