# Syntaxe : apptainer pull docker://<image>:<tag> # On ne spécifie pas de nom de fichier .sif, Apptainer va créer ubuntu_latest.sif apptainer pull docker://ubuntu:latest # Vérifier que le fichier a été créé ls -lh ubuntu_latest.sif
Vous avez peut-être déjà rencontré ce problème frustrant : un logiciel scientifique fonctionne parfaitement sur votre ordinateur, mais impossible de le faire tourner sur le cluster de calcul (ou chez un collègue) à cause de problèmes de versions, de bibliothèques manquantes ou de conflits… C’est là qu’interviennent les conteneurs ! Cette documentation cours vous explique ce que sont les conteneurs, pourquoi ils sont si utiles en calcul scientifique (HPC), et comment utiliser Apptainer (anciennement connu sous le nom de Singularity), un système de conteneurs spécialement conçu pour le HPC.
Le projet Singularity a évolué et une grande partie de la communauté open-source utilise maintenant Apptainer. Les commandes sont souvent identiques ou très similaires ( vs ). Il est possible que les deux commandes soient disponibles en fonction du cluster sur lequel vous travaillez. c’est le cas sur cluster IO. Dans cette doc, nous utiliserons principalement , mais gardez à l’esprit que peut être un alias ou la version installée.
|
C’est l’une des phrases les plus redoutées en informatique scientifique ! Vous développez un code, installez des dizaines de dépendances (bibliothèques logicielles), tout fonctionne… puis vous essayez de le lancer sur le cluster :
Erreur de version de Python ou d’une bibliothèque cruciale.
Bibliothèque manquante sur le système du cluster.
Conflit entre les versions nécessaires pour votre code et celles utilisées par un autre logiciel.
Impossible pour un collègue de reproduire vos résultats car son environnement est différent.
Ces problèmes de dépendances et de reproductibilité peuvent faire perdre un temps fou.
Environnements Complexes : Les logiciels scientifiques ont souvent besoin de nombreuses bibliothèques spécifiques (ex: une version précise de Python, de NumPy, d’une bibliothèque de calcul parallèle comme MPI, d’outils bioinformatiques…).
Conflits : Installer toutes ces dépendances pour tous les utilisateurs sur un système partagé comme un cluster est un casse-tête. La version qui convient à l’un peut casser le code de l’autre.
Reproductibilité : Pour que la science soit fiable, il faut pouvoir refaire exactement la même analyse plus tard, ou sur une autre machine, et obtenir le même résultat. C’est difficile si l’environnement logiciel change. Attention, les conteneurs sont très loin d’être la panacée en reproductibilité mais c’est déjà ça.
Quelles sont les principes d’un conteneur logiciel :
C’est une "boîte" standardisée qui contient votre application, toutes ses dépendances (bibliothèques, fichiers de configuration, voire une partie d’un système d’exploitation minimal), et les instructions pour la lancer.
Cette "boîte" (appelée une image) peut être facilement déplacée et exécutée sur différentes machines (votre portable, le cluster, le cloud…) sans avoir à réinstaller toutes les dépendances sur chaque machine.
L’application s’exécute de manière isolée à l’intérieur du conteneur, sans interférer avec le système principal (l’hôte) ou d’autres conteneurs.
⇒ Résultat : moins de "ça marche sur ma machine", bienvenue à la portabilité et un peu reproductibilité !
On compare souvent les conteneurs aux machines virtuelles (VMs), mais il y a une différence clé :
VM (ex: VirtualBox, OpenStack, VMware) : Virtualise le matériel complet. Chaque VM a son propre système d’exploitation complet (noyau Linux/Windows inclus). C’est lourd et lent à démarrer. Analogie : Une maison entière avec ses fondations.
Conteneur (ex: Apptainer, Docker) : Virtualise au niveau du système d’exploitation. Tous les conteneurs partagent le noyau Linux de la machine hôte. Ils embarquent seulement les applications, bibliothèques et fichiers nécessaires. C’est beaucoup plus léger et rapide. Analogie : Une pièce préfabriquée (le conteneur maritime) posée sur des fondations communes (le noyau de l’hôte).
Docker est très populaire, mais Apptainer/Singularity a été conçu spécifiquement pour les environnements scientifiques et HPC :
Sécurité : Par défaut, vous exécutez le conteneur avec vos propres permissions utilisateur, pas en tant que root (administrateur). C’est crucial sur un système partagé. Vous ne pouvez pas faire n’importe quoi dans le conteneur ou sur le système hôte.
Intégration : Il est conçu pour s’intégrer facilement avec les ressources HPC :
Accès aux systèmes de fichiers partagés (comme votre $HOME et scratch).
Utilisation des GPU Nvidia (avec l’option --nv).
Compatibilité avec les réseaux haute performance (InfiniBand…).
Fonctionne bien avec les gestionnaires de jobs comme SLURM.
Image = Fichier Unique : Une image Apptainer/Singularity est généralement un seul fichier (extension .sif - Singularity Image Format). C’est facile à copier, partager et archiver.
Image (Fichier .sif) : C’est le "plan de montage" ou la "recette". C’est un fichier qui contient l’application, ses dépendances, et l’environnement nécessaire. L’image est immuable (on ne la modifie pas directement une fois construite).
Conteneur : C’est l'instance en cours d’exécution de l’image. C’est le processus qui tourne, isolé du reste du système mais utilisant le noyau de l’hôte. Quand vous arrêtez le conteneur, il disparaît, mais l’image reste là pour être réutilisée.
Pour utiliser un conteneur, il faut d’abord avoir une image (.sif). La façon la plus simple pour commencer est de télécharger (pull) une image existante depuis un registre public.
Docker Hub (hub.docker.com) : Le plus grand registre d’images conteneurs au monde. Contient des images pour presque tous les logiciels imaginables (OS, langages, bases de données, outils scientifiques…). Apptainer peut télécharger et convertir les images Docker directement. C’est souvent le point de départ.
Sylabs Cloud (cloud.sylabs.io) : Un registre spécifiquement pour les images Singularity/Apptainer, avec une section "Library" pour les images publiques.
Autres Registres : Quay.io, GitHub Container Registry (GHCR), registres institutionnels…
Fichiers Locaux : Un collègue ou un administrateur peut vous fournir directement un fichier .sif.
| Attention, quand vous téléchargez une image depuis un dépôt, vous prenez le risque qu’elle comporte un logiciel malveillant ! |
apptainer pullLa commande principale pour télécharger est .apptainer pull
Syntaxe : apptainer pull [destination_fichier.sif] <source_URI>
[destination_fichier.sif] : Optionnel. Le nom que vous voulez donner au fichier .sif localement. Si omis, Apptainer choisit un nom basé sur l’URI.
<source_URI> : L’identifiant de l’image à télécharger. Formats courants :
(pour Docker Hub, docker://<nom_utilisateur_dockerhub>/<nom_image>:<tag> <tag> est souvent latest ou une version spécifique).
(pour Sylabs Cloud Library).library://<utilisateur_sylabs>/<collection>/<nom_image>:<tag>
# Syntaxe : apptainer pull docker://<image>:<tag> # On ne spécifie pas de nom de fichier .sif, Apptainer va créer ubuntu_latest.sif apptainer pull docker://ubuntu:latest # Vérifier que le fichier a été créé ls -lh ubuntu_latest.sif
il est déconseillé d’utiliser le tag latest; préférer mettre la version afin de préserver la répétabilité de l’opération.
|
# Télécharger l'image python version 3.9 "slim" (une version légère) # et la sauvegarder localement sous le nom python39.sif apptainer pull python39.sif docker://python:3.9-slim # Vérifier ls -lh python39.sif
# Exemple avec une image Alpine Linux de Sylabs # apptainer pull library://alpine:latest # Peut nécessiter enregistrement/login chez Sylabs selon l'image # Exemple avec lolcow (une image de démo classique) apptainer pull lolcow.sif library://sylabsed/examples/lolcow:latest ls -lh lolcow.sif
Où sont stockées les images ? Ce sont de simples fichiers .sif que vous téléchargez dans votre répertoire courant (ou à l’endroit où vous le spécifiez). Vous pouvez les déplacer, les copier comme n’importe quel fichier.
Maintenant que vous avez une image, comment l’utiliser ?
apptainer execC’est la commande la plus courante. Elle démarre le conteneur, exécute la commande que vous spécifiez à l’intérieur, puis arrête le conteneur.
Syntaxe : apptainer exec [options_exec] <image.sif> <commande_a_executer> [arguments_commande…]
# Utiliser l'image ubuntu_latest.sif téléchargée précédemment apptainer exec ubuntu_latest.sif cat /etc/os-release # La sortie montrera les informations de l'Ubuntu DANS le conteneur
# Utiliser l'image python39.sif
apptainer exec python39.sif python --version
# Devrait afficher "Python 3.9.x"
# Exécuter un petit script Python directement
apptainer exec python39.sif python -c 'import sys; print(f"Hello from Python {sys.version_info.major}.{sys.version_info.minor} inside Apptainer!")'
| La commande est exécutée dans l’environnement du conteneur. Elle utilise les bibliothèques et les outils présents dans l’image, pas ceux de votre système hôte (sauf pour les systèmes de fichiers montés, voir plus bas). |
apptainer shellUtile pour explorer l’intérieur d’un conteneur, installer des choses temporairement (à éviter, ne sera pas sauvé dans l’image !), ou lancer plusieurs commandes interactivement.
Syntaxe : apptainer shell [options_shell] <image.sif>
apptainer shell ubuntu_latest.sif # Votre prompt change, indiquant que vous êtes DANS le conteneur : # Apptainer> # Vous pouvez maintenant taper des commandes Linux qui seront exécutées à l'intérieur Apptainer> ls / Apptainer> pwd Apptainer> cat /etc/os-release Apptainer> echo "Je suis dans le conteneur" # Pour sortir du shell et arrêter le conteneur Apptainer> exit # Votre prompt normal revient
apptainer runUne image peut avoir un "script d’exécution" par défaut (défini lors de sa création dans le fichier de définition via %runscript). La commande exécute ce script.apptainer run
Syntaxe : apptainer run [options_run] <image.sif>
lolcow a un runscript amusant# Utiliser l'image lolcow.sif téléchargée plus tôt apptainer run lolcow.sif # Devrait afficher une vache ASCII avec une citation aléatoire :)
Si l’image n’a pas de runscript défini, se comporte souvent comme ou exécute /bin/sh.
|
Par défaut, pour des raisons de sécurité et d’intégration, Apptainer/Singularity rend certains répertoires de votre système hôte automatiquement visibles à l’intérieur du conteneur :
Votre répertoire personnel ($HOME).
Le répertoire courant (pwd).
Les répertoires système temporaires (/tmp, /var/tmp).
votre scratch personnel ($HOME/scratch_LoginName)
vos scratchs de groupes ($HOME/scratch_WorkspaceName)
| Ce comportement par défaut peut être modifié par les administrateurs du cluster pour des raisons de sécurité ou de performance. Toujours vous référer à la page qui regroupe l’ensemble des spécificités du cluster IO : io-hpc-spec |
Cela signifie que vous pouvez facilement lire et écrire dans votre $HOME depuis le conteneur.
# Créer un fichier dans votre répertoire courant sur l'hôte echo "Données de l'hôte" > mon_fichier_local.txt # Lire ce fichier depuis le conteneur Ubuntu echo "--- Lecture depuis conteneur ---" apptainer exec ubuntu_latest.sif cat ./mon_fichier_local.txt # Devrait afficher "Données de l'hôte" # Modifier ce fichier depuis le conteneur echo "--- Modification depuis conteneur ---" apptainer exec ubuntu_latest.sif \ bash -c 'echo "Modification depuis conteneur" >> ./mon_fichier_local.txt' # bash -c '...' permet d'exécuter une commande shell complexe # Vérifier la modification sur l'hôte echo "--- Vérification sur l'hôte ---" cat mon_fichier_local.txt # Devrait maintenant contenir les deux lignes # Nettoyage rm mon_fichier_local.txt
-B ou --bindSi vous avez besoin d’accéder à des données qui ne sont pas dans les répertoires montés automatiquement (par exemple, dans /data ou /scratch), vous devez utiliser l’option -B (ou --bind).
Syntaxe : -B /chemin/sur/hote:/chemin/dans/conteneur[:options]`
: Le répertoire sur votre machine (l’hôte)./chemin/sur/hote
: Où ce répertoire apparaîtra à l’intérieur du conteneur. Vous pouvez choisir le même chemin ou un chemin différent. Si le chemin dans le conteneur n’existe pas, Apptainer essaiera de le créer./chemin/dans/conteneur
(Optionnel) : Peut être [:options] ro (read-only, lecture seule) ou rw (read-write, lecture-écriture, souvent le défaut).
/data_in à l’intérieur du conteneur# Préparation sur l'hôte mkdir -p /tmp/mes_donnees_hote # Utiliser /tmp pour cet exemple echo "Fichier de données 1" > /tmp/mes_donnees_hote/data1.csv echo "Fichier de données 2" > /tmp/mes_donnees_hote/data2.csv echo "--- Contenu Hôte (/tmp/mes_donnees_hote) ---" ls -l /tmp/mes_donnees_hote/ # Exécuter une commande dans le conteneur en montant le dossier # On le monte en lecture seule (ro) sur /data_in dans le conteneur echo "" echo "--- Contenu Conteneur (/data_in) via bind mount ---" apptainer exec \ -B /tmp/mes_donnees_hote:/data_in:ro \ ubuntu_latest.sif \ ls -l /data_in/ # Devrait afficher data1.csv et data2.csv # Essayer d'écrire dans le dossier monté en lecture seule (doit échouer) echo "" echo "--- Tentative d'écriture (doit échouer) ---" apptainer exec \ -B /tmp/mes_donnees_hote:/data_in:ro \ ubuntu_latest.sif \ touch /data_in/nouveau_fichier.txt || echo "Erreur d'écriture comme attendu." # Monter en lecture-écriture echo "" echo "--- Montage en lecture-écriture ---" apptainer exec \ -B /tmp/mes_donnees_hote:/data_rw \ ubuntu_latest.sif \ bash -c 'touch /data_rw/fichier_cree_conteneur.txt ; echo "Fichier créé dans /data_rw"' # Vérifier sur l'hôte que le fichier a été créé echo "" echo "--- Vérification Hôte après montage rw ---" ls -l /tmp/mes_donnees_hote/ # Nettoyage rm -rf /tmp/mes_donnees_hote
Monter plusieurs répertoires : Séparez les chemins par des virgules : -B /chemin1:/c1,/chemin2:/c2 ou répétez l’option : -B /chemin1:/c1 -B /chemin2:/c2.
apptainer exec -B /scratch/$USER:/mnt/scratch image.sif …
C’est là que la puissance des conteneurs rencontre celle du gestionnaire de jobs ! Votre script SLURM va simplement lancer les commandes apptainer nécessaires.
Le Principe : Le script sbatch demande les ressources (CPU, mémoire, temps, GPU…) à SLURM comme d’habitude. Ensuite, au lieu de lancer directement votre application (qui pourrait avoir des problèmes de dépendances), il lance ou apptainer exec en utilisant l’image apptainer run .sif qui contient l’application et son environnement prêt à l’emploi.
1 Créez un script Python simple (mon_script_analyse.py) :
import argparse
import os
import time
print("--- Début script Python dans conteneur ---")
parser = argparse.ArgumentParser(description='Script Python simple pour Apptainer.')
parser.add_argument('--input_dir', required=True, help='Répertoire contenant les données d\'entrée')
parser.add_argument('--output_dir', required=True, help='Répertoire pour écrire les résultats')
parser.add_argument('--param', type=int, default=5, help='Un paramètre exemple')
args = parser.parse_args()
print(f"Répertoire d'entrée: {args.input_dir}")
print(f"Répertoire de sortie: {args.output_dir}")
print(f"Paramètre: {args.param}")
input_file = os.path.join(args.input_dir, 'data.txt')
output_file = os.path.join(args.output_dir, 'result.txt')
print(f"Lecture depuis: {input_file}")
try:
with open(input_file, 'r') as f:
content = f.read()
print(f"Contenu lu: {content.strip()}")
except FileNotFoundError:
print(f"ERREUR: Fichier d'entrée {input_file} non trouvé !")
exit(1)
print(f"Simulation d'un calcul pendant {args.param} secondes...")
time.sleep(args.param)
print(f"Ecriture du résultat dans: {output_file}")
with open(output_file, 'w') as f:
f.write(f"Résultat du traitement avec param={args.param}\n")
f.write(f"Basé sur le contenu: {content.strip()}\n")
print("--- Fin script Python dans conteneur ---")
2 Préparez les données d’entrée (dans le répertoire de soumission) :
mkdir -p input_data_job
echo "Données initiales pour le test" > input_data_job/data.txt
3 Créez le script SLURM (job_apptainer_python.sh) :
#!/bin/bash
#SBATCH --job-name=ApptainerPyJob
#SBATCH --output=apptainer_py_%j.out
#SBATCH --error=apptainer_py_%j.err
#SBATCH --time=00:05:00 # 5 minutes devraient suffire
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=1 # Python simple ici, pas besoin de plus
#SBATCH --mem=500M # Mémoire pour Apptainer + Python
# --- Configuration ---
# Chemin vers l'image Apptainer/Singularity contenant Python 3.9+
# Assurez-vous d'avoir téléchargé cette image avant !
IMAGE_SIF="/path/to/your/images/python39.sif" # !!! ADAPTEZ CE CHEMIN !!!
# Chemin vers le script Python à exécuter (dans le répertoire de soumission)
PYTHON_SCRIPT="$SLURM_SUBMIT_DIR/mon_script_analyse.py"
# Répertoires pour les données et résultats
# On utilise le répertoire de soumission pour cet exemple simple
DATA_DIR="$SLURM_SUBMIT_DIR/input_data_job"
# Créer un répertoire de sortie unique pour ce job
OUTPUT_DIR="$SLURM_SUBMIT_DIR/output_job_${SLURM_JOB_ID}"
mkdir -p "$OUTPUT_DIR"
# Vérifier si l'image existe
if [ ! -f "$IMAGE_SIF" ]; then
echo "ERREUR: L'image conteneur $IMAGE_SIF n'existe pas!"
exit 1
fi
# Vérifier si le script python existe
if [ ! -f "$PYTHON_SCRIPT" ]; then
echo "ERREUR: Le script python $PYTHON_SCRIPT n'existe pas!"
exit 1
fi
echo "--- Lancement du Job Conteneurisé ---"
echo "Date: $(date)"
echo "Noeud: $(hostname)"
echo "Image: $IMAGE_SIF"
echo "Script Python: $PYTHON_SCRIPT"
echo "Données depuis: $DATA_DIR"
echo "Résultats vers: $OUTPUT_DIR"
echo "------------------------------------"
# Exécution via apptainer exec
apptainer exec \
--containall \ # Option pour plus d'isolation (peut nécessiter des -B explicites)
-B "$DATA_DIR":/input_data:ro \ # Monter les données en lecture seule dans /input_data
-B "$OUTPUT_DIR":/output_data \ # Monter le dossier de sortie en lecture/écriture dans /output_data
-B "$PYTHON_SCRIPT":/app/script.py:ro \ # Monter le script python lui-même en lecture seule
"$IMAGE_SIF" \
python /app/script.py --input_dir /input_data --output_dir /output_data --param 15 # Lancer Python DANS le conteneur
# Vérifier le code de retour de apptainer exec
STATUS=$?
if [ $STATUS -ne 0 ]; then
echo "ERREUR: Apptainer exec a échoué avec le code $STATUS"
exit $STATUS
fi
echo "------------------------------------"
echo "Job conteneurisé terminé avec succès."
echo "Résultats dans : $OUTPUT_DIR"
echo "--- Fin du Job SLURM ---"
4 Soumettez le job : sbatch job_apptainer_python.sh
5 Vérifiez les résultats : Regardez le fichier et le contenu du dossier apptainer_py_<job_id>.out .output_job_<job_id>/
Pour utiliser des GPU Nvidia depuis un conteneur dans un job SLURM :
Demandez les GPU à SLURM : Ajoutez (et la bonne partition) à votre script.#SBATCH --gres=gpu:<nombre>
Utilisez l’option --nv d’Apptainer : Cette option magique dit à Apptainer de monter les bibliothèques Nvidia de l’hôte et les périphériques GPU à l’intérieur du conteneur.
```bash
# Extrait du script SLURM
#SBATCH --partition=gpu
#SBATCH --gres=gpu:1
# …
IMAGE_GPU="/path/to/gpu_enabled_image.sif" # Une image avec CUDA/PyTorch/TF...
apptainer exec \ --nv \ # <---- L'option clé pour les GPU Nvidia ! -B ... # Vos autres bind mounts "$IMAGE_GPU" \ python mon_script_gpu.py # Votre script qui utilise le GPU ```
Récapitulatif :
Les conteneurs résolvent les problèmes de dépendances et améliorent (un peu) la reproductibilité.
Apptainer/Singularity est adapté au HPC (sécurité, intégration).
Une Image (.sif) est le modèle, un Conteneur est l’exécution.
Commandes clés : , apptainer pull , apptainer exec , apptainer shell .apptainer run
L’option -B (--bind) est essentielle pour accéder aux données hors $HOME.
L’intégration avec SLURM est simple : lancez apptainer depuis votre script sbatch.
Utilisez --nv pour l’accès aux GPU Nvidia.
Avantages Clés Rappelés :
Fini (ou presque) le "Ça marche sur ma machine !".
Partage facile d’environnements logiciels complexes.
Meilleure reproductibilité des expériences scientifiques.
| Commande / Option | Description |
|---|---|
|
Télécharge une image depuis une source (Docker Hub, Sylabs…). |
|
Exécute une commande spécifique à l’intérieur du conteneur. |
|
Lance un shell interactif à l’intérieur du conteneur. |
|
Exécute le script |
|
Monte ( |
|
Active le support GPU Nvidia (monte les bibliothèques et périphériques nécessaires). |
|
Augmente l’isolation (monte moins de choses par défaut, nécessite plus de |
|
Définit le répertoire de travail initial dans le conteneur. |
|
Définit une variable d’environnement dans le conteneur. |