Aujourd'hui petit mash-up de deux articles précédemment publiés dans nos colonnes. Comme je l'avais promis, je vais vous présenter ma méthode pour faire du pipelining avec le gestionnaire de ressources de notre cluster. Si vous n'avez pas compris la phrase précédente, je vous invite à aller (re-)lire l'article sur les pipelines et celui sur les clusters, tout devrait être plus clair. Je vais présenter une idée simple : comment identifier chacune de vos commandes sur un cluster et utiliser cette identification pour les lier dans un pipeline. Les exemples de l'article sont basés sur le système de management LSF, installé sur les clusters Vital-it du SIB. Bien qu'il existe des différences avec d'autres systèmes, vous devriez retrouver ces fonctionnalités sur chacun d'entre eux.
Nommez vos commandes…
Comme vous le savez maintenant, avec un pipeline, on souhaite enchaîner différentes tâches sans avoir à intervenir. Il faut donc pouvoir dire quand la commande 'une' s'est terminée, pour lancer la 'deux'. Pour cela il faut pouvoir créer une chaîne entre nos différentes commandes et un bon moyen pour le faire est de toutes les identifier.
Pour cela rien de plus simple. LSF propose de donner un nom à vos commandes avec l'option '-J'.
1 2 |
bsub -J première 'ma première commande'<br> bsub -J deuxième 'ma deuxième commande' |
Cette option est très utile et pas seulement pour faire des pipelines. Imaginez que vous ayez 100 fichiers à traiter et que vous utilisiez le cluster pour le faire. Si vous vous apercevez qu'il y a une erreur sur certaines de ces commandes, comment allez-vous pouvoir arrêter juste celles-ci ? Si elles n'ont pas de nom, vous n'avez plus qu'à chercher parmi les 100 commandes ou encore tout arrêter. Mais si vous avez utilisé l'option '-J' vous pouvez arrêter juste ces commandes. De plus avec le symbole '*' vous pouvez arrêter toutes les commandes avec le même morceau de nom.
1 |
bkill -J groupe_1_* |
'bkill' est la commande LSF pour tuer un job et elle prend aussi l'option '-J'. Ici je tue toutes les commandes dont le nom commence par 'groupe_1_', on peut imaginer que j'ai un groupe 2 qui lui ne sera pas arrêté.
Bien, maintenant on sait donner un nom à nos commandes, mais LSF ne sait pas lire, du coup il faut lui dire quelle commande doit attendre l'autre.
…puis connectez-les.
<
p style="text-align : justify;">'bsub' a une autre option extrêmement utile : ‑w
Il s'agit en fait d'une option 'wait' et celle-ci est très bien pensée. Elle vous permet d'attendre qu'une autre commande soit terminée et même de vérifier si elle s'est bien achevée ou si une erreur s'est produite. Elle inclut aussi l'utilisation d'opérateurs logiques (and, or et not) pour pouvoir attendre la fin (ou le lancement) de plusieurs commandes.
Concrètement, il existe quatre conditions possibles : started, done, exit, ended.
Started teste si la commande s'est lancée, ended si elle s'est terminée. Pour 'done' et 'ended' je dois préciser que, sous GNU/Linux, chaque commande renvoie à la fin un code. 0 indique que tout s'est bien passé et toutes les autres possibilités correspondent à différentes erreurs. Si je crée une commande 1 puis une commande 2 avec la condition '-w done(1)' et une commande 3 avec la condition 'exit(1)'. La commande 2 sera exécutée seulement si la commande 1 se termine sans erreur et la commande 3 seulement si la commande 1 se termine avec une erreur. Il est même possible, avec 'exit', d'attendre un code d'erreur spécifique (1, 2 ou 3 …), ce qui permet de prévoir plusieurs réactions. Voici donc un exemple :
1 |
bsub -J messager -w "(done(groupe_1_*) || done(groupe_2_*)) && ; started(trieur)" 'commande envoyer message' |
La commande 'messager' ne démarrera que si toutes les commandes dont le nom commence par 'groupe_1_' se sont bien terminées ou ('||') si toutes les commandes dont le nom commence par 'groupe_2_' se sont bien terminées et ('&&') que la commande 'trieur' a été lancée.
Et voila comment, avec les outils mis à disposition par le queuing système, on peut construire un pipeline. Il suffit ensuite de construire un script (en python par exemple) qui crée et nomme les commandes pour vous et vous obtenez un pipeline performant.
Ici je vous ai présenté une seule stratégie, elle n'est pas unique et n'est pas obligatoirement la meilleure. On pourrait aussi utiliser un fichier de log où les commandes notent quand elles commencent et quand elles se terminent et avoir un distributeur qui lit ce fichier pour savoir quand lancer la commande suivante. Les stratégies sont nombreuses et vous devez vous adapter à votre environnement et aux besoins de vos collègues.
Laisser un commentaire