Vamos a proceder, de forma concreta, a explicar cómo podemos ejecutar software con parelización con MPI, usando un código de ejemplo, ayudándonos de Charliecloud.
En este caso sólo tendremos que realizarlo cuando nuestro entorno miniconda esté en HPC Drago, podría ser trasladable a otros entornos o sistemas.
# ssh usuario@drago.csic.es
Tal como se indica en el listado de software disponible para usar Charliecloud siguiente charliecloud, podemos hacer uso del mismo de la siguiente forma:
[usuario@drago31010015 ~]$ module load foss/2021b
[usuario@drago31010015 ~]$ module load charliecloud/0.28
[usuario@drago31010015 ~]$ ch-
ch-checkns ch-convert ch-fromhost ch-image ch-run ch-run-oci ch-ssh ch-test
[usuario@drago31010015 ~]$ ch-
Si no lo tenemos clonado en nuestro home, procedemos tal como sigue.
Procedemos a usar por línea de comandos git para clonar proyecto drago-hpc-containers, nos autenticamos en git.csic.es con nuestro usuario.
[usuario@drago31010015 ~]$ git clone https://git.csic.es/area-de-informatica-cientifica/drago-hpc-
containers.git
Cloning into 'drago-hpc-containers'...
Username for 'https://git.csic.es': USUARIO
Password for 'https://NIF@git.csic.es':
remote: Enumerating objects: 24, done.
remote: Counting objects: 100% (24/24), done.
remote: Compressing objects: 100% (20/20), done.
remote: Total 24 (delta 5), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (24/24), 8.58 KiB | 2.14 MiB/s, done.
Resolving deltas: 100% (5/5), done.
[usuario@drago31010015 ~]$ ls -lh
[usuario@drago31010015 ~]$ ls -lh
total G
drwxr-xr-x 10 usuario grupo 4.0K Jan 17 10:58 drago-hpc-containers
En primer lugar, accedemos al directorio generado a partir de clonar el proyecto drago-hpc-containers:
[usuario@drago31010015 ~]$ cd drago-hpc-containers
[usuario@drago31010015 drago-hpc-containers]$ ls -lh
total 32K
-rw-r--r-- 1 usuario grupo 2.6K Jan 17 10:28 README.md
drwxr-xr-x 2 usuario grupo 4.0K Jan 17 10:28 almalinux8
drwxr-xr-x 2 usuario grupo 4.0K Jan 17 11:11 centos7
drwxr-xr-x 2 usuario grupo 4.0K Jan 17 11:11 mpihelloworld
drwxr-xr-x 2 usuario grupo 4.0K Jan 17 11:11 openmpi
drwxr-xr-x 2 usuario grupo 4.0K Jan 17 11:22 rockylinux8
drwxr-xr-x 3 usuario grupo 4.0K Jan 17 11:44 scuff
drwxr-xr-x 3 usuario grupo 4.0K Jan 17 10:28 trinity
[usuario@drago31010015 drago-hpc-containers]$
Accedemos, al directorio openmpi, y vemos su contenido.
[usuario@drago31010015 mpihelloworld]$ cd ../openmpi/
[usuario@drago31010015 openmpi]$ ls -lh
total 20K
-rw-r--r-- 1 usuario grupo 5.0K Sep 18 17:18 Dockerfile
-rw-r--r-- 1 usuario grupo 5.0K Jan 17 10:28 Dockerfile.openmpi_2_1_6
-rw-r--r-- 1 usuario grupo 5.0K Jan 17 10:28 Dockerfile.openmpi_4_1_1
-rw-r--r-- 1 usuario grupo 5.0K Sep 18 17:18 Dockerfile.ori
-rw-r--r-- 1 usuario grupo 807 Sep 18 17:18 dont-init-ucx-on-intel-cray.patch
Podemos finalmente a construirla, pero previamente podemos ver el contenido del Dockerfile, que entre otras cosas (compiladores, etc) instala cliente slurm, versión concreta de openmpi y sus dependencias requeridas.
[usuario@drago31010015 openmpi]$ cat Dockerfile
Finalmente, construimos.
[usuario@drago31010015 openmpi]$ ch-image build --force .
inferred image name: openmpi
2. FROM almalinux8
copying image ...
will use --force: rhel8: RHEL 8+ and derivatives
63. RUN.F dnf install -y --setopt=install_weak_deps=false
automake
file
flex
gcc
gcc-c++
gcc-gfortran
git
....
RUN.F ldconfig
RUN.F echo 'plm_rsh_agent = false' >> /mnt/0/openmpi-mca-params.conf
--force: init OK & modified 1 RUN instructions
grown in 14 instructions: openmpi
build slow? consider enabling the new build cache
hint: https://hpc.github.io/charliecloud/command-usage.html#build-cache
[usuario@drago31010015 openmpi]$ ch-image list
almalinux:8
almalinux8
centos:7
openmpi
Una vez finaliza la construcción y si no ha habido errores el siguiente paso.
Accedemos al directorio que contiene mpihellowold y procedemos a mostrar y construir la imagen a partir del Dockerfile.
[usuario@drago31010015 openmpi]$ cd ../mpihelloworld/
[usuario@drago31010015 mpihelloworld]$ ls -lh
total 16K
-rw-r--r-- 1 usuario grupo 113 Sep 18 17:18 Dockerfile
-rw-r--r-- 1 usuario grupo 111 Sep 18 17:18 Makefile
-rw-r--r-- 1 usuario grupo 325 Sep 18 17:18 helloworld.c
-rwxr-xr-x 1 usuario grupo 1.3K Sep 18 17:18 slurm.sh
[usuario@drago31010015 mpihelloworld]$ cat Dockerfile
# ch-test-scope: full
FROM openmpi
# This example
COPY . /helloworld
WORKDIR /helloworld
RUN make clean && make
[usuario@drago31010015 mpihelloworld]$ ch-image build --force .
inferred image name: mpihelloworld
2. FROM openmpi
copying image ...
will use --force: rhel8: RHEL 8+ and derivatives
5. COPY ['.'] -> '/helloworld'
6. WORKDIR /helloworld
7. RUN.F make clean && make
/bin/rm -f helloworld *.o
mpicc -o helloworld helloworld.c
warning: --force specified, but nothing to do
grown in 4 instructions: mpihelloworld
build slow? consider enabling the new build cache
hint: https://hpc.github.io/charliecloud/command-usage.html#build-cache
Listamos y vemos que tenemos finalmente el contenedor mpihelloworld
[usuario@drago31010015 mpihelloworld]$ ch-image list
almalinux:8
almalinux8
centos:7
mpihelloworld
openmpi
En el mismo directorio donde tenemos el Dockerfile del código mpi para construirlo, tenemos un ejemplo de código con script para slurm con SBATCH para lanzar un job con el contenedor.
El contenido del script slurm.sh es tal como sigue:
[usuario@drago31010015 mpihelloworld]$ cat slurm.sh
#!/bin/bash
#SBATCH --partition=special # Specify partition name for job execution
# ### PART 2: modify according to your requirements:
# Arguments: Path to tarball, path to image parent directory.
set -e
tar=$1
imgdir=$2
#img=${2}/$(basename "${tar%.tar.gz}")
img=${2}/"${tar%%.*}"
if [[ -z $tar ]]; then
echo 'no tarball specified' 1>&2
exit 1
fi
printf 'tarball: %s\n' "$tar"
if [[ -z $imgdir ]]; then
echo 'no image directory specified' 1>&2
exit 1
fi
printf 'image: %s\n' "$img"
# Make charliecloud available (varies by site).
module purge
module unuse /dragofs/sw/campus/0.2/modules/all/Core
module unuse /dragofs/sw/restricted/0.2/modules/all/Core
module load foss/2021b
module load charliecloud
rm -rf /tmp/mpihelloworld
# Unpack image.
#srun ch-convert -o dir "$tar" "$imgdir"
srun ch-convert -o dir "$tar" /tmp/mpihelloworld
# MPI version in container.
printf 'container: '
#ch-run "$img" -- mpirun --version | grep -E '^mpirun'
srun ch-run /tmp/mpihelloworld -- mpirun --version | grep -E '^mpirun'
# Run the app.
#srun ch-run "$img" -- /helloworld/helloworld
srun ch-run /tmp/mpihelloworld -- echo hola
srun ch-run /tmp/mpihelloworld -- hostname
srun ch-run -w /tmp/mpihelloworld -- /helloworld/helloworld
[usuario@drago31010015 mpihelloworld]$
Vamos a comprimir nuestra imagen de contenedor mpihelloworld tal como sigue para cuando se lance el job se genere un contenedor en cada nodo a partir de ese tar.gz que generamos ahora para poder hacer uso del mismo.
[usuario@drago31010015 mpihelloworld]$ ch-convert mpihelloworld mpihelloworld.tar.gz
input:
ch-image mpihelloworld
output: tar
mpihelloworld.tar.gz
exporting ...
done
[usuario@drago31010015 mpihelloworld]$ ls -lh mpihelloworld.tar.gz
-rw-r--r-- 1 usuario grupo 255M Sep 19 19:43 mpihelloworld.tar.gz
El anterior tar.gz es usado por el script slurm.sh tal como se indica previamente para generar el contenedor en cada nodo que nos reserve SLURM.
Y finalmente lanzamos el job.
[usuario@drago31010015 mpihelloworld]$ sbatch -N10 slurm.sh
~/drago-hpc-containers/mpihelloworld/mpihelloworld.tar.gz /tmp/mpihelloworld
Submitted batch job 8513
[usuario@drago31010015 mpihelloworld]$ squeue
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
8513 special slurm.sh usuario R 0:01 10 drago[31010005-31010014]
[usuario@drago31010015 mpihelloworld]$ scontrol show jobid 8513
JobId=8513 JobName=slurm.sh
UserId=usuario() GroupId=grupo() MCS_label=N/A
Priority=4294894472 Nice=0 Account=grupo QOS=normal
JobState=COMPLETED Reason=None Dependency=(null)
Requeue=1 Restarts=0 BatchFlag=1 Reboot=0 ExitCode=0:0
RunTime=00:00:13 TimeLimit=7-10:00:00 TimeMin=N/A
SubmitTime=2022-09-19T19:49:16 EligibleTime=2022-09-19T19:49:16
AccrueTime=2022-09-19T19:49:16
StartTime=2022-09-19T19:49:17 EndTime=2022-09-19T19:49:30 Deadline=N/A
PreemptEligibleTime=2022-09-19T19:49:17 PreemptTime=None
SuspendTime=None SecsPreSuspend=0 LastSchedEval=2022-09-19T19:49:17
Partition=special AllocNode:Sid=drago31010015:3020542
ReqNodeList=(null) ExcNodeList=(null)
NodeList=drago[31010005-31010014]
BatchHost=drago31010005
NumNodes=10 NumCPUs=10 NumTasks=10 CPUs/Task=1 ReqB:S:C:T=0:0:*:*
TRES=cpu=10,mem=40G,node=10,billing=10
Socks/Node=* NtasksPerN:B:S:C=0:0:*:* CoreSpec=*
MinCPUsNode=1 MinMemoryCPU=4G MinTmpDiskNode=0
Features=(null) DelayBoot=00:00:00
OverSubscribe=OK Contiguous=0 Licenses=(null) Network=(null)
Command=/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/slurm.sh
/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/mpihelloworld.tar.gz
/tmp/mpihelloworld
WorkDir=/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld
StdErr=/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/slurm-8513.out
StdIn=/dev/null
StdOut=/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/slurm-8513.out
Power=
NtasksPerTRES:0
[usuario@drago31010015 mpihelloworld]$ ls -lh
total 255M
-rw-r--r-- 1 usuario grupo 113 Sep 18 17:18 Dockerfile
-rw-r--r-- 1 usuario grupo 111 Sep 18 17:18 Makefile
-rw-r--r-- 1 usuario grupo 325 Sep 18 17:18 helloworld.c
-rw-r--r-- 1 usuario grupo 255M Sep 19 19:43 mpihelloworld.tar.gz
-rw-r--r-- 1 usuario grupo 2.8K Sep 19 19:49 slurm-8513.out
-rwxr-xr-x 1 usuario grupo 1.3K Sep 18 17:18 slurm.sh
[usuario@drago31010015 mpihelloworld]$ cat slurm-8513.out
tarball:
/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/mpihelloworld.tar.gz
image:
/tmp/mpihelloworld//lustre/home/grupof/usuario/drago-hpc-containers/mpihelloworld/
mpihelloworld
input:
tar
/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/mpihelloworld.tar.gz
output: dir
/tmp/mpihelloworld
analyzing ...
input:
tar
/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/mpihelloworld.tar.gz
output: dir
/tmp/mpihelloworld
input:
tar
/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/mpihelloworld.tar.gz
output: dir
/tmp/mpihelloworld
input:
tar
/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/mpihelloworld.tar.gz
input:
tar
/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/mpihelloworld.tar.gz
output: dir
/tmp/mpihelloworld
input:
tar
/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/mpihelloworld.tar.gz
output: dir
/tmp/mpihelloworld
input:
tar
/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/mpihelloworld.tar.gz
output: dir
/tmp/mpihelloworld
input:
tar
/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/mpihelloworld.tar.gz
analyzing ...
input:
tar
/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/mpihelloworld.tar.gz
analyzing ...
output: dir
/tmp/mpihelloworld
analyzing ...
analyzing ...
analyzing ...
analyzing ...
output: dir
/tmp/mpihelloworld
analyzing ...
output: dir
/tmp/mpihelloworld
analyzing ...
input:
tar
/lustre/home/grupo/usuario/drago-hpc-containers/mpihelloworld/mpihelloworld.tar.gz
output: dir
/tmp/mpihelloworld
analyzing ...
unpacking ...
unpacking ...
unpacking ...
unpacking ...
unpacking ...
unpacking ...
unpacking ...
unpacking ...
unpacking ...
unpacking ...
done
done
done
done
done
done
done
done
done
done
container: mpirun (Open MPI) 4.1.1
mpirun (Open MPI) 4.1.1
mpirun (Open MPI) 4.1.1
mpirun (Open MPI) 4.1.1
mpirun (Open MPI) 4.1.1
mpirun (Open MPI) 4.1.1
mpirun (Open MPI) 4.1.1
mpirun (Open MPI) 4.1.1
mpirun (Open MPI) 4.1.1
mpirun (Open MPI) 4.1.1
hola
hola
hola
hola
hola
hola
hola
hola
hola
hola
drago31010011
drago31010010
drago31010014
drago31010005
drago31010013
drago31010012
drago31010008
drago31010006
drago31010009
drago31010007
Hello world from process 7 of 10
Hello world from process 5 of 10
Hello world from process 4 of 10
Hello world from process 9 of 10
Hello world from process 2 of 10
Hello world from process 0 of 10
Hello world from process 6 of 10
Hello world from process 1 of 10
Hello world from process 8 of 10
Hello world from process 3 of 10