Skip to content

Variante niveau 1 : utilisation de Singularity v4

Auteurs

Assemblé et rédigé par Martin Souchal et Pavel Zakarov

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

Verifier l'Installation

$ singularity --version
$ singularity help

Manipulation de conteneurs singularity

Principes de base

L’utilisateur qui lance le conteneur est le même utilisateur dans le conteneur (ça peut être déroutant au départ quand on utilise d’autres solutions de conteneurisation).

Par défaut, quelques fichiers/dossiers sont bind montés de l’hôte dans le conteneur. Au minimum : - le $HOME - le /tmp et /var/tmp - /etc/resolv.conf et /etc/hosts

Ce comportement peut être différent et modifié pour en rajouter/enlever en éditant la configuration de singularity (singularity.conf). Singularity utilise à minima un chroot, le mount et le PID namespace et des actions avec un bit suid : https://sylabs.io/guides/3.7/user-guide/security.html Le but d’un conteneur singularity est de packager une ou plusieurs applications dans un conteneur et d’avoir le minimum d’impacts sur les performances et l’environnement système (pas de démon).

Prévu au départ pour le HPC, les développeurs ont volontairement limité le nombre de fonctionnalités d’un conteneur pour laisser juste l’essentiel. Il y a également quelques facilités d’utilisation dans le cadre du HPC : - la couche réseau qui est dans le conteneur est la même que celle de l’hôte, et l’utilisation - d’applications de type MPI est également prévue, - l’utilisation des cartes GPU nvidia est possible, avec une option dédiée, - l’image produite est un fichier exécutable, qui peut donc être appelée directement, - le HOME de l’utilisateur est monté par défaut dans le conteneur, - le build de l’image se fait en root, le reste en tant que simple utilisateur.

Executer une image singularity

Il est possible de faire une recherche d'image existante dans le catalogue global singularity :

singularity search centos

Pour lancer une image du catalogue, on peut utiliser la commande run

1
2
3
4
singularity run library://library/default/centos
INFO:    Downloading library image
80.2MiB / 80.2MiB   [==============================================================================] 100 % 6.8 MiB/s 0s
Singularity>

Le prompt Singularity> indique que vous vous situez à l'intérieur du conteneur :

1
2
3
4
Singularity> cat /etc/centos-release
CentOS Linux release 8.4.2105
Singularity> exit
exit

Par défaut les conteneurs sont en lecture seule.

Il est aussi possible d'utiliser des images docker avec singularity :

singularity run docker://centos:8
INFO:    Converting OCI blobs to SIF format
WARNING: 'nodev' mount option set on /tmp, it could be a source of failure during build process
INFO:    Starting build...
Getting image source signatures
Copying blob 7a0437f04f83 done
Copying config 8dd57e171a done
Writing manifest to image destination
Storing signatures
2021/01/06 15:35:28  info unpack layer: sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621
2021/01/06 15:35:28  warn xattr{run/utmp} ignoring ENOTSUP on setxattr "user.rootlesscontainers"
2021/01/06 15:35:28  warn xattr{/tmp/build-temp-046504359/rootfs/run/utmp} destination filesystem does not support xattrs, further warnings will be suppressed
2021/01/06 15:35:28  warn rootless{usr/bin/newgidmap} ignoring (usually) harmless EPERM on setxattr "security.capability"
2021/01/06 15:35:28  warn rootless{usr/bin/newuidmap} ignoring (usually) harmless EPERM on setxattr "security.capability"
2021/01/06 15:35:28  warn rootless{usr/bin/ping} ignoring (usually) harmless EPERM on setxattr "security.capability"
2021/01/06 15:35:30  warn rootless{usr/sbin/arping} ignoring (usually) harmless EPERM on setxattr "security.capability"
2021/01/06 15:35:30  warn rootless{usr/sbin/clockdiff} ignoring (usually) harmless EPERM on setxattr "security.capability"
INFO:    Creating SIF file...

Cette commande à téléchargée l'image Centos 8 depuis le docker hub et l'a convertie en image Singularity.

Vous pouvez vérifier :

Singularity> cat /etc/centos-release
CentOS Linux release 8.4.2105

Jouer avec les images

Télécharger une image

Depuis le catalogue Singularity :

$ singularity pull library://library/default/centos:8`

Depuis le catalogue Docker :

$ singularity pull docker://centos:8`

Depuis un repository gitlab :

$ singularity pull image.sif oras://gitlab-registry.in2p3.fr/user/project:tag

Voir les informations d'une image

Avec les commandes suivantes on vérifie l'authenticité de l'image et ses métadonnées.

1
2
3
$ singularity pull library://library/default/centos:8
$ singularity verify centos_8.sif
$ singularity inspect centos_8.sif

Différents modes d'execution

Lancer une commande dans un conteneur :

$ singularity exec docker://centos:8 ls

Lancer un shell interactif dans un conteneur :

$ singularity shell centos_8.sif

Lancer le programme runscript d'un conteneur :

$ singularity run library://godlovedc/funny/lolcow

Les runscript sont des scripts définis par l'utilisateur qui définissent les actions qu'un conteneur doit effectuer lorsque quelqu'un l'exécute. Le script d'exécution peut être déclenché avec la commande run, ou simplement en appelant le conteneur comme s'il s'agissait d'un exécutable.

Formats d'images

Par défaut, les conteneurs sont lancés en lecture seule : c'est à dire qu'il n'est pas possible de modifier le contenu du conteneur puis d'enregistrer les modifications. Essayez par exemple d'installer un package dans le conteneur centos précedemment téléchargé.

Singularity> yum install vim
Config error: [Errno 30] Read-only file system: '/var/log/dnf.log': '/var/log/dnf.log'

Pour pouvoir modifier un conteneur, il faut rajouter l'option --writable :

singularity run --writable centos_8.sif
FATAL:   no SIF writable overlay partition foun

Cette erreur signifie qu'il est impossible de modifier un conteneur au format d'image SIF : en effet ce format d'image est sécurisé, il garantit que le conteneur que vous avez téléchargé n'a pas été altéré, qu'il est tel que son concepteur l'a voulu. C'est un peu l'équivalent du format PDF pour les documents classiques.

Pour modifier un conteneur singularity de manière interactive, il faut utiliser un autre format de conteneur : le format sandbox, c'est à dire que votre conteneur va être accessible sous la forme d'un dossier.

Pour créer une image sandbox a partir d'un conteneur existant, il faut utiliser la commande build :

singularity build --sandbox centos/ library://library/default/centos:8

Une fois la nouvelle image buildée, vous avez un dossier centos qui contient tous les fichiers de l'image :

$ ls centos
bin  boot  dev  environment  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  singularity  srv  sys  tmp  usr  var

Pour lancer un conteneur a partir de cette image, il faut utiliser la même syntaxe que precedemment :

singularity run centos
Singularity>

Et pour pouvoir modifier en écriture ce conteneur, il faut utiliser l'option --writable :

singularity run --writable centos
Singularity>

Vous pouvez maintenant ajouter des paquets dans le conteneur. Pour rappel, c'est le même utilisateur dans le conteneur que hors du conteneur : pour pouvoir installer un paquets dans le conteneur, vous devez avoir les droits d'administration en dehors du conteneur.

Recettes

Un fichier de définition Singularity (ou "def file" en abrégé) est comme un ensemble de plans expliquant comment construire un conteneur personnalisé. Il comprend des détails sur le système d'exploitation de base à construire ou le conteneur de base à partir duquel démarrer, le logiciel à installer, les variables d'environnement à définir au moment de l'exécution, les fichiers à ajouter à partir du système hôte et les métadonnées du conteneur. C'est l'équivalent du fichier Dockerfile pour Docker.

Vue d'ensemble

Un fichier de définition Singularity est divisé en deux parties :

  • L'en-tête : L'en-tête décrit le système d'exploitation de base à construire dans le conteneur. Ici, vous configurerez les fonctionnalités du système d'exploitation de base nécessaires au sein du conteneur. Vous pouvez spécifier la distribution Linux, la version spécifique et les paquets qui doivent faire partie de l'installation de base (empruntés au système hôte).

  • Sections : Le reste de la définition est composé de sections (parfois appelées scriptlets ou blocs de données). Chaque section est définie par un caractère % suivi du nom de la section particulière. Toutes les sections sont facultatives, et un fichier def peut contenir plus d'une instance d'une section donnée. Les sections qui sont exécutées au moment de la compilation sont exécutées avec l'interpréteur /bin/sh et peuvent accepter les options de /bin/sh. De même, les sections qui produisent des scripts à exécuter au moment de l'exécution peuvent accepter des options destinées à /bin/sh

Pour des exemples plus approfondis et pratiques de fichiers def, voir le dépôt d'exemples Sylabs

Pour une comparaison entre Dockerfile et le fichier de définition Singularity, veuillez consulter : cette section de la documentation officielle.

Définition

BootStrap: library
From: ubuntu:16.04
%post
    apt-get -y update
    apt-get -y install fortune cowsay lolcat
%environment
    export LC_ALL=C
    export PATH=/usr/games:$PATH
%runscript
    fortune | cowsay | lolcat
%labels
    Author GodloveD

Pour construire cette image, créez un fichier texte contenant le contenu ci dessus. Vous pouvez l'appler comme vous voulez (ici ce sera Singularity.def). Ensuite, utilisez la commande build pour construire l'image image.sif :

singularity build image.sif Singularity.def
Attention, pour construire une image Singularity, vous devez être root (administrateur). Si vous êtes dans un environnement sans droits administrateurs, vous devez utiliser un builder sur le cloud (option --remote, nécessite d'avoir un compte sylabs cloud) ou fakeroot avec un certain nombre de pré-requis et de limitations (option --fakeroot).

Il est aussi possible de passer par l'intégration continue pour construire un conteneur si l'on ne possède pas les droits d'administration. Nous verrons cette méthode un peu plus loin.

Remote build

The remote builder service can build your container in the cloud removing the requirement for root access. The Sylabs Cloud provides a Remote Builder, allowing you to build containers on a secure remote service. This is convenient so that you can build containers on systems where you do not have root privileges.

Make an Account

Making an account is easy, and straightforward:

  • Go to: https://cloud.sylabs.io/library.
  • Click “Sign in to Sylabs” (top right corner).
  • Select your method to sign in, with Google, GitHub, GitLab, or Microsoft.
  • Type your passwords, and that’s it!

Creating a Access token

Access tokens for pushing a container, and remote builder.

To generate a access token, do the following steps:

  • Go to: https://cloud.sylabs.io/
  • Click “Sign In” and follow the sign in steps.
  • Click on your login id (same and updated button as the Sign in one).
  • Select “Access Tokens” from the drop down menu.
  • Enter a name for your new access token, such as “test token”
  • Click the “Create a New Access Token” button.
  • Click “Copy token to Clipboard” from the “New API Token” page.
  • Run singularity remote login and paste the access token at the prompt.

Now that you have your token, you are ready to push your container!

Build

Here’s a typical remote build command:

singularity build --remote file-out.sif docker://ubuntu:18.04

Building from a definition file:

To build the container, use the --remote flag :

singularity build --remote image.sif Singularity.def

Bootstrap

Le bootstrap est la manière dont sont construites les images, il existe les bootstrap suivants dans Singularity :

  • library (images du catalogue global Sylabs cloud);
  • docker (images du Docker Hub);
  • shub (images sur un Singularity Hub privé);
From: shub://<registry>/<username>/<container-name>:<tag>@digest
  • local image (images sur votre machine locale);
From: /path/to/container/file/or/directory
  • yum (pour les OS compatibles yum comme CentOS).
1
2
3
4
Bootstrap: yum
OSVersion: 7
MirrorURL: http://mirror.centos.org/centos-%{OSVERSION}/%{OSVERSION}/os/$basearch/
Include: yum

Apps

Les sections %app* peuvent exister à côté de n'importe laquelle des sections primaires (c'est-à-dire %post, %runscript, %environment, etc.). ). Comme pour les autres sections, l'ordre des sections %app* n'est pas important.

Le runscript suivant montre comment construire 2 applications différentes dans le même conteneur en utilisant les modules SCI-F :

Bootstrap: docker
From: ubuntu

%environment
    GLOBAL=variables
    AVAILABLE="to all apps"

##############################
# foo
##############################

%apprun foo
    exec echo "RUNNING FOO"

%applabels foo
   BESTAPP FOO

%appinstall foo
   touch foo.exec

%appenv foo
    SOFTWARE=foo
    export SOFTWARE

%apphelp foo
    This is the help for foo.

%appfiles foo
   foo.txt

##############################
# bar
##############################

%apphelp bar
    This is the help for bar.

%applabels bar
   BESTAPP BAR

%appinstall bar
    touch bar.exec

%appenv bar
    SOFTWARE=bar
    export SOFTWARE

Une section %appinstall est l'équivalent de %post mais pour une application particulière. De même, %appenv équivaut à la version de %environment de l'application et ainsi de suite.

Après avoir installé des applications dans des modules utilisant les sections %app*, l'option --app devient disponible et permet les fonctions suivantes :

Exécuter une application spécifique dans le conteneur :

% singularity run --app foo my_container.sif
RUNNING FOO

La même variable d'environnement, "$SOFTWARE", est définie pour les deux applications dans le fichier def ci-dessus. Vous pouvez exécuter la commande suivante pour rechercher la liste des variables d'environnement actives et grep pour déterminer si la variable change en fonction de l'application que nous spécifions :

1
2
3
4
5
$ singularity exec --app foo my_container.sif env | grep SOFTWARE
SOFTWARE=foo

$ singularity exec --app bar my_container.sif env | grep SOFTWARE
SOFTWARE=bar

Bonnes pratiques

Lorsque vous élaborez votre recette, il est préférable de tenir compte des éléments suivants :

  • Installez toujours les paquets, programmes, données et fichiers dans les emplacements du système d'exploitation (par exemple, pas dans /home, /tmp, ou tout autre répertoire qui pourrait être lié).

  • Documentez votre conteneur. Si votre script d'exécution ne fournit pas d'aide, écrivez une section %help ou %apphelp. Un bon conteneur indique à l'utilisateur comment interagir avec lui.

  • Si vous avez besoin de définir des variables d'environnement spéciales, ajoutez-les aux sections %environment et %appenv de la recette de construction.

  • Les fichiers doivent toujours être détenus par un compte système (UID inférieur à 500).

  • Assurez-vous que les fichiers sensibles comme /etc/passwd, /etc/group et /etc/shadow ne contiennent pas de secrets.

  • Construisez des conteneurs de production à partir d'un fichier de définition au lieu d'une sandbox qui a été modifiée manuellement. Cela garantit une plus grande possibilité de reproductibilité et atténue l'effet de "boîte noire".

Intégration continue

Nous allons détailler ici la construction et la mise à disposition de conteneurs Singularity avec Gitlab CI. Il est également possible d'utiliser d'autres services comme Github ou Jenkins...

Pré-requis

Le projet Gitlab doit avoir la structure suivante :

1
2
3
4
    .
    ├── .gitlab-ci.yml
    ├── README.md
    └── Singularity.def

Le fichier .gitlab-ci.yml contient les parametres d'intégration continue pour Gitlab.

Le fichier Singularity.def est un fichier de recette Singularity comme vu précedemment.

Construction automatisée du conteneur

Dans un premier temps, nous allons mettre en place une procédure qui va construire le conteneur correspondant au fichier Singularity.def .

Cette configuration se fait dans le fichier .gitlab-ci.yml exclusivement : a chaque commit, gitlab va effectuer la tâche de lancer un build de l'image singularity dans un runner partagé docker. Nous allons faire du Singularity in Docker.

Définition de l'étape de build

Voici le contenu du fichier .gitlab-ci.yml qui va permettre d'effectuer cette tâche :

1
2
3
4
5
6
7
singularity:
  stage: build
  image:
    name: quay.io/singularity/singularity:v3.7.0
    entrypoint: [""]
  script:
    - singularity build app.sif Singularity.def
On utilise ici l'image docker officielle de Singularity (hébergée sur quay.io). Cette image contient les binaires de Singularity, ce qui nous permets de lancer la commande de build comme précedemment.

Lors du premier commit avec ce fichier, gitlab va automatiquement lancer le build. Pour suivre l'avancement du build, il faut cliquer dans l'interface de gauche de Gitlab sur CI/CD, puis Jobs.

Running with gitlab-runner 13.7.0 (943fc252)
on ccosvms0006@gitlab.in2p3.fr FxoAW2ya
Preparing the "docker" executor
Using Docker executor with image quay.io/singularity/singularity:v3.4.0 ...
Pulling docker image quay.io/singularity/singularity:v3.4.0 ...
Using docker image sha256:9363aec8d3d2014f59c6cd074624c8f08026ab02cafb3fe753555ec0127bc5a9 for quay.io/singularity/singularity:v3.4.0 with digest quay.io/singularity/singularity@sha256:1bcafecbe53a8a84fedbd226d7428f6fc3dd1bb24930c31187ece209bb3decfd ...
Preparing environment
00:15
Running on runner-fxoaw2ya-project-11799-concurrent-0 via ccosvms0006...
Getting source from Git repository
00:03
Fetching changes with git depth set to 50...
Initialized empty Git repository in /builds/souchal/tp-singularty-v3/.git/
Created fresh repository.
Checking out 2e73ea11 as master...
Skipping Git submodules setup
Executing "step_script" stage of the job script
$ singularity build Singularity.sif Singularity.def
INFO:    Starting build...
[...]

A la fin du jobs, il doit être écrit "Job succeeded" :

1
2
3
4
5
6
INFO:    Adding runscript
INFO:    Creating SIF file...
INFO:    Build complete: Singularity.sif
Cleaning up file based variables
00:02
Job succeeded

Votre conteneur à été crée, et cette procédure sera relancée a chaque fois que vous allez faire un commit sur ce projet.

Récupération de l'image générée

Maintenant que nous avons une image qui est produite, il faut être en mesure de la récuperer. Pour cela nous allons utiliser les artefacts Gitlab, qui permettent de sauvegarder le résultat d'un job de CI.

Modifiez le fichier .gitlab-ci.yml de façon a rajouter le paramètre artifacts :

singularity:
  stage: build
  image:
    name: quay.io/singularity/singularity:v3.7.0
    entrypoint: [""]
  script:
    - singularity build app.sif Singularity.def
  artifacts:
    paths:
    - app.sif

Avec cet ajout, Gitlab va maintenant récuperer le fichier app.sif et le stocker.

1
2
3
4
5
Uploading artifacts for successful job
00:07
Uploading artifacts...
Singularity.sif: found 1 matching files and directories 
Uploading artifacts as "archive" to coordinator... ok  id=239025 responseStatus=201 Created token=C3U72FuV

Il est également possible de le télécharger via http, pour cela dans l'interface Gitlab, cliquer sur Télécharger.

Cette méthode n'est pas optimale pour travailler avec des conteneurs : elle oblige à utiliser une interface graphique ou des liens http. Faisons en sorte que cette image puisse être récupérée en ligne de commande avec le client singularity : pour cela nous allons utiliser les registres Gitlab.

Registres Gitlab

Chaque projet gitlab contient un registre de conteneur. Ce registre est accessible via l'interface graphique Gitlab : dans Packages & Registries puis Container Registry. Par défaut il est vide : nous allons ajouter notre image à l'intérieur.

Pour pousser une image dans le registre Gitlab, on utilise le protocole ORAS, qui permet d'utiliser un registre Docker avec Singularity. La commande pour envoyer une image est la suivante :

$ singularity push --docker-username $user --docker-password $passwd app.sif oras://gitlab-registry.in2p3.fr/user/project:latest
INFO:    Upload complete

Avec $user et $passwd qui sont vos logins et mot de passe Gitlab et project l'url de votre projet Gitlab.

Pour télécharger une image depuis un registre Gitlab, il faut utiliser la commande pull :

$ singularity pull --docker-username $user --docker-password $passwd oras://gitlab-registry.in2p3.fr/user/project:latest
INFO:    Downloading oras image

Si votre projet est public, vous n'avez pas besoin de spécifier de login et de mot de passe.

Cette méthode est manuelle, et n'est pas recommandée car elle affiche vos logins et mot de passe en clair. Nous allons automatiser cette étape avec Gitlab, pour cela il faut à nouveau éditer le fichier .gitlab-ci.yml pour rajouter la ligne suivante qui va ajouter une nouvelle étape après le build :

- singularity push --docker-username "${CI_REGISTRY_USER}" --docker-password "${CI_REGISTRY_PASSWORD}" Singularity.sif oras://"$CI_REGISTRY_IMAGE"/"$CI_PROJECT_NAME":"$CI_COMMIT_TAG"

On utilise ici les variable Gitlab prédéfinies pour le login et le mot de passe, ce qui est plus sécurisé que de les écrire.

Si tout se passe sans erreur, vous allez avoir une nouvelle image dans le registre Gitlab attaché au projet.

Vous pouvez aussi télécharger cette image via le client Singularity, en ligne de commande :

$ singularity pull --docker-username $user --docker-password $passwd oras://gitlab-registry.in2p3.fr/user/project:latest
INFO:    Downloading oras image

Si votre projet est public, vous n'avez pas besoin de spécifier de login et mot de passe :

$ singularity pull oras://gitlab-registry.in2p3.fr/user/project:latest
INFO:    Downloading oras image

Vous pouvez alors copier cette commande et l'envoyer à n'importe qui pour qu'il utilise votre conteneur.

Utilisation HPC

L'interêt principal de Singularity est sa parfaite adaptation pour les environnements HPC. Singularity permet nativement de faire du MPI ou d'utiliser des GPUs.

MPI

Nous allons créer un conteneur Ubuntu avec un environnement de compilation et d'execution OpenMPI, puis nous compilerons un petit programme MPI qui va faire un ping entre les processeurs.

Pour cela nous allons avoir besoin d’installer openmpi dans le container. Dans ubuntu, pour avoir le paquet openmpi il faut utiliser le repository universe.

Le fichier en C à compiler est mpi-ping.c

Voilà ce que donne le fichier singularity final avec toutes les dépendances requises :

Bootstrap: docker
From: ubuntu:latest

%runscript
mpicc /mpi-ping.c


%files
tp-singularity/mpi-ping.c /mpi-ping.c

%environment
DEBIAN_FRONTEND=noninteractive
export DEBIAN_FRONTEND
%labels
AUTHOR souchal@apc.in2p3.fr

%post
export DEBIAN_FRONTEND=noninteractive
apt-get update && apt-get -y install software-properties-common wget build-essential sgml-base rsync xml-core openssh-client
add-apt-repository universe
apt-get update && apt-get -y install cmake git gfortran openmpi-common openmpi-bin libopenmpi-dev
apt-get clean

mkdir /data

Attention ! Cet exemple ne respecte pas les bonnes pratiques ! Pouvez vous identifier ce qui n'est pas correct ?

Si on nomme ce document Singularity.def, la commande suivante permet de construire le conteneur :

sudo singularity build mpi.sif Singularity.def

Le runscript peut être executé avec la commande run :

singularity run mpi.sif

On peut ensuite compiler le fichier mpi-ping.c avec le compilateur inclus dans le conteneur :

singularity exec mpi.sif mpicc tp-singularity/mpi-ping.c -o ./mpi-ping

Une fois la compilation terminée, on lance le test avec le binaire mpirun de la machine hôte et le fichier que nous venons de compiler :

mpirun -np 2 singularity exec mpi.sif ./mpi-ping

Attention ! Vous devez avoir une version de OpenMPI compatible avec celle utilisée dans le conteneur.

Pour vérifier la version de OpenMpi :

mpirun --version

Python

Ici nous allons tester l'utilisation d'un conteneur qui contient une application de calcul en Python. Les fichiers source du conteneur sont disponibles ici : https://gitlab.in2p3.fr/souchal/singularity_multinest

Test du conteneur en local

Téléchargez et executez le conteneur disponible dans le dépot Gitlab :

singularity pull oras://gitlab-registry.in2p3.fr/souchal/singularity_multinest/singularity_multinest:latest

Utilisation de MPI

Essayez de lancer ce conteneur avec deux threads MPI (attention, il faut que vous ayez une version compatible de MPI sur la machine hôte et dans le conteneur) :

mpiexec -np 2 singularity run singularity_multinest_latest.sif

Si cela fonctionne, vous pouvez tester sur une ferme de calcul, en utilisant un scheduler comme SGE ou Slurm pour faire du MPI entre noeuds.

Intégration avec le Job Scheduler

Ecrire un script de soumission en utilisant un scheduler.

Voici un exemple avec Slurm :

1
2
3
4
5
6
7
8
9
#!/bin/bash

#SBATCH --job-name=singularity-run
#SBATCH --nodes=1
#SBATCH --time=0-00:05:00
#SBATCH --partition quiet
#SBATCH --mem-per-cpu=1500MB
#SBATCH --output=singularity-run.out
mpirun /usr/local/bin/singularity exec /home/${USER}/mpi.sif /usr/bin/mpi-ping >> output.txt