引言
我之前写过一篇关于高性能科学计算的工作流与工具链的内容1,那里是从用户视角描述了在超算上做什么,怎么做。最近,我自己一人完成了从服务器和周边硬件的选择采购,到服务器上柜布线与网络拓扑设置,从集群搭建到数值软件安装的全过程,算是自己一手搭起了个小型超级计算机集群。在这个过程中,我也积累了很多经验和个人觉得的较佳实践,会在这篇分享和记录一下。更具体的关于这个集群的搭建和使用内容,可以参考我为集群写的文档2,里面的用户部分包含了集群使用的很多方面和具体软件的使用方法,这部分内容可能普适性很强,对其他集群的用户也会有所启发。而文档的管理员部分,则包含了集群搭建,从硬件到软件的方方面面,从整体框架到踩坑细节,从虚拟机集群模拟到实际配置全都很详细。我相信这也会是任何一个 HPC 风格集群搭建者和管理员很好的参考资料。
从硬件到系统
硬件没什么好说的,个人很喜欢 DELL PowerEdge R 系列的服务器,于是计算节点全都配置的 R740。交换机选的华为 S1720,因为没钱上 InfiniBand,所以就用六类网线直接用原生的千兆以太网通讯。幸好我们需要高吞吐通讯类型的计算不多,不然估计还是挺瓶颈的。因此这里就无法分享配置 InfiniBand 的硬件和软件方案了。R740 倒是有四个网口,足以支持管理网络和计算网络分离的方案,不过考虑到集群规模较小,还是只上了一套网络,并用了最简单的一个 master 多个 slave 的网络拓扑。操作系统的话,计算节点买来时就让卖家安装好了 Ubuntu Server 18.04 LTS,因此也没折腾 provision 方案(PXE 操作系统安装与 kickstart 初始化系统配置)。不过这方面的技术储备倒是有的,将来规模扩展时,可以比较轻松的应用起来。 provision 推荐的打包解决方案就是使用 cobbler。
一般来讲,对于大型的 HPC 集群,会有单独的文件系统服务器,其承载大容量的存储阵列,并且以某种分布式文件系统的方式(如 Lustre 等),通过高速局域网络向集群中其他节点提供存储,实现类似本地存储的效果。不过作为一个小集群,也一切从简了,基本上就是存储硬盘在主节点,直接通过 nfs 的方式分享给其他节点 mount。分享的文件夹包括 home 和 opt 等。这样基本上额外安装的软件都要考虑安装在这两个文件夹,从而可以提供给整个集群的各个节点直接使用。至于其他 ntp 保持时间同步等服务,就不赘述了。这里说的所有配置都可以通过下面要讲的 Ansible 自动完成。
Ansible
CMS 是 configuration management softwares 的缩写,事实上这类软件大都包含一种 DSL (Domain specific language),用来描述各个节点应在的状态,应有的配置。这类工具特别适合对于整个集群进行自动化部署。事实上集群运维的最大困难,不是怎么同时在多台电脑上输入命令,这通过诸如 pdsh 这类的轻量级并行 shell 工具很容易实现。维护集群最大的困难,其实是,当一台新电脑加入集群时,如何保证其状态和其他节点一致?如果以前所有操作都是直接用命令行做的,那么加入新的服务器时,很难复现之前服务器上的所有设置,这就会导致集群中节点配置不一致的问题。事实上,集群运维的解决方案就是 CMS,而在我的管理过程中,选择了 Ansible 作为 CMS 工具。事实上著名的 CMS 工具很多,比如 puppet 等。Ansible 吸引我的地方在与其 agentless 的特性,使用 ansible 无需在子节点上进行任何配置,这就特别适合进行一些集群的“开荒”配置工作。同时 Ansible 基于 Python,我也更加熟悉,方便将来有特殊需求时,自己能够 hack 和扩展。有了 Ansible 之后,可以将集群的几乎所有配置都纳入 ansible 一个个的 playbook,从这种角度说,ansible 也有点像所谓的 dotfiles 方案,将所有配置文件集中在一起便于管理。这样,整个集群的搭建过程这一系列动词,就可以完全变为 ansible playbooks 中的一些名词,从而可以将集群的整个设定轻松迁移到其他集群,或者轻松将新加入的节点配置得与其他节点完全同步。实现这一点的核心就是,绝不在子节点做任何手动操作,所有子节点的操作都通过描述性的 playbooks 完成,从而保证了集群的一致性。我搭建集群的 ansible playbooks 已经开源3,这里边几乎包括了集群的所有核心配置,你可以从 playbooks 中学到更多具体的配置细节,从而动手搭建属于自己的超算。
可以说 Ansible 是整个集群管理和配置的核心,其重要性怎么强调也不为过。如果不知道 CMS 的存在,就强行搭建集群的话,后期的管理复杂性和混乱将是无法想象的。同时 Ansible 也是合格的集群初始化开荒工具。首先在只有操作系统的头节点上,单独运行一次 ansible playbooks,可以完成 hosts,DHCP,NAT 等各种集群所需的网络设置。之后再将各个子节点网线连到交换机,加入集群网络,这些子节点将自动获得 ip,并已经可以顺利连接网络。此时只要各机器间可以互相 ssh,那么就可以再次运行 ansible playbooks,从而完成集群基础设施的所有设置。在这一开荒过程中,除了初始需要在子节点建立和主节点同名的账号,并运行 ssh server 以及粘贴主节点 ssh 公钥之外,子节点就不需要任何其他手动操作了。而这几个不多的操作,如果使用 cobbler 方案的话,也可以在 kickstart 的时候自动完成设置。
同时,Ansible 也用来添加新用户。考虑下手动添加新用户的工作的复杂性,其包括在所有节点 useradd
,还得把 ssh 公钥放入 authorized_keys 保证集群内 ssh 互通,此外极大可能还需要额外添加 quota 磁盘限额,和 slurm 用户限制与 QOS 添加等。而这些工作,ansible 可以轻松自动化,从而分分钟无痛创建大量用户。
Spack
作为高性能计算集群,数值计算当然是重头戏,而这需要管理大量恼人的 C,C++,Fortran 的数值库依赖。一方面用户可能要求使用不同版本的库;另一方面,不同编译器和不同 MPI 编译出的库也很可能不兼容。spack 就是轻松解决 HPC 软件管理的一揽子解决方案,真的好用,谁用谁知道。其难得的不只是用起来多么傻瓜直接,更是在需要复杂特殊解决方案时,spack 可以提供的强大扩展性和灵活性,让管理员可以放心选择 spack 生态,作为 HPC 集群上的软件管理基石。关于 spack 的具体使用和与 module 系统的结合,我已经写过一篇博客4,具体细节可以参考那里。
Slurm
HPC 风格的集群当然少不了资源调度软件,这里我选择了 Slurm,主要是我对 slurm 比较熟,功能也很强。天河2号近两万个计算节点,就是通过了一百行左右的 slurm 配置实现了轻松的资源管理和任务调度。不得不说, slurm 功能真的很强,集群调度器想有的功能它基本都提供。同时不得不说,slurm 的文档和社区是真的很一般,很多功能设置起来主要得靠自己摸索。这方面我最后的配置细节,可以参考开源的 ansible playbooks,特别是对应的 slurm.conf 配置文件的设置。估计 slurm 也是少有的没有默认配置文件,配置文件需要详细和高度的调整,才能符合预期运行的软件了。
总之,通过相当长时间的折腾,以下功能都实现了,对于现在 slurm 的效果基本满意:
- 对于指定用户进行资源限制,超过资源限制的任务将会排队
- 用户除非在对应节点提交了任务,否则无法 ssh 到子节点
- 管理员总是可以 ssh 到任意节点(Ansible 可以正常使用的保证)
- 所有提交的任务都有良好的审计,数据录入数据库供以后统计
- 不同节点分配的优先顺序调整
- 隐藏非本人用户的任务数据和状态(但我在集群并未启用该条)
用户控制
作为管理员,要操心的事就是比较多。管理员需要对用户有更严格的和更精细化的限制。基本的 linux 文件系统的 rwx 文件权限管理,以及 sbit,suid 和 ACL 等更精细化的文件权限管理工具都是必须要熟悉的。用户文件夹权限设为700是基本要求。但是需要注意,如果采取了上面的工作流,把 spack 安装在了用户路径,那么该管理用户的家目录权限需要755,使得其他用户可以使用 spack 安装的库和软件。与此同时,由于 ansible playbooks 中包含了用户信息,和配置文件密码等内容,权限需要严格设为 700,防止普通用户查看。如果系统有文件备份时,一定要保证文件备份之后权限不变或者普通用户根本无法查看。
此外,对于用户磁盘空间的管理也很有必要。特别是,一般的超算家目录空间都有限,往往比较大的空间都集中在诸如 /DATA
之类的挂载文件夹。而用户通常都很“懒”,经常将数据文件直接生成在家目录,因此对于磁盘限额很有必要。这里需要使用的工具是 quota,可以以文件系统为单位,很轻松的配置来限制各用户可以占的磁盘空间以及创建的总的文件数目。
对于子节点的计算资源,都是通过 slurm 统一管理的。因为除非 slurm 提交任务,否则用户无法 access 子节点。因此只需在 slurm 中利用 slurmdbd 支持的 sacctmgr
工具,来配置相应的 QOS 组,用来限制不同用户可以消耗的子节点资源总数。
与此同时,由于主节点作为登陆节点,承担着很多用户登陆,传文件,编译程序,运行小规模脚本等任务,因此应该限制普通用户大量占用主节点资源而影响其他用户的正常使用。由于用户在主节点上可以直接 ./a.out
的方式运行程序而绕开 slurm 的 QOS,因此还需要额外的限制。这里需要利用的工具是 ulimit。具体的,可以通过在主节点 /etc/security/limits.d
中添加配置的方式,限制各种系统资源,比如 cpu 时间或内存大小,从而防止用户大量占用主节点的资源。特别是对于进程数目的限制,可以有效防止用户误输入 fork 炸弹之类的命令,或者用户自己写的程序出现严重 bug 导致的操作系统崩溃。除了 ulimit,也可以利用 cgroup 工具来更精细的限制用户和进程的资源使用。
安全
既然都搭建集群了,那么安全自然是不得不考虑的问题。上面讨论的对用户的各种限制和管理,可以算是安全的一部分。此外,安全更重要的是防范系统遭到入侵,由于 HPC 集群的特殊性,很多时候无法及时升级,因此安全的要求就更加迫切。
最基本的安全实践就是断外网。整个集群只通过 http proxy 的方式和外界联系,这样就屏蔽了大量的安全隐患,因为所有外来攻击都无法主动到达本机。当然还是要小心 HPC 所连接的内容的安全性,否则不安全内容可以以被动回复的方式到达本机。当然 air gapped system 也有不方便的地方,很多软件有自己单独的 http proxy 配置方式,还有些软件根本不支持代理配置,因此完全断网的系统,还有很多连接方面需要折腾的内容,并不是简单的一个 export http_proxy=blah
就可以都解决的。而外界想要合法的登陆集群的话(比如 ssh),则可以通过跳板机提供端口映射服务。需要注意的时,当你需要向外提供更多的 web 服务时,比如 jupyter,可能需要更多端口映射。另一个方案则是搭建一个沟通外网和集群的 VPN,供用户使用,这也是天河采用的方案。
另一个重要的安全实践就是备份,将重要的文件保持一定的自动备份频率。所谓的安全问题,主要后果有两种,一种是数据没了或打不开了(比如加密勒索软件),另一种是数据被别人看了(比如用户密码数据库)。对于后者,似乎 HPC 问题不严重,因为至少我们的 HPC 上也没什么敏感数据。而对于前者,定时备份无疑一劳永逸,不用担心文件丢失;既应对了可能的安全隐患,也应对了硬盘损坏的硬件风险。当然这里的备份,不能只在集群内部进行,否则集群被攻破之后还是没用。因此至少还要有一份备份通过 rsync 等方式备份到外部服务器,或通过各种云盘的命令行工具备份到各种云服务或云主机(可参考 rclone 等工具)。
除此之外,安全意识一定要强,这个可能只有吃过亏才会真重视起来。在尽可能的情况下把相应的软件服务及时升级到最新的稳定版,特别是会向外暴露网络端口的服务。对于基于网络的集群内部服务,一定要确保相应的服务只监听集群局域网的 ip,而不对公网 ip 开放。此外,如果部署了各种 web 可视化的监视和管理前端的话,一定要用密码进行保护,nginx 和 apache 都有相似的密码保护 server 的设置,一定要启用。此外要注意关注相应的日志,比如 ssh 登陆日志和 nginx 访问日志等等,发现异常要及时处理。由于日志数量巨大,内容复杂,建议部署 ELK 日志管理工具栈,可以更友好的跟踪和可视化相关日志的关键指标。对于 ssh 本身,建议改端口,关 root 登陆。至于关密码登陆,我倒是不太赞成。因为一旦管理员的私钥丢失,或者发生其他什么奇怪的情况,那么你可能只能去机房接显示器了,还是不太方便。
Singularity
最后稍微提一下,现在在 HPC 社区越来越火的 HPC 上的容器方案,singularity。Singularity 可以通过 spack 直接安装,需要记得安装后执行一个脚本,来为一些文件设置 suid,从而使得普通用户可用。具体 tweak 可以 spack edit singularity
查看。Singularity 号称是 HPC 上的 docker 方案,其解决了 docker 需要具有 root 权限的 daemon 从而无法使得不信任的用户使用的弊端。同时 singularity 具有良好的 GPU 和 MPI 等支持,特别适合高性能计算环境。事实上,singularity 隔离性比 docker 差太多,甚至可以简单看作一个进程。这也意味着其上手难度和学习成本比 docker 都低了很多。其还原生支持 docker 镜像,同时具有自己的镜像 hub,甚至有和 k8s 结合的方案。某种程度上 singularity 可能代表着 HPC 未来的发展方向,打包复杂的软件开发环境,使得结果复现更加有保证。也可以更轻松地在多台 HPC 集群上进行部署和同一种计算任务。也许以后有时间,单独写一篇 singularity 相关的内容。
本篇内容只是简单介绍了一些集群上基础设施搭建和设置的经验,至于具体的计算工具链,比如选用什么 lapack,使用怎样的 python 工作流,mathematica 如何在集群并行计算等等和科学计算相关具体话题并没有涉及,这些科学计算的软件与工作流选择及在集群上的配置,可能另文记述。