Variante niveau 1 : utilisation de Singularity v4
Auteurs
Assemblé et rédigé par Martin Souchal et Pavel Zakarov

This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
Verifier l'Installation
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 :
Pour lancer une image du catalogue, on peut utiliser la commande run
Le prompt Singularity> indique que vous vous situez à l'intérieur du conteneur :
Par défaut les conteneurs sont en lecture seule.
Il est aussi possible d'utiliser des images docker avec singularity :
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 :
Jouer avec les images
Télécharger une image
Depuis le catalogue Singularity :
Depuis le catalogue Docker :
Depuis un repository gitlab :
Voir les informations d'une image
Avec les commandes suivantes on vérifie l'authenticité de l'image et ses métadonnées.
Différents modes d'execution
Lancer une commande dans un conteneur :
Lancer un shell interactif dans un conteneur :
Lancer le programme runscript d'un conteneur :
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é.
Pour pouvoir modifier un conteneur, il faut rajouter l'option --writable :
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 :
Une fois la nouvelle image buildée, vous avez un dossier centos qui contient tous les fichiers de l'image :
Pour lancer un conteneur a partir de cette image, il faut utiliser la même syntaxe que precedemment :
Et pour pouvoir modifier en écriture ce conteneur, il faut utiliser l'option --writable :
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
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 :
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:
Building from a definition file:
To build the container, use the --remote flag :
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é);
- local image (images sur votre machine locale);
- yum (pour les OS compatibles yum comme CentOS).
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 :
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 :
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 :
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
%helpou%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
%environmentet%appenvde 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 :
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 :
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.
A la fin du jobs, il doit être écrit "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 :
Avec cet ajout, Gitlab va maintenant récuperer le fichier app.sif et le stocker.
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 :
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 :
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 :
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 :
Si votre projet est public, vous n'avez pas besoin de spécifier de login et mot de passe :
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 :
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 :
Le runscript peut être executé avec la commande run :
On peut ensuite compiler le fichier mpi-ping.c avec le compilateur inclus dans le conteneur :
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 :
Attention ! Vous devez avoir une version de OpenMPI compatible avec celle utilisée dans le conteneur.
Pour vérifier la version de OpenMpi :
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 :
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) :
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 :