Contents

TP : Créer un conteneur avec Singularity

Créer un conteneur avec Singularity

Utilisation de singularity version 2.6, disponible ici : https://www.sylabs.io/docs/

Les fichiers utilisés dans le document sont disponibles dans le dépot git suivant : https://gitlab.in2p3.fr/souchal/tp-singularity

Pour récupérer les fichiers vous pouvez cloner le dépot git :

git clone https://gitlab.in2p3.fr/souchal/tp-singularity.git

Dans ce TP nous allons installer un conteneur Ubuntu sur une machine CentOS 7. Il est possible d’adapter ce TP sur n’importe quelle autre OS Linux récent, il faudra remplacer yum install par le gestionnaire de paquet de la distribution hôte.

Ce TP à été rédigé pour Linux exclusivement. Une partie du TP nécessite les droits administrateurs (sudo)

Mon premier conteneur

  • Installation de singularity
  • Création du conteneur
  • Travailler dans le conteneur

Installation de singularity

git clone https://github.com/singularityware/singularity.git
cd singularity
git fetch --all
git checkout 2.6.0
./autogen.sh
./configure --prefix=/usr/local
make
sudo make install

L’aide de singularity est accessible via la commande suivante :

singularity help
USAGE: singularity [global options...] <command> [command options...] ...

GLOBAL OPTIONS:
    -d|--debug    Print debugging information
    -h|--help     Display usage summary
    -s|--silent   Only print errors
    -q|--quiet    Suppress all normal output
    --version  Show application version
    -v|--verbose  Increase verbosity +1
    -x|--sh-debug Print shell wrapper debugging information

GENERAL COMMANDS:
    help       Show additional help for a command or container                  
    selftest   Run some self tests for singularity install                      

CONTAINER USAGE COMMANDS:
    exec       Execute a command within container                               
    run        Launch a runscript within container                              
    shell      Run a Bourne shell within container                              
    test       Launch a testscript within container                             

CONTAINER MANAGEMENT COMMANDS:
    apps       List available apps within a container                           
    bootstrap  *Deprecated* use build instead                                   
    build      Build a new Singularity container                                
    check      Perform container lint checks                                    
    inspect    Display container's metadata                                     
    mount      Mount a Singularity container image                              
    pull       Pull a Singularity/Docker container to $PWD                      

COMMAND GROUPS:
    image      Container image command group                                    
    instance   Persistent instance command group                                


CONTAINER USAGE OPTIONS:
    see singularity help <command>

For any additional help or support visit the Singularity
website: https://www.sylabs.io/

Création de l’image

Comme avec docker, la création d’un conteneur passe par l’écriture d’un fichier décrivant la configuration du conteneur. Singularity et Docker utilisent des formats de fichiers différents, toutefois il est possible de transformer un conteneur Docker en conteneur Singularity. Nous allons commencer par créer un conteneur avec la méthode Singularity. Nous verrons comment passer d’un conteneur Docker à un conteneur Singularity dans un second temps.

Il faut installer la base du systeme d’exploitation que nous voulons avoir à l’intérieur du conteneur. Pour cela on a deux solutions :

  • on écrit un fichier de “recette” qui va contenir une description de ce que nous voulons dans le conteneur.
  • on crée le conteneur à partir d’un fichier Dockerfile ou d’une image docker.

1) Fichier recette singularity

Commencez par créer un fichier texte nommé example.def et indiquons que nous voulons utiliser la dernière version d’Ubuntu :

Bootstrap: docker
From: ubuntu:latest

Dans Singularity 2.6, par défaut l’image crée est immuable pour permettre la reproductibilité. Il faut donc ajouter l’option –sandbox lorsqu’on teste un nouveau conteneur. Testons la création de notre conteneur décrit dans le fichier example.def :

IMPORTANT: La commande suivante nécessite d’avoir les droits administrateurs (sudo). Si vous ne les avez pas, passez au point 2.

sudo singularity build --sandbox example.img example.def

Pour ajouter plus de logiciels dans le conteneur on peut lancer des commandes directement à l’intérieur du conteneur :

sudo singularity shell --writable example.img
Singularity: Invoking an interactive shell within container...
Singularity example.img:~> apt-get update
Singularity example.img:~> apt-get install wget build-essential

Pour sortir du conteneur, tapez “exit”.

ou en utilisant la variable %post dans le fichier de recette :

%post
apt-get update && apt-get -y install wget build-essential

Le fichier example.def ressemble maintenant à ca :

Bootstrap: docker
From: ubuntu:latest

%post
apt-get update && apt-get -y install wget build-essential

Nous avons demandé d’installer les outils de compilations standard pour pouvoir en disposer dans le conteneur. Relancons le build :

sudo singularity build --sandbox example.img example.def

On peut écrire d’importe quelle commande bash dans le fichier de bootstrap. Par exemple, créons un répértoire dans le conteneur qui contiendra nos données :

Bootstrap: docker
From: ubuntu:latest

%post
apt-get update && apt-get -y install wget build-essential
mkdir /data

2) Depuis Docker

Si vous n’avez pas les droits d’administration sur votre machine, vous pouvez passer par une image docker existante :

singularity build --sandbox example.img docker://sysmso/docker-openmpi

Nous avons maintenant un conteneur prêt à l’emploi qui contient un compilateur et un répertoire de travail. Nous pouvons commencer à travailler dans le conteneur.

Travailler dans le conteneur

Nous avons maintenant un conteneur basique qui contient les outils de compilations. Nous pouvons alors entrer dans le conteneur et lancer un shell pour voir le résultat :

singularity shell example.img
Singularity: Invoking an interactive shell within container...
Singularity.example.img> $
Singularity.example.img> $ gcc --version
gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Singularity.example.img> $ exit

Pour sortir du conteneur, tapez exit. La version de gcc dans le conteneur est la 7.3. Par défaut vous lancez un shell dans votre conteneur en lecture-seule, donc vous ne pourrez pas installer de logiciels supplémentaire dans votre conteneur :

singularity shell example.img
Singularity.example.img> $ apt install vim
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
libgpm2 libpython3.5 vim-common vim-runtime
Suggested packages:
gpm ctags vim-doc vim-scripts vim-gnome-py2 | vim-gtk-py2 | vim-gtk3-py2 | vim-athena-py2 | vim-nox-py2
The following NEW packages will be installed:
libgpm2 libpython3.5 vim vim-common vim-runtime
0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded.
Need to get 7682 kB of archives.
After this operation, 35.1 MB of additional disk space will be used.
W: Not using locking for read only lock file /var/lib/dpkg/lock
W: chmod 0700 of directory /var/cache/apt/archives/partial failed - SetupAPTPartialDirectory (30: Read-only file system)

Pour lancer un shell dans un conteneur en écriture, il faut utiliser l’option –writable, a condition que le conteneur ait été crée avec l’option –sandbox :

singularity shell --writable example.img
Singularity.example.img> $ apt install vim

La commande ne devrait pas marcher, car vous n’êtes pas root.

Attention, avec singularity, vous ne pouvez pas changer d’utilisateur dans le conteneur : l’utilisateur hors du conteneur est le même dans le conteneur. Pour être root dans votre conteneur, il faut donc être root sur la machine hôte.

Vous pouvez executer les commandes suivantes dans le conteneur pour voir la différence avec votre système :

singularity shell -p example.img
Singularity.example.img> $ top

Si vous executez la commande top en dehors du container, vous avez beaucoup plus de processus. A l’intérieur du conteneur vous êtes isolés du reste de la machine avec l’option -p.

Par défaut lorsque vous lancez un shell dans votre conteneur, vous avez accès à tous les fichiers de votre machine locale :

touch toto.txt
singularity shell example.img
Singularity.example.img> $ ls
Singularity.example.img> $ toto.txt

Si vous voulez lancer un shell sans que le conteneur puisse avoir accès aux fichiers locaux, vous pouvez utiliser l’option -H pour utiliser un autre repertoire comme /home :

mkdir -p /tmp/temp_home_${USER}
singularity shell -H /tmp/temp_home_${USER}:/home/${USER} example.img
Singularity.example.img> $ ls

L’intêret du conteneur est de produire un résultat reproductible dans n’importe quel environnement : il est par conséquent important de s’isoler au maximum du système de fichier local (des fichiers cachés pourraient avoir une influence sur le code que vous exécutez).

Pour ajouter des fichiers à l’intérieur de votre container, vous pouvez rajouter une section %files :

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

Votre fichier singularity complet ressemble maintenant à ça :

Bootstrap: docker
From: ubuntu:latest

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

%post
apt-get update && apt-get -y install wget build-essential

Relancez le build, et votre fichier sera inséré dans le conteneur :

sudo singularity build --sandbox example.img example.def
singularity shell example.img
Singularity.example.img> $ ls /mpi-ping.c

Nous avons maintenant un conteneur qui contient nos outils de compilation et notre fichier à compiler.

Executer votre container sur un cluster de calcul

Créons un container avec une application et son environnement

Le conteneur que nous avons crée contient Ubuntu avec notre environnement de compilation. Allons plus loin et ajoutons une application MPI compilée dans le container.

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.

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
VARIABLE=MEATBALLVALUE
export VARIABLE

%labels
AUTHOR souchal@apc.in2p3.fr

%post

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

Pour information, voici l’équivalent de ce fichier avec Docker :

FROM ubuntu:latest

RUN apt-get update && \
    apt-get install -y wget software-properties-common build-essential sgml-base rsync xml-core openssh-client && \
    apt-get clean

RUN add-apt-repository universe && \
    apt-get update && \
    apt-get -y install cmake git gfortran openmpi-common openmpi-bin libopenmpi-dev && \
    apt-get clean

RUN mkdir /data

ENTRYPOINT ["echo","Le runscript est la commande par défaut du conteneur !"]

ADD ./mpi-ping.c /mpi-ping.c

On peut maintenant contruire le container :

sudo singularity build example2.img example2.def

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

singularity run example2.img

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

singularity exec example2.img mpicc tp-singularity/mpi-ping.c -o ./mpi-ping

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

mpirun -np 20 singularity exec example2.img ./mpi-ping

La commande mpirun doit être disponible sur la machine hôte, c’est cette commande qui va dispatcher le conteneur sur les CPU physiques de la machine. De plus il faut que la version locale de MPI soit compatible avec celle utilisée pour compiler le fichier dans le conteneur.

Lancer un programme conteneurisé en batch

Il faut envoyer votre conteneur sur le cluster, par exemple via scp.

Par exemple avec Torque Scheduler, avec 8 coeurs sur le même noeud :

#!/bin/bash
#PBS -N SINGULARITY
#PBS -l nodes=1:ppn=8,mem=2gb,walltime=24:00:00
export SCRATCH="/scratch/$USER.$PBS_JOBID"
mpirun /usr/local/bin/singularity exec /home/${USER}/example2.img /usr/bin/mpi-ping >> output.txt

Le résultat :

mpi-ping: ping-pong
nprocs=8, reps=10000, min bytes=0, max bytes=0 inc bytes=0
4 pings 5
6 pings 7
0 pings 1
2 pings 3
4 pinged   5:        0 bytes      0.36 uSec     0.00 MB/s
2 pinged   3:        0 bytes      0.36 uSec     0.00 MB/s
0 pinged   1:        0 bytes      0.37 uSec     0.00 MB/s
6 pinged   7:        0 bytes      0.36 uSec     0.00 MB/s

avec Torque Scheduler, avec 16 coeurs sur 2 noeuds différents :

#!/bin/bash
#PBS -N SINGULARITY
#PBS -l nodes=2:ppn=4,mem=2gb,walltime=24:00:00
export SCRATCH="/scratch/$USER.$PBS_JOBID"
mpirun /usr/local/bin/singularity exec /home/${USER}/example2.img /usr/bin/mpi-ping >> output.txt

Le résultat :

mpi-ping: ping-pong
nprocs=16, reps=10000, min bytes=0, max bytes=0 inc bytes=0
4 pings 5
6 pings 7
0 pings 1
2 pings 3
10 pings 11
12 pings 13
14 pings 15
8 pings 9
0 pinged   1:        0 bytes      0.56 uSec     0.00 MB/s
6 pinged   7:        0 bytes      0.54 uSec     0.00 MB/s
4 pinged   5:        0 bytes      0.52 uSec     0.00 MB/s
2 pinged   3:        0 bytes      0.52 uSec     0.00 MB/s
8 pinged   9:        0 bytes      0.52 uSec     0.00 MB/s
14 pinged  15:        0 bytes      0.52 uSec     0.00 MB/s
10 pinged  11:        0 bytes      0.53 uSec     0.00 MB/s
12 pinged  13:        0 bytes      0.52 uSec     0.00 MB/s

Autre exemple : un script en python

Récupérer l’image sur le singularity Hub

singularity pull shub://sysmso/singularity-multinest

Exécuter un fichier de démo pour tester python :

singularity exec sysmso-singularity-multinest-master-latest.simg python pymultinest_demo_minimal.py