MPI(Message Passin Interface)广泛应用于高性能服务器中,可用于单系统多节点或者多个计算平台间通讯,目前主流的两个开源软件分别是OpenMPI和MPICH。Singularity同时支持这两个开源工具,本篇教程介绍如何在Singularity容器开发和运行MPI程序。
在Singularity容器是执行MPI程序最常见的方法就是使用宿主机中的MPI。由于我们会同时使用宿主机的MPI和容器中的MPI,因此这被称为Host MPI或者混合模型。考虑到大部分高性能计算平台并不都支持宿主机和容器之间的文件系统共享,因此我们不讨论如何将存储盘挂载到容器,从而使用容器中宿主机MPI。
混合途径的基本想法就是,当你要执行SIngularity容器的MPI代码时,你会使用mpiexec
类似命令去调用singularity
命令。容器外部的MPI进程会和容器内的MPI进行写作,容器后的MPI代码会实例化任务。
Open MPI/Singularity的工作流程如下
用户在shell中启动MPI(mpirun, mpiexec)
Open MPI接着调用进程管理守护进程(process management daemon, ORTED)
ORTED进程启动所需的Singularity容器
Singularity构建容器和命名空间环境
Singularity启动容器内的MPI应用
MPI进程启动,加载Open MPI库
Open MPI库通过进程管理接口(Process Management, PMI)连接回ORTED进程
此时在容器运行MPI就和直接宿主机运行MPI程序一样。优势在于它能够整合到Slurm等资源管理工具,并且和之前运行MPI应用一样。但是你需要保证容器中的MPI和宿主机的MPI版本兼容,也就是在构建MPI容器时保证和系统的MPI一致。
我们举个简单的例子。假设我们已经有了一个叫做mpitest.c
MPI应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <mpi.h> #include <stdio.h> #include <stdlib.h> int main (int argc, char **argv) { int rc; int size; int myrank; rc = MPI_Init (&argc, &argv); if (rc != MPI_SUCCESS) { fprintf (stderr , "MPI_Init() failed" ); return EXIT_FAILURE; } rc = MPI_Comm_size (MPI_COMM_WORLD, &size); if (rc != MPI_SUCCESS) { fprintf (stderr , "MPI_Comm_size() failed" ); goto exit_with_error; } rc = MPI_Comm_rank (MPI_COMM_WORLD, &myrank); if (rc != MPI_SUCCESS) { fprintf (stderr , "MPI_Comm_rank() failed" ); goto exit_with_error; } fprintf (stdout , "Hello, I am rank %d/%d" , myrank, size); MPI_Finalize(); return EXIT_SUCCESS; exit_with_error: MPI_Finalize(); return EXIT_FAILURE; }
MPI只是一个库接口,里面的函数能够被其他编程语言所调用,所以支持Fortran, C, Python, R等。
接下来根据宿主机的MPI版本,编写一个定义文件。假如我们宿主机的MPI是MPICH,那么定义文件(命名为mpitest)如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 Bootstrap: docker From: ubuntu:latest %files mpitest.c /opt %environment export MPICH_DIR=/opt/mpich-3.3 export SINGULARITY_MPICH_DIR=$MPICH_DIR export SINGULARITYENV_APPEND_PATH=$MPICH_DIR /bin export SINGULAIRTYENV_APPEND_LD_LIBRARY_PATH=$MPICH_DIR /lib %post echo "Installing required packages..." apt-get update && apt-get install -y wget git bash gcc gfortran g++ make export MPICH_VERSION=3.3 export MPICH_URL="http://www.mpich.org/static/downloads/$MPICH_VERSION /mpich-$MPICH_VERSION .tar.gz" export MPICH_DIR=/opt/mpich echo "Installing MPICH..." mkdir -p /tmp/mpich mkdir -p /opt cd /tmp/mpich && wget -O mpich-$MPICH_VERSION .tar.gz $MPICH_URL && tar xzf mpich-$MPICH_VERSION .tar.gz cd /tmp/mpich/mpich-$MPICH_VERSION && ./configure --prefix=$MPICH_DIR && make install export PATH=$MPICH_DIR /bin:$PATH export LD_LIBRARY_PATH=$MPICH_DIR /lib:$LD_LIBRARY_PATH export MANPATH=$MPICH_DIR /share/man:$MANPATH echo "Compiling the MPI application..." cd /opt && mpicc -o mpitest mpitest.c
如果是Open MPI,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 Bootstrap: docker From: ubuntu:latest %files mpitest.c /opt %environment export OMPI_DIR=/opt/ompi export SINGULARITY_OMPI_DIR=$OMPI_DIR export SINGULARITYENV_APPEND_PATH=$OMPI_DIR /bin export SINGULAIRTYENV_APPEND_LD_LIBRARY_PATH=$OMPI_DIR /lib %post echo "Installing required packages..." apt-get update && apt-get install -y wget git bash gcc gfortran g++ make file echo "Installing Open MPI" export OMPI_DIR=/opt/ompi export OMPI_VERSION=4.0.1 export OMPI_URL="https://download.open-mpi.org/release/open-mpi/v4.0/openmpi-$OMPI_VERSION .tar.bz2" mkdir -p /tmp/ompi mkdir -p /opt cd /tmp/ompi && wget -O openmpi-$OMPI_VERSION .tar.bz2 $OMPI_URL && tar -xjf openmpi-$OMPI_VERSION .tar.bz2 cd /tmp/ompi/openmpi-$OMPI_VERSION && ./configure --prefix=$OMPI_DIR && make install export PATH=$OMPI_DIR /bin:$PATH export LD_LIBRARY_PATH=$OMPI_DIR /lib:$LD_LIBRARY_PATH export MANPATH=$OMPI_DIR /share/man:$MANPATH echo "Compiling the MPI application..." cd /opt && mpicc -o mpitest mpitest.c
接着我们从定义文件中构建出sig文件, singularity build mpitest.sig mpitest
最后,执行Singularity容器的MPI应用的标准方法就是在host中运行mpirun
命令,它会启动Singularity容器,并最终运行其中的MPI程序。
1 2 3 mpirun -n 4 singularity exec mpitest.sig /opt/mpitest
如果是在SLURM这类任务提交系统中,我们需要批处理脚本来执行MPI引用,举个例子
1 2 3 4 5 6 #!/bin/bash mpirun -n $NP singularity exec /var/nfsshare/gvallee/mpich.sif /opt/mpitest
执行方式如下
参考资料: https://sylabs.io/guides/3.3/user-guide/mpi.html