Skip to main content

Lmod - 使用集群上的软件

Lmod 是一个环境变量管理软件,是基于 Lua 的模块系统(Module System,或 Environment Module System) 实现。 在其他超算系统上,使用的通常是另一个基于 Tcl 的实现:Environment Modules

通过 Lmod,用户可以:

  • 加载/卸载软件;
  • 切换软件,如 Anaconda 和 Python;
  • 切换软件版本,如 GCC 7.3.0 和 GCC 8.2.0。

当用户想要使用软件时,只要运行相应命令来加载软件的环境变量即可。Lmod 提供了 moduleml命令供用户选择,让用户能够用命令来加载、卸载、查找已安装的软件。

caution

目前,实验室的集群已经用 Lmod 代替了以前的 Environment Modules。 使用旧模块系统的用户请参考 Tcl 旧模块的处理

note

为了方便叙述,在本节中我们把 Lmod 和 Environment Modules 两种实现统称为模块系统,并将 Environment Modules 简称为 Modules。

快速入门

$ man module                    # manpage
$ module help # help

$ module avail # 查看当前可加载的模块(软件)
$ module avail GCC # 查看当前可加载的包含'GCC'的模块
$ module spider # 查看所有可加载的模块(软件)
$ module spider GCC # 查看GCC有多少不同版本已安装在集群上
$ module spider GCC/8.2.0 # 查看必须手动加载的依赖
$ module keyword GCC # 在模块名称和描述中查找关键字

$ module load GCC/8.2.0 # 加载模块,从而能使用GCC/8.2.0
$ module list # 列出已加载的模块
$ module unload GCC/8.2.0 # 卸载模块

$ module save mymods # 保存已加载的模块到列表中
$ module savelist # 查看已保存的加载方案
$ module restore mymods # 恢复已保存的加载方案

$ module purge # 卸载所有已加载的模块

$ ml # module命令的前端,单独调用相当于 module list
$ ml avail # module avail
$ ml spider # module spider
$ ml GCC/8.2.0 # module load GCC/8.2.0
$ ml -GCC/8.2.0 # module unload GCC/8.2.0
$ ml -GCC/8.2.0 GCC/7.3.0 # module swap

更多关于 Lmod 的用法,可参考官方文档:

初始环境变量

如果不加载软件,用户使用的就是系统自带的软件,例如, GCC 可能使用的就是 /usr/bin 下面的 GCC 4.8.5,Python 可能使用的是 /usr/bin 下面的 Python 2.7.5。

Shell的配置

目前默认的配置是 bash,如果需要使用其他 shell 加载模块,可以在 Lmod 目录下找相应的配置脚本,或联系管理员解决。

依赖项的切换

多数情况下使用 Lmod 切换模块都不需要卸载之前的模块,例如,

$ module load GCC/7.3.0
$ module load GCC/8.2.0

执行第二条命令会自动切换相应的依赖项。

module availmodule spider

Lmod 有两个查询命令:module availmodule spider,Environment Modules 只有 module avail。区别如下:

  • module avail (Lmod):查询当前可加载的软件包。如果一个软件包需要一些依赖项,在依赖项被加载之前它可能不会显示在列表里。
  • module spider (Lmod):查询所有可加载的软件包。
  • module avail (Modules):查询所有可加载的软件包。

moduleml

ml 实际上是 Lmod 中的一个脚本,它像 spack 一样给用户提供便利的功能。Modules 从 4.5 开始也引入了 ml

Lmod:https://lmod.readthedocs.io/en/latest/010_user.html#ml-a-convenient-tool

Modules:https://modules.readthedocs.io/en/latest/ml.html

模块层次

参考

科学计算软件的依赖比较复杂,许多软件在特定编译器/依赖版本下能顺利编译和运行,换作其他编译器/依赖就出现很多意想不到的错误(如链接库错误)。 EasyBuild & Lmod 使用模块层次来处理这种软件间的依赖关系。

Lmod 的每个模块都可以依赖于其他模块,依赖关系也有不同类型,最终形成像文件系统一样的层次结构。用户可以用 module avail 查看当前可加载的模块,该命令的结果会随着用户加载的模块不同而变化,因为模块文件所在的路径 $MODULEPATH 会在运行时被修改。

例如,OpenMPI 只有在加载了特定编译器之后才会显示在 module avail 结果中:

## 清空所有已加载的模块,保证环境是干净的
$ module purge

## 查看当前可加载的模块,OpenMPI不会显示
$ module avail

GCC/8.2.0-2.31.1

## 加载GCC后再次查看模块列表,OpenMPI已经可以加载
$ module load GCC/8.2.0-2.31.1
$ module avail

GCC/8.2.0-2.31.1 (L) OpenMPI/3.1.3

如果未加载依赖项,直接加载 OpenMPI/3.1.3 会报错:

## 清空所有已加载的模块,保证环境是干净的
$ module purge

## 跳过GCC,直接加载OpenMPI
$ module load OpenMPI/3.1.3

Lmod has detected the following error: These module(s) exist but cannot be loaded as requested:
"OpenMPI/3.1.3"
Try: "module spider OpenMPI/3.1.3" to see how to load the module(s).

## 查看OpenMPI所需的依赖项
$ module spider OpenMPI/3.1.3

-----------------------------------------------------------
OpenMPI: OpenMPI/3.1.3
-----------------------------------------------------------
Description:
The Open MPI Project is an open source MPI-3 implementation.

You will need to load all module(s) on any one of the lines below before the "OpenMPI/3.1.3" module is available to load.

GCC/8.2.0-2.31.1

在用户没有加载任何模块的情况下,可以加载的模块称为 核心 (Core) 模块。其模块所在路径也以 Core 命名:

/apps/modulefiles/Core
/apps/lmod/lmod/modulefiles/Core

为了避免手动加载依赖项,用户可以为当前加载的模块创建 savelist ,或者使用核心模块中的工具链。关于工具链的说明见后续小节。

模块名称及版本

集群上所有 Lmod 的模块文件均由 EasyBuild 生成,采用 EasyBuild 的命名规则。

切换软件版本

集群上有的软件是用同一编译器编译的。假设有4个模块:GCC/8.2.0, OpenMPI/3.1.3, OpenMPI/4.0.0, GCC/9.1.0。 其中两个 OpenMPI 都是由 GCC/8.2.0 编译的。在下面的演示中,我们先加载一个 OpenMPI,再切换到另一个 OpenMPI 版本,最后切换 GCC 的版本。

## 使用OpenMPI之前必须加载GCC/8.2.0,否则报错
## module load OpenMPI/3.1.3
$ module load GCC/8.2.0-2.31.1 OpenMPI/3.1.3

## 成功加载OpenMPI/3.1.3后,切换到OpenMPI/4.0.0
$ module load OpenMPI/4.0.0

The following have been reloaded with a version change:
1) OpenMPI/3.1.3 => OpenMPI/4.0.0 2) hwloc/1.11.11 => hwloc/2.0.2

## 在加载了GCC/8.2.0和OpenMPI/3.1.3的情况下,切换到GCC/9.1.0
## 此时OpenMPI会进入inactive状态
$ module load GCC/9.1.0-2.32

Inactive Modules:
1) OpenMPI/4.0.0 2) hwloc/2.0.2 3) libxml2/2.9.8

Due to MODULEPATH changes, the following have been reloaded:
1) XZ/5.2.4 2) libpciaccess/0.14 3) numactl/2.0.12 4) zlib/1.2.11

The following have been reloaded with a version change:
1) GCC/8.2.0-2.31.1 => GCC/9.1.0-2.32 2) GCCcore/8.2.0 => GCCcore/9.1.0 3) binutils/2.31.1 => binutils/2.32

最后一条命令输出了三类信息:

  • Inactive Modules:模块不再生效。这是由于 OpenMPI/4.0.0 这些软件包并没有针对 GCC/9.1.0 安装的版本。当我们把原本的 GCC/8.2.0 切换为 GCC/9.1.0 后,Lmod 找不到这个版本的编译器对应的 OpenMPI/4.0.0。解决办法:查看并加载 GCC/9.1.0 对应的 OpenMPI 版本。

  • ...have been reloaded:模块被重新加载。这是由于 libpciaccess/0.14 这些软件包在两个GCC版本下都存在,但安装路径不同。

  • ...reloaded with a version change:模块被重新加载为不同版本。这是由于这些模块在两个GCC版本下都存在,但版本和路径都不同。

默认模块

参考:

如果用户在加载模块时不指定版本,那么 Lmod 会加载默认模块。在模块列表中,默认模块的名字后面有个 (D)。用户可以用命令查看当前可加载的所有默认模块:

$ module -d avail

例如,在实验室集群上,以下两条命令的效果是相同的:

$ module load GCC/8.2.0-2.31.1

$ module load GCC

默认模块可以由模块名目录下的配置文件 .modulerc.lua.modulerc 指定。例如,以下命令可以指定 GCC 的默认版本为 8.3.0:

$ MY_GCC_DIR="/apps/modulefiles/Core/GCC"
$ echo 'module_version("2.31.1","default")' > $MY_GCC_DIR/.modulerc.lua

普通用户无法通过这种方式设置公共Lmod的默认模块,只能为自定义的模块设置。

工具链

在科学计算中,多个编译工具、数学库往往会组合在一起使用。 实验室集群使用 EasyBuild 来生成 Lmod 模块文件,除了单个软件包的模块,还提供了工具链 (toolchains),每个工具链都是一组模块,这些软件模块相互之间有依赖关系。

加载工具链

加载一个工具链时,相当于同时加载了一系列模块。 使用工具链可以很方便地编译绝大多数的 C/C++/Fortran 程序。 加载工具链和卸载工具链可以帮助用户快速切换所需的编译器,而不用手动加载依赖项。

例如,gmpich 就是一些与 MPICH 相关的编译工具,它包括 gccmpiccgompi 则是一些与 OpenMPI 相关的编译工具。 下面两条命令分别加载了这两个工具链的默认版本。

## 加载工具链gmpich
$ module load gmpich

## 清空当前加载的模块,因为同时只能使用一个MPI实现
$ module purge

## 加载工具链gompi
$ module load gompi

每个工具链都依赖于一些编译器和库。例如,在实验室集群上,gompi/2019a 工具链主要依赖于 GCC/8.2.0-2.31-1OpenMPI/3.1.3,其中的 GCCOpenMPI 又有各自的依赖项。

如果想确认一个工具链里具体包含哪些模块,我们可以先加载工具链,再用命令查看所有加载进来的模块:

## 加载gompi
$ module purge
$ module load gompi/2019a

## 查看当前加载的模块
$ module list
Currently loaded Modules:
1) GCCcore/8.2.0 4) zlib/1.2.11 7) libxml2/2.9.8 10) OpenMPI/3.1.3
2) binutils/2.31.1 5) numactl/2.0.12 8) libciaccess/0.14 11) gompi/2019a
3) GCC/8.2.0-2.31-1 6) XZ/5.2.4 9) hwloc/1.11.11

或者检查 modulefile 中的 depends_on 语句:

$ module show gompi/2019a |& grep depends_on

depends_on("GCC/8.2.0-2.31.1")
depends_on("OpenMPI/3.1.3")
Spack的工具链

集群上的公共 Spack 也可以加载 gompigmpich 等少量工具链,具体用法请参考文档中 Spack 章节。

卸载工具链

卸载工具链时会把所有依赖项全部卸载。

## 从当前shell卸载整个工具链
$ module unload gompi/2019a

$ module list
No modules loaded

切换工具链

切换工具链时,建议使用 ml 的切换语法或者 swap 等切换命令。 例如,我们可以通过以下命令在加载 gompi/2019a 之后切换到 gmpich/2019a

## 加载gompi
$ module load gompi/2019a

## 切换到gmpich
$ module swap gompi/2019a gmpich/2019a

常用的工具链见集群文档的公共软件列表。

保存加载的模块

用户在使用 Lmod 时,通过 ml save 命令,可以把已经加载的模块保存起来,便于下次使用。 保存模块的配置信息位于用户的 ~/.lmod.d 目录下。

## 加载一些模块
$ module load CMake/3.19.1 GCC/8.2.0-2.31-1 OpenMPI/3.1.3

## 保存到名为devtools的列表
$ module save devtools

Saved current collection of modules to: "devtools"

## 查看所有已保存的配置
$ module savelist
Named collection list:
1) devtools

## 清空当前环境,加载之前保存的配置
$ module purge
$ module restore devtools

Restoring modules from user's devtools

## 弃用一个已保存的加载配置
$ module disable devtools

自定义软件和模块

参考:

预装的软件都以是特定的编译选项编译的,可能无法完全满足你的需求。普通用户无法更改公共的软件,但可以向 Lmod 添加自定义的模块文件。因此,我们只要为自己安装的软件写 modulefile,让 Lmod 能够搜索到即可。

步骤总结如下:

  • 建立一个目录用于存放 modulefiles,如 $HOME/modulefiles/
  • 使用 module use 添加该目录到 Lmod 搜索路径;
  • 安装软件到自己的家目录($HOME);
  • 为安装的软件写一个 modulefile,放在自己的 modulefiles 目录下。

假设用户安装了 GCC/10.2.0,并且写了一个名为 gcc-10.2.0.lua 的 modulefile 放在 $HOME/modulefiles/ 目录下。为了让 Lmod 能搜索到这个模块,执行命令

$ module use $HOME/modulefiles

随后便可以加载自定义的模块

$ module load gcc-10.2.0

在这个例子中,我们的模块文件名为 gcc-10.2.0.lua,由 module 命令显示的名称为 gcc-10.2.0。 根据 Lmod 的模块层次,如果我们想要一个名为 gcc/10.2.0/mpich/3.3.2 的模块,就应该创建如下文件

$ mkdir -p $HOME/modulefiles/gcc/10.2.0/mpich
$ touch $HOME/modulefiles/gcc/10.2.0/mpich/3.3.2.lua

## 添加Lmod搜索路径
$ module use $HOME/modulefiles
更新 cache

实验室集群上的 Lmod 配置了 cache 文件来加快模块的访问速度,这些文件每小时都被更新。 新的模块被添加后,若 Lmod 的 cache 文件没有正常更新,就需要管理员来手动更新。相关命令和路径如下:

  • 命令:/apps/lmod/lmod/libexec/update_lmod_system_cache_files
  • cache 目录:/apps/moduleData/cacheDir
  • system 目录:/apps/moduleData/system.txt

自定义模块的命名

为防止自定义模块与系统中安装的模块发生冲突,有两种解决方法。

第一种是以不同风格命名。例如,系统中已安装 MPICH/3.2,如果用户想使用自己编译的 MPICH,可以命名为 mpich/3.2, user/mpich/3.2 等。

第二种是修改 Lmod 搜索路径。只有处于 Lmod 搜索路径中的同名模块才会冲突,我们可以在需要时去掉现存的路径(例如默认加载的 Core/ 路径),只使用自己的路径。 比如,集群上的 showlegacy 模块文件里实际上是一句 module use,它启用了一个新的搜索路径让 Lmod 能找到 Tcl 旧模块。 我们可以仿照 showlegacy 模块的做法,写一个模块文件,启用自定义路径的同时删除公共模块文件的路径。

编写模块文件

一个 modulefile 中最主要的就是各种环境变量,例如 PATHLIBRARY_PATH。 用户使用 module load 加载这个 modulefile 时,实际上就是在当前 shell 设置这些环境变量。 因此,用户在安装完软件后,通常需要把 binincludelib 等目录的绝对路径写在 modulefile 中。

自定义模块文件时,可以用命令查看集群上已有模块的配置文件供参考,例如,查看 GCC/8.2.0 的模块文件:

$ module show GCC/8.2.0-2.31.1

需要注意的是,集群上的模块文件通常都是 Lua 语法,在其他超算上可能需要 Tcl。Tcl 的模块文件可以参考 Tcl 旧模块。

Tcl 旧模块的处理

模块系统的迁移(指从 Environment Modules 迁移到 Lmod)并不影响 Tcl 旧的软件模块的使用, 因为 Lmod 同样兼容 Tcl 模块文件。

目前,实验室集群上的 Tcl 模块都转移到另一个路径了,因此无法直接用 module avail 命令看到。如果要使用旧的模块,请执行:

$ ml showlegacy

旧模块提供的软件安装在 /opt 下,新模块提供的软件安装在 /apps 下。

冲突的模块

为防止模块冲突,在 /opt 中安装编译工具、科学计算软件 Tcl 模块时尽量不要与 Lua 模块同名(如 GCC、OpenMPI、MPICH、PETSc 等)。除此之外,其他手动安装的软件都可随意使用。