Version 2 (modified by 9 months ago) ( diff ) | ,
---|
Table of Contents
Table of Contents
- -
- Spécial nouveaux entrants
- Comptes utilisateurs et authentification
- Introduction
- Demande de compte
- Charte Informatique du LPSC
- Changer son mot de passe
- Certificats electroniques
- -
- Support aux utilisateurs
- Traitements des demandes, tickets urgents
- Aide au developpement
- -
- Postes de travail
- Postes de travail
- PC de pret
- Configurations et problemes rencontres
- Programmes sous Windows
- -
- Acces au reseau
- Reseaux filaire et wifi
- Accueil de visiteurs
- Reseau EDUROAM
- Acces externes
- Acces externe par VPN
- -
- Impressions, photocopies, scanner
- Installation et utilisation des imprimantes
- Suivi des impressions
- -
- Stockage centralise
- Introduction
- Utilisation des espaces de stockage du LPSC
- -
- Messagerie / Outils collaboratifs
- Messagerie
- Salles de reunion et videoconference
- Espaces internet
- Surveillance MaJ page web
- Synchronisation ATRIUM
- Liens vers outils collaboratifs
- -
- Calcul et serveurs
- Introduction
- Ferme locale
- Systeme de batch HTCondor
- Serveurs Windows
- Logiciels sous licence
- -
- Divers
- Mise a jour des informations Annuaire IN2P3
- Bases de Linux
- Trucs et astuces sous Linux
- Trucs et astuces de programmation
- SSH
- X2GO
- FileZilla
- SSHFS
- Latex
- Doxygen
- -
- Pour les administrateurs
- Index des pages de l'espace utilisateur
- Espace administrateur acces restreint
Utilisation du système de batch HT-Condor
Machines pour la soumission de tâches
Quatre machines de soumission sont à votre disposition pour soumettre vos tâches avec le système de batch HTCondor:
- lpsc-batch-almalinux et lpsc-batch-almalinux2 (AlmaLinux release 9.2)
- lpsc-batch-fedora (Fedora 39)
- lpsc-batch-centos7 (CentOS Linux release 7.4)
Prérequis pour soumettre une tâche
Pour soumettre une tâche sur HTCondor il faut :
- un *exécutable* qui pourra dans un premier temps être testé en interactif sur les machines de soumission
- un *fichier de description* qui sera lu par le système HTCondor et qui définira les ressources et le type d'environnement dont a besoin votre exécutable.
Commandes utiles
- [xxx@lpsc-cyy] condor_submit <description.submit> : soumet une tâche décrite par un fichier <description.submit>
- [xxx@lpsc-cyy] condor_q (-long)? (-run)? : Voir l'avancement de vos tâches soumises
- [xxx@lpsc-cyy] condor_rm -all : Supprime toutes vos tâches de la file d'attente
- [xxx@lpsc-cyy] condor_status -avail : Voir toutes les machines disponibles dans le batch
- [xxx@lpsc-cyy] condor_status -avail : Voir toutes les machines disponibles dans le batch
- [xxx@lpsc-cyy] condor_status (-long)? (<machine>)? : Voir tous les attributs "ClassAd" de l'ensemble des machines ou d'une machine donnée. (voir documentation). Cela permets de sélectionner les machines avec les ressources nécessaires pour exécuter une tâche en ajoutant une ligne "+Requirements" dans un fichier de soumission (voir exemples ci-dessous)
Liens utiles
- https://htcondor.readthedocs.io/en/latest/
- http://www.iac.es/sieinvens/siepedia/pmwiki.php?n=HOWTOs.CondorHowTo#howto_nestloop
- https://www-auth.cs.wisc.edu/lists/htcondor-users/2016-August/msg00034.shtml
- http://www.iac.es/sieinvens/siepedia/pmwiki.php?n=HOWTOs.CondorSubmitFile
Le concept d'univers
Un univers pour HTCondor est un environnement d’exécution pour une tâche. HTCondor définit plusieurs univers, plusieurs d'entre-eux ont été configurés sur la ferme de calcul du LPSC :
- vanilla, l'univers "par défaut" avec lequel vous pouvez soumettre la plupart de vos tâches
- parallel, l'univers qui prend en charge les tâches qui s'étendent sur plusieurs machines, où les multiples processus d'une tâche s'exécutent simultanément, éventuellement en communiquant entre eux.
- docker, l'univers qui permet d'utiliser des containers docker depuis DockerHub pour faire s’exécuter vos tâches
- java, l'univers pour les tâches à base d'exécutables Java
D'autres univers existent et pourront être proposés au besoin.
Univers vanilla
Python
test_python.submit
# Définition de l'univers universe = vanilla # Chemin vers l'exécutable executable = ./test_python.py # Fichiers à produire en sortie output = test_python.out log = test_python.log error = test_python.err # directive pour le transfert des fichiers de sortie depuis le nœud d’exécution should_transfer_files = YES when_to_transfer_output = ON_EXIT queue
test_python.py
#!/usr/bin/env python3 import platform from datetime import datetime def afficher_informations_systeme(): # Obtenir le nom de la machine nom_machine = platform.node() # Obtenir la date et l'heure actuelles date_heure_actuelles = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # Afficher les informations print(f"Nom de la machine: {nom_machine}") print(f"Date et heure actuelles: {date_heure_actuelles}") # Appeler la fonction pour afficher les informations afficher_informations_systeme()
Shell
nombresPremiers.submit
universe = vanilla executable = nombresPremiers.sh # Arguments à passer à l'executable arguments = 100 # Fichier de sortie à rappatrier depuis les nœuds d'execution output=results.output.$(Process) error=results.error.$(Process) log=results.log should_transfer_files=YES when_to_transfer_output = ON_EXIT queue
nombresPremiers.sh
#!/bin/sh limit=${1} echo "Les nombres premiers entre 1 et ${limit} sont :" # Utilisation du crible d'Ératosthène pour trouver les nombres premiers jusqu'à "limit" sieve=( $(seq 2 $limit) ) count=0 for ((i=2; i*i<=limit; i++)) do if [ ${sieve[$i-2]} -ne 0 ] then for ((j=i*i; j<=limit; j+=i)) do sieve[$j-2]=0 done fi done # Affichage des nombres premiers restants for num in "${sieve[@]}" do if [ $num -ne 0 ] then echo -n "$num " count=$((count + 1)) if [ $count -eq 50 ] then echo count=0 fi fi done echo # Saut de ligne final si nécessaire
MPI avec OpenMPI
Dans l'univers vanilla, les tâches MPI ne tourne que sur une seule machine en utilisant tous les cœurs et processeurs demandés dans la limite des capacités de la machine hôte.
run.sh
run.sh est un script shell pour faire le setup de l'environnement.
#!/bin/bash export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib/:${LD_LIBRARY_PATH} export PATH=/usr/lib64/openmpi/bin/:${PATH} mpirun $*
L'executable est compilé au préalable avec : /usr/lib64/mpi/bin/mpicc simple.c -o simple
simple.c
#include <stdio.h> #include <mpi.h> int main(int argc, char** argv) { int rank, size; // Initialisation de l'environnement MPI MPI_Init(&argc, &argv); // Obtention du rang du processus MPI_Comm_rank(MPI_COMM_WORLD, &rank); // Obtention de la taille du communicateur MPI_Comm_size(MPI_COMM_WORLD, &size); // Affichage du rang et de la taille printf("Hello from process %d of %d\n", rank, size); // Finalisation de l'environnement MPI MPI_Finalize(); return 0; }
simple.submit
universe = vanilla executable = run.sh arguments = -np 8 simple output = simple.out error = simple.err log = simple.log should_transfer_files = yes when_to_transfer_output = on_exit transfer_input_files = simple # Spécifiez le nombre de slots (processus MPI) requis request_cpus = 1 request_memory = 1024M request_disk = 10240K #+Requirements = OpSysAndVer =?= "AlmaLinux" #+Requirements = machine =?= "lpsc-c21.in2p3.fr" +Requirements = member(machine, {"lpsc-c22.in2p3.fr", "lpsc-c23.in2p3.fr"}) queue
MPI avec MPICH
Dans l'univers vanilla, les tâches MPI ne tourne que sur une seule machine en utilisant tous les cœurs et processeurs demandés dans la limite des capacités de la machine hôte.
run.sh
run.sh est un script shell pour faire le setup de l'environnement.
#!/bin/bash export LD_LIBRARY_PATH=/usr/lib64/mpich/lib/:${LD_LIBRARY_PATH} export PATH=/usr/lib64/mpich/bin/:${PATH} mpirun $*
L'executable est compilé au préalable avec : /usr/lib64/mpich/bin/mpicc simple.c -o simple
simple.c
#include <stdio.h> #include <mpi.h> int main(int argc, char** argv) { int rank, size; // Initialisation de l'environnement MPI MPI_Init(&argc, &argv); // Obtention du rang du processus MPI_Comm_rank(MPI_COMM_WORLD, &rank); // Obtention de la taille du communicateur MPI_Comm_size(MPI_COMM_WORLD, &size); // Affichage du rang et de la taille printf("Hello from process %d of %d\n", rank, size); // Finalisation de l'environnement MPI MPI_Finalize(); return 0; }
simple.submit
universe = vanilla executable = run.sh # Attention au chemin relatif pour l'executable avec MPICH: ./simple et pas simple arguments = -np 8 ./simple output = simple.out error = simple.err log = simple.log should_transfer_files = yes when_to_transfer_output = on_exit transfer_input_files = simple # Spécifiez le nombre de slots (processus MPI) requis request_cpus = 1 request_memory = 1024M request_disk = 10240K #+Requirements = OpSysAndVer =?= "AlmaLinux" #+Requirements = machine =?= "lpsc-c21.in2p3.fr" +Requirements = member(machine, {"lpsc-c22.in2p3.fr", "lpsc-c23.in2p3.fr"}) queue
Univers parallel
Shell
parallel.submit
#!/bin/sh echo "Hello I am ${HOSTNAME} and I am node ${1}"
parallel.submit
universe = parallel executable = parallel.sh arguments = $(Node) machine_count = 2 output = parallel.$(Node).out error = parallel.$(Node).err # Il n'est pas possible de faire un fichier de log par nœuds log = parallel.log should_transfer_files = yes when_to_transfer_output = on_exit +Requirements = member(machine, {"lpsc-c21.in2p3.fr","lpsc-c22.in2p3.fr"}) queue
MPI avec OpenMPI
Pour soumettre une tâche avec OpenMPI dans l'univers parallel, vous pouvez utiliser un script fourni par HTCondor et qui se trouve sur les machines de soumission sous : /usr/share/doc/condor/examples/openmpiscript.
Dans l'exemple qui suit, on execute un fichier example préalablement compilé à partir d'un fichier source example.c : /usr/lib64/openmpi/bin/mpicc example.c -o example
example.c
#include <stdio.h> #include <mpi.h> int main(int argc, char **argv) { int rank, size; int local_array[5] = {1, 2, 3, 4, 5}; int global_sum[5] = {0, 0, 0, 0, 0}; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); // Chaque processus ajoute son tableau local à global_sum MPI_Reduce(local_array, global_sum, 5, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); // Seul le processus de rang 0 affiche le résultat if (rank == 0) { printf("Résultat de la somme globale : "); for (int i = 0; i < 5; i++) { printf("%d ", global_sum[i]); } printf("\n"); } MPI_Finalize(); return 0; }
example.submit
universe = parallel executable = /usr/share/doc/condor/examples/openmpiscript arguments = example machine_count = 2 output = example.out error = example.err log = example.log should_transfer_files = yes when_to_transfer_output = on_exit transfer_input_files = example want_io_proxy = True request_cpus = 8 #request_memory = 1024M #request_disk = 10240K #+Requirements = OpSysAndVer =?= "AlmaLinux" +Requirements = member(machine, {"lpsc-c21.in2p3.fr","lpsc-c22.in2p3.fr"}) queue
MPI avec MPICH
Non disponible dans l'univers parallèle...
Univers java
Les programmes Java peuvent être exécutés après avoir été préalablement "compilés" avec un JDK (Java Devlopment Kit) normalement présent sur les machines de soumission:
- génération d'un fichier .class
javac !MachineDate.java
- génération d'une archive exécutable .jar (optionnel)
jar cf !MachineDate.jar ./!MachineDate.class
MachineDate.java
import java.net.InetAddress; import java.util.Date; public class MachineDate { public static void main(String[] args) { try { InetAddress localMachine = InetAddress.getLocalHost(); System.out.println("Machine: " + localMachine.getHostName()); System.out.println("Date: " + new Date()); } catch (Exception e) { e.printStackTrace(); } } }
Option 1 : MachineDate.class
universe = java executable = MachineDate.class arguments = MachineDate transfer_input_files = ${PWD}/MachineDate.class log = MachineDateClass.log output = MachineDateClass.out error = MachineDateClass.err should_transfer_files = YES when_to_transfer_output = ON_EXIT request_cpus = 1 request_memory = 1024M request_disk = 10240K queue
Option 2 : MachineDate.jar
universe = java executable = MachineDate.class jar_files = MachineDate.jar arguments = MachineDate log = MachineDateJar.log output = MachineDateJar.out error = MachineDateJar.err transfer_input_files = ${PWD}/MachineDate.jar should_transfer_files = YES when_to_transfer_output = ON_EXIT queue
Univers docker
Docker est déployé sur les machines de la ferme locale. Il est possible d'utiliser n'importe quelle image pour définir un environnement d’exécution pour vos tâches. Les images docker peuvent être trouvées sur le site DockerHub
Exécuter "cat" sur Debian
docker.submit
#universe = docker is optional universe = docker # nom de l'image sur dockerHub docker_image = debian # programme à exécuter dans l'environnement debian avec ses arguments executable = /bin/cat arguments = /etc/hosts # fichiers de sortie à rapatrier depuis le nœud d’exécution de la ferme locale should_transfer_files = YES when_to_transfer_output = ON_EXIT output = out.$(Process) error = err.$(Process) log = log.$(Process) # pré-requis pour sélection d'un nœud de la ferme locale où faire tourner la tâche request_cpus = 1 request_memory = 1024M request_disk = 10240K queue 1
Génération d'un histogramme avec ROOT
ROOT est un programme d'analyse qui n'est pas forcément installé ou configuré sur les machines de la ferme locale. Il est cependant possible de l'utiliser via une image docker.
Candle Histo.C : le fichier root à exécuter
#include <TROOT.h> #include <TCanvas.h> #include <TH2I.h> #include <TRandom.h> void candlehisto() { // Définissez la taille du canevas avec une résolution plus élevée TCanvas *c1 = new TCanvas("c1", "Candle Presets", 1920, 1080); c1->Divide(3, 2); TRandom *rng = new TRandom(); TH2I *h1 = new TH2I("h1", "Sin", 18, 0, 360, 100, -1.5, 1.5); h1->GetXaxis()->SetTitle("Deg"); float myRand; for (int i = 0; i < 360; i += 10) { for (int j = 0; j < 100; j++) { myRand = rng->Gaus(sin(i * 3.14 / 180), 0.2); h1->Fill(i, myRand); } } c1->cd(1); for (int i = 1; i < 7; i++) { c1->cd(i); TString title = TString::Format("CANDLEX%d", i); TH2I *myhist = (TH2I *)h1->DrawCopy(title); myhist->SetTitle(title); } // Sauvegardez dans un seul fichier image dans le répertoire courant TString imgFileName = "output.png"; c1->SaveAs(imgFileName); } int main() { candlehisto(); return 0; }
candlehisto.submit : le fichier de soumission
#universe = docker is optional universe = docker # nom de l'image sur dockerHub docker_image = rootproject/root # programme à exécuter dans l'environnement ROOT avec ses arguments executable = /opt/root/bin/root.exe arguments = -b -q -l candlehisto.C # Fichier d'input à transférer sur le nœud où va tourner la tâche transfer_input_files = candlehisto.C # Fichier de résultat à transférer depuis le nœud ou a tourner la tâche en fin d’exécution transfer_output_files = output.png should_transfer_files = YES when_to_transfer_output = ON_EXIT # Fichier de logs output = out.$(ClusterId).$(ProcId) error = err.$(ClusterId).$(ProcId) log = log.$(ClusterId).$(ProcId) # Pré-requis pour la sélection du nœud local ou faire tourner la tâche # mémoire requise request_memory = 2000M # type de système requis +Requirements = OpSysAndVer =?= "AlmaLinux" # nom du nœud d’exécution de la ferme locale +Requirements = machine =?= "lpsc-c27.in2p3.fr" queue 1
Le concept de jobset
HTCondor permet la soumission d'ensemble de tâches. La syntaxe du fichier de soumission est légèrement différente que pour une tâche simple, vous pouvez trouver la documentation ici
La commande HTcondor diffère également, elle est de la forme suivante : htcondor jobset submit jobs.set
Soumission d'un ensemble de tâches OpenMPI
On retrouve dans le fichier jobs.set le contenu d'un fichier de soumission de l'univers vanilla. La différence réside dans son "encapsulation" dans un itérateur dans le ficher de soumission du jobset.
jobs.set
name = ExampleJobSet # Définition d'un itérateur: un tableau contenant des items qui sont des liste d'arguments (dans notre cas, un seul argument par item, le nom d'une machine) iterator = table machine { lpsc-c0.in2p3.fr lpsc-c8.in2p3.fr lpsc-c12.in2p3.fr lpsc-c13.in2p3.fr lpsc-c15.in2p3.fr lpsc-c16.in2p3.fr lpsc-c17.in2p3.fr lpsc-c18.in2p3.fr lpsc-c19.in2p3.fr lpsc-c20.in2p3.fr lpsc-c21.in2p3.fr lpsc-c22.in2p3.fr lpsc-c23.in2p3.fr lpsc-c24.in2p3.fr lpsc-c25.in2p3.fr lpsc-c26.in2p3.fr lpsc-c27.in2p3.fr } # définition de la tâche de base qui va être "itérée" job { universe = vanilla executable = run.sh arguments = -np 8 example output = example_$(machine).out error = example_$(machine).err log = example.log should_transfer_files = yes when_to_transfer_output = on_exit transfer_input_files = example # renommage du fichier de sortie en fonction du nom de la machine transfer_output_remaps = "example_$(machine).out=$(machine)/example_$(machine).out ; example_$(machine).err=$(machine)/example_$(machine).err" # Spécifiez le nombre de slots (processus MPI) requis request_cpus = 1 request_memory = 1024M request_disk = 10240K +Requirements = machine =?= "$(machine)" +WantParallelSchedulingGroups = True queue }
Autre exemples
more testprog01.sh #! /bin/sh echo "HT-condor testprog01" echo "I'm process id $$ on" `hostname` echo "This is sent to standard error" 1>&2 date echo "Running as binary $0" "$@" echo "My name (argument 1) is $1" echo "My sleep duration (argument 2) is $2" sleep $2 echo "Sleep of $2 seconds finished. Exiting" exit 0
exemple d'un fichier de description de soumission:
more testprog01.submit executable=testprog01.sh universe=vanilla arguments=Example.$(Cluster).$(Process) 100 output=results.output.$(Process) error=results.error.$(Process) log=results.log notification=never should_transfer_files=YES when_to_transfer_output = ON_EXIT queue
exemple d'un fichier de description de soumission en sélectionnant !AlmaLinux9 comme Operating System:
more testprog2.submit executable=testprog01.sh universe=vanilla arguments=Example.$(Cluster).$(Process) 100 output=results.output.$(Process) error=results.error.$(Process) log=results.log notification=never should_transfer_files=YES when_to_transfer_output = ON_EXIT requirements = (OpSysAndVer =?= "AlmaLinux9") queue
Pour choisir Centos7:
requirements = (OpSysAndVer =?= "CentOS7")
Pour choisir Fedora39:
requirements = (OpSysAndVer =?= "Fedora39")
L'utilisateur définit lui-même le groupe auxquel il appartient via une instruction de type :
accounting_group = informatique