Lab 1: Simple Cluster Building¶
- Lab1: 简单集群搭建
- Q:请简要说明本次实验中 AI Agent 的使用情况,包括所采用的 Agent 平台(或模型服务提供方)、使用的客户端/IDE,以及其在实验中的具体辅助作用:
- A: Codex/Claude Code,其中Codex为ChatGPT 5.4-high,Claude Code为Deepseek-v4-Flash/Pro. 我也混合使用Claude-Sonnet-4.6-Medium / ChatGPT-Thinking / Deepseek-V4-Pro/Flash联网模式来做小的问答.
本实验是简单集群搭建. 计算机集群是指将多台计算机连在一起通过网络协同工作,以完成一些大规模的计算任务 \(\Longrightarrow\) 并行计算思想.
从源代码构建Linux应用-Angband¶
~$ cd Angband-4.2.5
~/Angband-4.2.5$ ls
acinclude.m4 config.guess CONTRIBUTING.md m4 run-tests tests
aclocal.m4 config.sub docs Makefile screenshots utils
changes.txt configure install-sh mk scripts version
CMakeLists.txt configure.ac lib README.md src
~/Angband-4.2.5$ vim README.md
显示如下:

编译并启动Angband:(这里直接按教程安装的话前端是不会出现的,会报错. 我选择直接在终端中显示,因此多安装了一个sudo apt install libncurses-dev)
显示如下:

(嗯?怎么做完1.1的时候1.2也顺便做完了?那看来不是我的环境和教程不一致,而是顺序不一样)
ncurses库是一个用于在 UNIX-like 系统上进行文本界面操作的库. 它提供了一套 API,使得开发者能够在终端上创建和管理文本界面应用程序,包括窗口、菜单、对话框、文本输入等功能. Angband 使用了了ncurses库来实现游戏界面. 但这个库不会被包含在 Angband 的源代码中,也没有默认包含在系统中,因此我们需要手动安装.我们解决了一个简单的依赖问题:Angband -> ncurses. 在 HPC 应用中,实际的依赖关系极其复杂.
自动化构建工具是指在自动化软件构建过程的工具,主要功能有:编译链接、依赖管理、代码检查、单元测试、打包和部署.
常用的Autotools有两种:
-
GNU Autotools
-
CMake
集群环境搭建¶
首先需要在主节点上生成ssh密钥对,此处我重新生成了一次:
grapesea@grapesea:~/codingspace/.ssh$ ssh-keygen -t ed25519
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/grapesea/.ssh/id_ed25519): /home/grapesea/codingspace/.ssh/cluster_id_ed25519
/home/grapesea/codingspace/.ssh/cluster_id_ed25519 already exists.
Overwrite (y/n)? y
Enter passphrase for "/home/grapesea/codingspace/.ssh/cluster_id_ed25519" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/grapesea/codingspace/.ssh/cluster_id_ed25519
Your public key has been saved in /home/grapesea/codingspace/.ssh/cluster_id_ed25519.pub
The key fingerprint is:
SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx grapesea@grapesea
The key's randomart image is:
+--[ED25519 256]--+
| ...o. |
| o..+. |
| ...o o . |
| . o o |
| o S .. . o |
| . E O o. o |
|. . . .o@ =. |
|oo =..oo*o. |
|..*=+o=Bo+ |
+----[SHA256]-----+
现在:
$ ssh-copy-id -i ~/codingspace/.ssh/cluster_id_ed25519.pub grapesea@192.168.159.128
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/grapesea/codingspace/.ssh/cluster_id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
Number of key(s) added: 1
Now try logging into the machine, with: "ssh -i /home/grapesea/codingspace/.ssh/cluster_id_ed25519 'grapesea@192.168.159.128'"
and check to make sure that only the key(s) you wanted were added.
$ ssh -i /home/grapesea/codingspace/.ssh/cluster_id_ed25519 'grapesea@192.168.159.128'
我安装OpenMPI是按照这个方法进行的:
-
首先从官网下载对应软件包,选择了5.0.9的版本,然后配置:
看到了一长串输出,最后的一段是:
Miscellaneous ----------------------- Atomics: GCC built-in style atomics Fault Tolerance support: mpi HTML docs and man pages: installing packaged docs hwloc: internal libevent: internal Open UCC: no pmix: internal PRRTE: internal Threading Package: pthreads Transports ----------------------- Cisco usNIC: no Cray uGNI (Gemini/Aries): no Intel Omnipath (PSM2): no (not found) Open UCX: no OpenFabrics OFI Libfabric: no (not found) Portals4: no (not found) Shared memory/copy in+copy out: yes Shared memory/Linux CMA: yes Shared memory/Linux KNEM: no Shared memory/XPMEM: no TCP: yes Accelerators ----------------------- CUDA support: no ROCm support: no OMPIO File Systems ----------------------- DDN Infinite Memory Engine: no Generic Unix FS: yes IBM Spectrum Scale/GPFS: no (not found) Lustre: no (not found) PVFS2/OrangeFS: no -
编译和安装:
-
环境变量配置:
-
验证安装和配置:
让Claude-4.6-Sonnet写了一段测试程序:
#include <mpi.h> #include <stdio.h> int main(int argc, char *argv[]) { MPI_Init(&argc, &argv); int world_size, world_rank; MPI_Comm_size(MPI_COMM_WORLD, &world_size); MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); char processor_name[MPI_MAX_PROCESSOR_NAME]; int name_len; MPI_Get_processor_name(processor_name, &name_len); printf("Hello from rank %d out of %d on %s\n", world_rank, world_size, processor_name); MPI_Finalize(); return 0; }编译和运行:
这里我遇到了一段warning:
--------------------------------------------------------------------------
PMIx was unable to find a usable compression library
on the system. We will therefore be unable to compress
large data streams. This may result in longer-than-normal
startup times and larger memory footprints. We will
continue, but strongly recommend installing zlib or
a comparable compression library for better user experience.
You can suppress this warning by adding "pcompress_base_silence_warning=1"
to your PMIx MCA default parameter file, or by adding
"PMIX_MCA_pcompress_base_silence_warning=1" to your environment.
--------------------------------------------------------------------------
这是因为压缩依赖没有安装,只需要:
接下来处理连通性,文档有10.1. Quick start:, 10.6. Launching with SSH.
实验中需要测试完成的ssh节点:
我使用了自己的腾讯云服务器ubuntu@101.34.74.93作为远程节点worker1.
添加ssh公钥:
测试:
ssh -i /home/grapesea/openmpi/.ssh/cluster_id_ed25519 'ubuntu@101.34.74.93' Last login: Sun May 31 15:46:08 2026 from 124.160.20.41 ubuntu@VM-0-9-ubuntu:~$本机配置:(这是在使用tailscale之后修改的配置)
GNU nano 8.4 /home/grapesea/.ssh/config Host 100.111.224.57 User ubuntu IdentityFile ~/openmpi/.ssh/cluster_id_ed25519 Host 100.81.185.53 User grapesea IdentityFile ~/openmpi/.ssh/cluster_id_ed25519 Host localhost User grapesea IdentityFile ~/openmpi/.ssh/cluster_id_ed25519测试:
给mpirun指定节点和进程数:
指定工作路径:使用--wdir来指定工作路径.
在上面的子节点配置中,由于我的子节点是腾讯云服务器,跟本地虚拟机不在同一个网络里,无法直接通信,因此在最后调试:
# 本地虚拟机
$ mpirun --hostfile ~/openmpi/hostfile -np 8 \
--prefix /home/ubuntu/openmpi \
hostname
Abort is in progress...hit ctrl-c again to forcibly terminate
# 进程始终不停止.
# 远程服务器
ubuntu@VM-0-9-ubuntu:~/.ssh$ ssh worker1 "ping -c 2 192.168.159.128"
ssh: Could not resolve hostname worker1: Temporary failure in name resolution
这个解决方法有很多(我搜索到可以用Tailscale),但是HPC101使用ZJU集群的话应该可以规避这个问题吧(?),在后面的报告中,因为虚拟机和远程服务器的理想环境配置与实际的原有配置差异过大,我不得不放弃自己的远程服务器.
似乎这样就行了:
会提示登录,开启之后得到的结果是:
此时要把自己的配置修改掉. 具体而言,需要修改主节点上的
~/.ssh/config(已经改成了最上面所示的配置)和~/openmpi/hostfile的ip地址:然后我还遇到另一个逆天问题:远程服务器是没办法修改用户名的,而我也不愿意把
grapesea改成ubuntu这么丑陋平淡的用户名,所以链接又失败了,需要在主节点做一个符号链接:验证成果:
openmpi$ mpirun --hostfile ~/openmpi/hostfile -np 4 \ --prefix /home/ubuntu/openmpi \ hostname -------------------------------------------------------------------------- A request was made to bind that would require binding processes to more cpus than are available in your allocation: Application: hostname #processes: 4 Mapping policy: BYCORE Binding policy: CORE You can override this protection by adding the "overload-allowed" option to your binding directive. --------------------------------------------------------------------------
刚刚下载openmpi的时候就要求两台服务器使用相同的mpirun配置路径,接下来的共享文件系统就提到了如何共享文件. 牢方法大概指的就是我scp传递文件的方法吧,
-
共享文件系统和复制文件有什么区别?
最好出现在同一路径下,这样不需要手动指定路径
-
共享文件系统为什么通常要求各节点使用相同的挂载路径?
共享文件系统是让多个节点通过网络访问同一份数据,根据一致性模型/策略不同
-
共享文件系统通过什么方式来检查用户的一致性?
Linux 文件权限最终依赖数字 UID/GID来判断文件归于哪个用户.
知识讲解里面提到了Slurm作业调度系统,这是一种作业调度系统,能控制多个用户对节点的访问,进行任务调度、提供队列.
组件 职责 类比 MPI (OpenMPI) 进程间通信:让不同机器上的程序能互相发送/接收数据 快递员(负责送货) Slurm 资源调度:决定哪些节点运行任务、何时运行、分配多少资源 物流调度中心(决定谁来送、送到哪) NFS 共享文件系统:让所有节点看到同一份文件(可执行文件、输入数据、输出结果) 共享仓库(所有人取同一份货) Slurm相较于mpirun更加正式,不需要写hostfile:
方式 命令 适用场景 说明 直接 mpirunmpirun --hostfile hostfile -np 4 ./xhpl调试、测试、小规模验证 手动指定节点,不依赖 Slurm 通过 Slurm sbatch run.slurm或srun ./xhpl正式运行、批量作业 Slurm 自动分配资源并调用 MPI 基础概念:
- 节点(node):一台可以参与计算的机器,例如
node02.- 分区(partition):一组节点构成的队列.
- 作业(job):用户提交给 Slurm 的一次计算请求.
- 作业步骤(step):作业内部的一次实际运行,例如一个
srun hostname或一个 MPI 程序启动以及常用命令:
sinfo:查看分区和节点状态.srun:直接启动一个并行任务,常用于快速测试或交互式运行.sbatch:提交批处理脚本,让作业进入队列并由 Slurm 调度运行.squeue:查看当前排队或运行中的作业.sacct:查看历史作业记录. 是否可用取决于是否配置了 accounting
集群概况¶
节点情况¶
跟前面的部分数据不同,有所改动(重新生成机器码,重置了tailscale提供的ip地址)
| 节点 (用户名) | 角色 | IP地址* | CPU / 内存 | 主要服务 |
|---|---|---|---|---|
| node00 (grapesea) | 控制节点(虚拟机) | 192.168.159.128 / 100.90.67.126 | 2核** / 1.9GiB | NFS server, Slurm controller |
| node01 (grapesea) | 计算节点(虚拟机克隆) | 192.168.159.129 / 100.71.155.103 | 2核 / 1.9GiB | NFS client, Slurm compute |
| node02 (grapesea) | 计算节点(虚拟机克隆) | 192.168.159.130 / 100.79.11.84 | 2核 / 1.9GiB | NFS client, Slurm compute |
| node03 (grapesea) | 计算节点(虚拟机克隆) | 192.168.159.131 / 100.113.24.3 | 2核 / 1.9GiB | NFS client, Slurm compute |
*此处的第二个IP地址是经过tailscale内网穿透服务处理过的,为了使远程服务器ubuntu和本机grapesea置于同一网络内. (虽然后来远程服务器的网络我配不好,删除掉了)
**实际在slurm配置中可用的仅为1核,下同.
实验环境概况¶
宿主机:
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 39 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 32
On-line CPU(s) list: 0-31
Vendor ID: GenuineIntel
Model name: 13th Gen Intel(R) Core(TM) i9-13900HX
CPU family: 6
Model: 183
Thread(s) per core: 2
Core(s) per socket: 16
Socket(s): 1
Stepping: 1
BogoMIPS: 4838.39
cpu cores : 16
model name : 13th Gen Intel(R) Core(TM) i9-13900HX
siblings : 32
total used free shared buff/cache available
Mem: 3.8Gi 1.0Gi 2.7Gi 2.5Mi 295Mi 2.8Gi
Swap: 1.0Gi 735Mi 288Mi
操作系统版本:(WSL)
操作系统版本:(VMWare Workstation 的 Debian)
Debian GNU/Linux 13 (trixie)
CPU(s): 2
Model name: 13th Gen Intel(R) Core(TM) i9-13900HX
Thread(s) per core: 1
Core(s) per socket: 1
Socket(s): 2
网络配置:
虚拟机和WSL通过NAT接通本地网络,本地使用Clash Verge常开全局代理,设置一般是局域网连接、DNS覆写,端口一般设置成混合配置的7897端口.
节点间连接情况¶
首先为了方便(?存疑,可能不是特别方便),我仍然使用tailscale处理从节点.
处理过程:
# 删除旧的 machine-id
sudo rm /etc/machine-id
sudo rm /var/lib/dbus/machine-id
# 重新生成唯一 machine-id
sudo systemd-machine-id-setup
# 确认已生成新的 ID
cat /etc/machine-id
# 同步 dbus 的 machine-id
sudo ln -sf /etc/machine-id /var/lib/dbus/machine-id
# 先退出旧的 tailscale 登录
sudo tailscale down
sudo tailscale logout
# 清除旧状态
sudo systemctl stop tailscaled
sudo rm -rf /var/lib/tailscale
sudo rm -rf /var/lib/tailscale/tailscaled.state
sudo rm -rf /var/lib/tailscale/*.conf
# 重启 tailscaled
sudo systemctl start tailscaled
# 每台机器独立登录获取新 IP
sudo tailscale up
分别验证完就可以得到:(忽略node04,后面不会用到它,是远程服务器配置失败的遗留问题)

然后修改/etc/hosts的配置:
$ sudo tee /etc/hosts <<EOF
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
# Tailscale 集群节点
100.90.67.126 node00
100.71.155.103 node01
100.79.11.84 node02
100.113.24.3 node03
EOF
在node01上测试能否连通:
~$ ping -c 2 node02
ping -c 2 node03
PING node02 (100.79.11.84) 56(84) bytes of data.
64 bytes from node02 (100.79.11.84): icmp_seq=1 ttl=64 time=150 ms
64 bytes from node02 (100.79.11.84): icmp_seq=2 ttl=64 time=1.32 ms
--- node02 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.324/75.885/150.447/74.561 ms
PING node03 (100.113.24.3) 56(84) bytes of data.
64 bytes from node03 (100.113.24.3): icmp_seq=1 ttl=64 time=155 ms
64 bytes from node03 (100.113.24.3): icmp_seq=2 ttl=64 time=2.02 ms
--- node03 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 2.017/78.270/154.524/76.253 ms
~$ ssh grapesea@node02 hostname
node02
~$ ssh grapesea@node03 hostname
node03
更改~/.ssh/config的内容:
Host node00
HostName 100.90.67.126
User grapesea
IdentityFile ~/openmpi/.ssh/cluster_id_ed25519
Host node01
HostName 100.71.155.103
User grapesea
IdentityFile ~/openmpi/.ssh/cluster_id_ed25519
Host node02
HostName 100.79.11.84
User grapesea
IdentityFile ~/openmpi/.ssh/cluster_id_ed25519
Host node03
HostName 100.113.24.3
User grapesea
IdentityFile ~/openmpi/.ssh/cluster_id_ed25519
~/openmpi/hostfile中改成:
最后测试:
~$ mpirun --hostfile ~/openmpi/hostfile -np 8 \
--prefix /home/ubuntu/openmpi \
hostname
node00
node00
node01
node03
node02
node03
node02
node01
任务1:从源码构建OpenMPI, BLAS和HPL¶
搬一下架构图:

OpenMPI¶
参见上面的第2节,我在那个位置已经动手配完了.
CBLAS, BLAS¶
BLAS是 Basic Linear Algebra Subprograms 的缩写,是一组用于实现基本线性代数运算的函数库.
下载2个压缩包:
wget https://raw.githubusercontent.com/ZJUSCT/HPC101/main/src/lab1/CBLAS.tgz
wget https://raw.githubusercontent.com/ZJUSCT/HPC101/main/src/lab1/blas-3.12.0.tgz
BLAS的构建:
$ tar -xzf blas-3.12.0.tgz
$ cd BLAS-3.12.0
$ gfortran -O3 -c *.f # 编译所有 .f 文件为目标文件
$ ar rcs libblas.a *.o # 打包成静态库
$ ls -lh libblas.a # 验证
将它安装到openmpi目录下:
对于CBLAS,首先查看有哪些Makefile文件:
$ tar -tzf CBLAS.tgz | head -30
CBLAS/
CBLAS/examples/
CBLAS/include/
CBLAS/lib/
CBLAS/Makefile
CBLAS/Makefile.ALPHA
CBLAS/Makefile.HPPA
CBLAS/Makefile.in
CBLAS/Makefile.LINUX
CBLAS/Makefile.SGI64
CBLAS/Makefile.SUN4
CBLAS/Makefile.SUN4SOL2
CBLAS/README
CBLAS/src/
CBLAS/testing/
CBLAS/testing/auxiliary.c
CBLAS/testing/c_c2chke.c
CBLAS/testing/c_c3chke.c
CBLAS/testing/c_cblas1.c
CBLAS/testing/c_cblas2.c
CBLAS/testing/c_cblas3.c
CBLAS/testing/c_cblat1.f
CBLAS/testing/c_cblat2.f
CBLAS/testing/c_cblat3.f
CBLAS/testing/c_d2chke.c
CBLAS/testing/c_d3chke.c
CBLAS/testing/c_dblas1.c
CBLAS/testing/c_dblas2.c
CBLAS/testing/c_dblas3.c
CBLAS/testing/c_dblat1.f
~$ tar -xzf CBLAS.tgz
cd CBLAS
将Linux配置文件复制进去:
这是为了查看Makefile里面的关键变量,需要将libblas.a路径填到makefile.in中去
编译:
将库添加到OpenMPI目录下方便使用:
CBLAS$ cp ~/CBLAS/lib/cblas_LINUX.a ~/openmpi/lib/
CBLAS$ cp ~/CBLAS/include/cblas.h ~/openmpi/include/
CBLAS$ ls ~/openmpi/lib/cblas_LINUX.a
CBLAS$ ls ~/openmpi/include/cblas.h
/home/grapesea/openmpi/lib/cblas_LINUX.a
/home/grapesea/openmpi/include/cblas.h
现在两个线性代数运算单元都构建成功了,跟文档中提到的生成两个.a文件匹配(cblas_LINUX.a, libblas.a).
HPL¶
HPL(high performance Linpack)是评测计算系统性能的程序,支持大规模并行超级计算系统。其报告的每秒浮点运算次数(floating-point operations per second,简称 FLOPS)是世界超级计算机 Top500 列表排名的依据。
进入官网HPL Software查看最新版本并使用wget下载:
查看README:

我建了Makefile.Linux来作为Makefile(直接复制了Makefile.Linux_PII_CBLAS),出现了跟文档一样的报错:
make[1]: Leaving directory '/home/test/hpl/hpl-2.3'
make -f Make.top build_src arch=Linux_PII_CBLAS
make[1]: Entering directory '/home/test/hpl/hpl-2.3'
( cd src/auxil/Linux_PII_CBLAS; make )
make[2]: Entering directory '/home/test/hpl/hpl-2.3/src/auxil/Linux_PII_CBLAS'
Makefile:47: Make.inc: No such file or directory
make[2]: *** No rule to make target 'Make.inc'. Stop.
make[2]: Leaving directory '/home/test/hpl/hpl-2.3/src/auxil/Linux_PII_CBLAS'
make[1]: *** [Make.top:54: build_src] Error 2
make[1]: Leaving directory '/home/test/hpl/hpl-2.3'
make: *** [Make.top:54: build] Error 2
我改动的地方是:
Arch = Linux
TOPdir = $(HOME)/hpl-2.3
MPdir = /home/grapesea/openmpi
MPinc = -I$(MPdir)/include
MPlib = $(MPdir)/lib/libmpi.so
LAdir = $(HOME)/BLAS-3.12.0
LAlib = $(MPdir)/lib/cblas_LINUX.a $(MPdir)/lib/libblas.a
CC = /home/grapesea/openmpi/bin/mpicc
CCFLAGS = $(HPL_DEFS) $(MPinc) -fomit-frame-pointer -O3 -funroll-loops
LINKER = /usr/bin/gfortran
此时编译生成的可执行文件在bin/Linux下.
这样Task 1就算成功了.
任务2:配置 NFS 共享目录¶
Slurm 是高性能计算集群中常见的作业调度系统,在真实集群中,用户通常通过 Slurm 申请节点和 CPU 资源,由调度器决定作业何时、在哪里运行.
在node00上面:
sudo apt install -y nfs-kernel-server
sudo mkdir -p /cluster/shared
sudo chown grapesea:grapesea /cluster/shared
sudo chmod 755 /cluster/shared
sudo tee /etc/exports <<EOF
/cluster/shared 100.71.155.103(rw,sync,no_subtree_check,no_root_squash)
/cluster/shared 100.79.11.84(rw,sync,no_subtree_check,no_root_squash)
/cluster/shared 100.113.24.3(rw,sync,no_subtree_check,no_root_squash)
EOF
sudo exportfs -ra
sudo systemctl enable nfs-kernel-server --now
sudo exportfs -v
在node[01-03]上面:
sudo apt install -y nfs-common
sudo mkdir -p /cluster/shared
sudo mount node00:/cluster/shared /cluster/shared
df -h | grep cluster # 手动挂载指令
并且还可以配置开机自动挂载:
在主节点node00上面验证共享是否正常:
echo "NFS test from node00 - $(date)" > /cluster/shared/test.txt
ssh node01 "cat /cluster/shared/test.txt"
ssh node02 "cat /cluster/shared/test.txt"
ssh node03 "cat /cluster/shared/test.txt"
ssh node01 "echo 'written from node01' >> /cluster/shared/test.txt"
cat /cluster/shared/test.txt
输出:
NFS test from node00 - Mon Jun 1 07:20:26 AM EDT 2026
NFS test from node00 - Mon Jun 1 07:20:26 AM EDT 2026
NFS test from node00 - Mon Jun 1 07:20:26 AM EDT 2026
NFS test from node00 - Mon Jun 1 07:20:26 AM EDT 2026
written from node01
这表明配置成功了.
按照实验要求额外测试一些东西:
主节点node00:

看到:

验证node00的NFS服务正常运行:

在node01上面进行验证:

任务3:配置 Slurm 作业调度系统¶
4个节点上都安装:
在node00上面配置Munge密钥:
此时失败了,但是munge又是正常运行的,搜了一下发现Debian安装 munge 时已自动生成了密钥,不需要手动创建,因此直接下一步分发节点:
for node in node01 node02 node03; do
sudo scp /etc/munge/munge.key ${node}:/tmp/munge.key
ssh $node "sudo mv /tmp/munge.key /etc/munge/munge.key && \
sudo chown munge:munge /etc/munge/munge.key && \
sudo chmod 400 /etc/munge/munge.key && \
sudo systemctl enable munge --now"
done
这里不知道为什么又出错了,要我输入root@node01的密码,然而直接测试跨节点解密又是正确的:
~$ for node in node01 node02 node03; do
echo -n "$node: "
ssh $node "munge -n | unmunge" | grep STATUS
done
node01: STATUS: Success (0)
node02: STATUS: Success (0)
node03: STATUS: Success (0)
~$ for node in node01 node02 node03; do
echo -n "$node: "
ssh $node "id grapesea"
done
node01: uid=1000(grapesea) gid=1000(grapesea) groups=1000(grapesea),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),100(users),101(netdev),102(scanner),106(bluetooth),108(lpadmin)
node02: uid=1000(grapesea) gid=1000(grapesea) groups=1000(grapesea),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),100(users),101(netdev),102(scanner),106(bluetooth),108(lpadmin)
node03: uid=1000(grapesea) gid=1000(grapesea) groups=1000(grapesea),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),100(users),101(netdev),102(scanner),106(bluetooth),108(lpadmin)
新建配置:
~$ sudo mkdir -p /etc/slurm
ls /etc/slurm/
plugstack.conf plugstack.conf.d
~$ nproc
# 在 node01-03 上也确认
for node in node01 node02 node03; do
echo -n "$node: "
ssh $node "nproc"
done
2
node01: 2
node02: 2
node03: 2
好,下面的事情就很诡异了,我调了很多次配置都没成功,一直看到一直修不好的配置报错是:
Job for slurmctld.service failed because a fatal signal was delivered causing the control process to dump core.
See "systemctl status slurmctld.service" and "journalctl -xeu slurmctld.service" for details.
× slurmctld.service - Slurm controller daemon
Loaded: loaded (/usr/lib/systemd/system/slurmctld.service; enabled; preset: enabled)
Active: failed (Result: core-dump) since Mon 2026-06-01 09:32:39 EDT; 20ms ago
Invocation: 562a5aa8f46e45309933870583548d43
Docs: man:slurmctld(8)
Process: 12971 ExecStart=/usr/sbin/slurmctld --systemd $SLURMCTLD_OPTIONS (code=dumped, signal=ABRT)
Main PID: 12971 (code=dumped, signal=ABRT)
Mem peak: 4.5M
CPU: 28ms
这里的service启动失败问题手动修了5h没修好,只能让codex来做了.
经过一大通测试,Munge 认证链路坏了,而且 Slurm 发出来的 credential 有问题,排查了半天发现:
链接有问题,并且slurmd的环境也缺了一堆. 我遇到的抽象问题包括但不限于,CPU识别rmpi出错;各个节点的slurm启动时间不同导致校验出错;CPU核数与配置到集群中的数矛盾导致表格显示STATE=INVAL etc.
拷打了一段时间的Codex终于看到:
~$ sinfo # 在node00上
PARTITION AVAIL TIMELIMIT NODES STATE NODELIST
debug* up infinite 3 idle node[01-03]
STATE=idle,所以成功了!
在配完下一部分之后:

让伟大的Codex总结一下:
这次配置过程里,问题基本分成三类:脚本问题、权限/分发问题、节点注册参数不匹配问题.
脚本和服务启动问题
一开始系统其实已经装了 slurmctld、slurmd、munge,不是“没安装”,而是控制器没正常拉起来.
最早的
slurm_setup_node00.sh用了slurmctld -t做配置校验,但你这台机器的 Slurm 24.11不支持这个参数,所以脚本在第 5 步直接失败了. 后面把这一步改掉了.之后又遇到 certmgr plugin only allowed on slurmctld and slurmd,原因不是配置内容错,而是脚本直接调用了
/usr/sbin/slurmctld-wlm. 这个名字会让插件判断异常. 改成调用/usr/sbin/slurmctld后解决.再往后出现 Address already in use,是因为之前前台调试启动留下了 slurmctld -D 进程,占住了 6817 端口. 后来把脚本改成先清理残留调试进程,再由 systemctl restart slurmctld 正式启动.
slurmctld 真正启动时一度
core dump,根因是 slurm.conf 里的运行路径和 Debian 默认方式不一致. 后来把SlurmUser=slurm StateSaveLocation=/var/lib/slurm/slurmctld、SlurmdSpoolDir=/var/lib/slurm/slurmd、PidFile 路径等改成 Debian 包的默认约定,控制器才稳定启动.权限和文件分发问题
分发
slurm_setup_compute.sh和slurm.conf没问题,但scp /etc/munge/munge.key失败了,因为这个文件只有 root 可读. 这个问题的处理方式是改用sudo scp,或者先以 root 复制一份临时文件再传到从节点. .
scontrol reconfigure和scontrol update早期报 Invalid user id,原因是用普通用户执行了管理员操作. 解决方式是改成sudo scontrol ....节点注册失败问题
控制器起来以后,
sinfo里节点状态是unk*,这说明 slurmd 还没注册. 把 munge.key 和 slurm.conf 分发到从节点,并在从节点启动 munge 和 slurmd 后,节点开始能注册.注册后节点变成
INVALID_REG,并且 Reason 明确写着Low socket*core*thread count, Low CPUs. 这说明不是网络问题,而是 slurm.conf 中写的硬件拓扑和从节点实际上报上来的值不一致.一开始我们按 show nodes 中的表象把节点写成了
Sockets=2, CPUs=2,但这仍然不对. 最后通过在从节点运行sudo /usr/sbin/slurmd -C,拿到了 Slurm 自己推荐的真实配置:CPUs=1 Boards=1 SocketsPerBoard=1 CoresPerSocket=1 ThreadsPerCore=1 RealMemory=1935.这说明前面的节点定义确实“写大了”. 解决方式就是按
slurmd -C的输出原样回填到slurm.conf,然后重新分发配置、重启 slurmctld 和各节点的 slurmd,再UNDRAIN节点.过程里的几个关键结论
node00 不在
NodeName=列表里,所以控制节点不应该强行启动slurmd. 脚本后来也改成了自动判断,node00 只起 slurmctld.munge 从一开始就是关键前置条件. 只有
munge -n | unmunge验证通过后,Slurm 组件之间的认证才算正常.真正可靠的节点硬件配置可以直接用每台从节点上的
sudo /usr/sbin/slurmd -C作为判断.
任务4:使用 Slurm 提交 HPL¶
基础配置¶
这部分我没时间认真做了,给Codex读了一下环境,以下是结果和我过程中发现错误的分析:
本环节涉及到的文件在共享目录/cluster/shared/hpl/下,包含xhpl, HPL.dat, run_hpl.sbatch, README.md, results/slurm-7.out.
以下是常用的运行指令:
上面的指令输出大致可以是这样:
$ squeue
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
10 debug hpl grapesea PD 0:00 3 (Resources)
9 debug hpl grapesea R 10:01 3 node[01-03]
$ sinfo
PARTITION AVAIL TIMELIMIT NODES STATE NODELIST
debug* up infinite 3 alloc node[01-03]
$ scontrol show nodes
NodeName=node01 Arch=x86_64 CoresPerSocket=1
CPUAlloc=1 CPUEfctv=1 CPUTot=1 CPULoad=1.00
AvailableFeatures=(null)
ActiveFeatures=(null)
Gres=(null)
NodeAddr=100.71.155.103 NodeHostName=node01 Version=24.11.5
OS=Linux 6.12.90+deb13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.90-1 (2026-05-22)
RealMemory=1800 AllocMem=0 FreeMem=76 Sockets=1 Boards=1
State=ALLOCATED ThreadsPerCore=1 TmpDisk=0 Weight=1 Owner=N/A MCS_label=N/A
Partitions=debug
BootTime=2026-06-01T12:43:42 SlurmdStartTime=2026-06-01T12:57:03
LastBusyTime=2026-06-01T22:28:27 ResumeAfterTime=None
CfgTRES=cpu=1,mem=1800M,billing=1
AllocTRES=cpu=1
CurrentWatts=0 AveWatts=0
NodeName=node02 Arch=x86_64 CoresPerSocket=1
CPUAlloc=1 CPUEfctv=1 CPUTot=1 CPULoad=1.00
AvailableFeatures=(null)
ActiveFeatures=(null)
Gres=(null)
NodeAddr=100.79.11.84 NodeHostName=node02 Version=24.11.5
OS=Linux 6.12.90+deb13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.90-1 (2026-05-22)
RealMemory=1800 AllocMem=0 FreeMem=106 Sockets=1 Boards=1
State=ALLOCATED ThreadsPerCore=1 TmpDisk=0 Weight=1 Owner=N/A MCS_label=N/A
Partitions=debug
BootTime=2026-06-01T12:43:38 SlurmdStartTime=2026-06-01T12:57:03
LastBusyTime=2026-06-01T22:28:27 ResumeAfterTime=None
CfgTRES=cpu=1,mem=1800M,billing=1
AllocTRES=cpu=1
CurrentWatts=0 AveWatts=0
NodeName=node03 Arch=x86_64 CoresPerSocket=1
CPUAlloc=1 CPUEfctv=1 CPUTot=1 CPULoad=1.03
AvailableFeatures=(null)
ActiveFeatures=(null)
Gres=(null)
NodeAddr=100.113.24.3 NodeHostName=node03 Version=24.11.5
OS=Linux 6.12.90+deb13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.90-1 (2026-05-22)
RealMemory=1800 AllocMem=0 FreeMem=83 Sockets=1 Boards=1
State=ALLOCATED ThreadsPerCore=1 TmpDisk=0 Weight=1 Owner=N/A MCS_label=N/A
Partitions=debug
BootTime=2026-06-01T12:47:08 SlurmdStartTime=2026-06-01T12:57:03
LastBusyTime=2026-06-01T22:28:25 ResumeAfterTime=None
CfgTRES=cpu=1,mem=1800M,billing=1
AllocTRES=cpu=1
CurrentWatts=0 AveWatts=0
其中squeue中的状态参数含义:
-
R:Running,运行中 -
PD:Pending,排队中 CG:Completing,快结束,正在清理CD:Completed,已完成F:Failed,失败CA:Cancelled,被取消TO:Timeout,超时
提交作业:
# 查看.sbatch文件的配置
hpl$ cat run_hpl.sbatch
#!/bin/bash
#SBATCH --job-name=hpl
#SBATCH --partition=debug
#SBATCH --nodes=3
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=1
#SBATCH --time=00:20:00
#SBATCH --output=/cluster/shared/hpl/results/slurm-%j.out
#SBATCH --error=/cluster/shared/hpl/results/slurm-%j.err
#SBATCH --chdir=/cluster/shared/hpl
set -euo pipefail
export PATH=/home/grapesea/openmpi/bin:$PATH
export LD_LIBRARY_PATH=/home/grapesea/openmpi/lib:${LD_LIBRARY_PATH:-}
export PMIX_MCA_psec=native
echo "job_id=${SLURM_JOB_ID}"
echo "node_list=${SLURM_JOB_NODELIST}"
echo "nodes=${SLURM_JOB_NUM_NODES}"
echo "ntasks=${SLURM_NTASKS}"
echo "ntasks_per_node=${SLURM_NTASKS_PER_NODE}"
echo "working_dir=$(pwd)"
echo "start_time=$(date -Is)"
echo "hpl_grid=P=1,Q=3"
srun --mpi=pmix -n "${SLURM_NTASKS}" ./xhpl
echo "end_time=$(date -Is)"
hpl$ sbatch run_hpl.sbatch
Submitted batch job 14
HPL.dat的配置:
HPLinpack benchmark input file
Innovative Computing Laboratory, University of Tennessee
HPL.out output file name (if any)
6 device out (6=stdout,7=stderr,file)
1 # of problems sizes (N)
20000 Ns
1 # of NBs
128 NBs
0 PMAP process mapping (0=Row-,1=Column-major)
1 # of process grids (P x Q)
1 Ps
3 Qs
16.0 threshold
...
查看 /cluster/shared/hpl/results/slurm-7.out 中的 HPL 结果:
N=20000,即矩阵规模是\(N\times N\)的.NB=128,指的是分块大小,即block sizeP=1,网格的行数Q=3,网格的列数Time=371.39 sGflops=1.4362e+01,即约 14.362 Gflops- 残差检查 PASSED
\(P,Q\) 和 Slurm 任务数的关系是:\(P \times Q = MPI \text{进程总数} = Slurm \text{总任务数}\)
用了 3 个节点、每节点 1 个任务,所以总任务数是 3 个;因此 HPL.dat 设为 \(P=1, Q=3\).
性能调优¶
我实现了\(N\)的大小尝试、\(P,Q\)参数切换、编译参数的更换.

调整 HPL.dat 参数¶
-
调整问题规模 N 以充分利用内存
我目前每个节点的总内存不是很大,并且slurm可用的均为单核,所以如果向>20000调整需要谨慎,防止oom.
一次性脚本:
- N=2000: 在
slurm-15.out中 - N=4000: 在
slurm-16.out中 - N=8000: 在
slurm-17.out中 - N=12000: 在
slurm-18.out中,此时运行已经有点慢了 - N=16000: 在
slurm-19.out中 - N=20000:baseline,取
slurm-7.out的数据 - N=24000:在
slurm-20.out中
到N=12000以后的epoch时,单个进程就需要20min以上,等待了很久.
N 完成的测试组合数 最佳时间 / s 最佳性能 / GFLOPS 2000 18 0.33 16.38 4000 18 2.33 18.32 8000 18 18.22 18.74 12000 16 65.98 17.46 16000 6 147.12 18.56 20000 2 360.27 14.81 24000 1 1023.4 9.01 从中可以看到,最佳性能随\(N\)的增大呈现先上升后下降的趋势,在\(N=8000\)时达到最高;同时,
run_hpl.sbatch我设置的时限为 20 分钟,因此从 \(N=12000\) 开始,部分作业已经无法完整跑完,slurm-18.err、slurm-19.err和slurm-20.err中都出现了DUE TO TIME LIMIT. 综合来看,当前环境下 \(N=8000\) 左右是更合适的取值. - N=2000: 在
-
调整 \(P\times Q\) 进程网格布局以匹配集群拓扑
我一开始选择的是\(P\times Q = 1\times 3\),这个似乎也只能尝试\(3\times1\)的搭配,但是按照实验文档FAQ第1条的意思,并非好选择.
编译优化¶
编译参数设置,在gcc编译器下我开了-O3优化.
调度系统选择¶
比较了srun(slurm原生调度系统)和mpirun,最终提交脚本采用的是 Slurm 原生的:
bash
srun --mpi=pmix -n "${SLURM_NTASKS}" ./xhpl
在 Slurm 环境中,srun 可以直接继承作业申请到的节点与任务信息,配置更直接,也更不容易出现资源映射不一致的问题,因此更适合作为正式提交方式。另一方面,从结果文件还能看到,早期作业slurm-4.err曾出现 PMIx 的 munge 组件告警;后续脚本中通过设置:
规避了这一问题,之后的提交过程更加稳定。因此,本实验最终选择 srun 作为正式调度方式,而不是额外再用 mpirun 手动管理进程分发.
Bonus¶
没时间了,跑步进入期末补天模式,前面的区域以后再来探索吧.
