120 | | |
121 | | = Utilisation du système de batch HT-Condor = |
122 | | |
123 | | == Machines pour la soumission de tâches == |
124 | | |
125 | | Quatre machines de soumission sont à votre disposition pour soumettre vos tâches avec le système de batch HTCondor: |
126 | | - lpsc-batch-almalinux et lpsc-batch-almalinux2 (!AlmaLinux release 9.2) |
127 | | - lpsc-batch-fedora (Fedora 39) |
128 | | - lpsc-batch-centos7 (CentOS Linux release 7.4) |
129 | | |
130 | | == Prérequis pour soumettre une tâche == |
131 | | |
132 | | Pour soumettre une tâche sur HTCondor il faut : |
133 | | - un *exécutable* qui pourra dans un premier temps être testé en interactif sur les machines de soumission |
134 | | - 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. |
135 | | |
136 | | == Commandes utiles == |
137 | | |
138 | | - [xxx@lpsc-cyy] condor_submit <description.submit> : soumet une tâche décrite par un fichier <description.submit> |
139 | | - [xxx@lpsc-cyy] condor_q (-long)? (-run)? : Voir l'avancement de vos tâches soumises |
140 | | - [xxx@lpsc-cyy] condor_rm -all : Supprime toutes vos tâches de la file d'attente |
141 | | - [xxx@lpsc-cyy] condor_status -avail : Voir toutes les machines disponibles dans le batch |
142 | | - [xxx@lpsc-cyy] condor_status -avail : Voir toutes les machines disponibles dans le batch |
143 | | - [xxx@lpsc-cyy] condor_status (-long)? (<machine>)? : Voir tous les attributs "!ClassAd" de l'ensemble des machines ou d'une machine donnée. (voir [https://htcondor.readthedocs.io/en/latest/users-manual/matchmaking-with-classads.html documentation]). |
144 | | 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) |
145 | | |
146 | | == Liens utiles == |
147 | | - https://htcondor.readthedocs.io/en/latest/ |
148 | | - http://www.iac.es/sieinvens/siepedia/pmwiki.php?n=HOWTOs.CondorHowTo#howto_nestloop |
149 | | - https://www-auth.cs.wisc.edu/lists/htcondor-users/2016-August/msg00034.shtml |
150 | | - http://www.iac.es/sieinvens/siepedia/pmwiki.php?n=HOWTOs.CondorSubmitFile |
151 | | |
152 | | == Le concept d'univers == |
153 | | |
154 | | Un [https://htcondor.readthedocs.io/en/23.0/users-manual/choosing-an-htcondor-universe.html univers pour HTCondor] est un environnement d’exécution pour une tâche. |
155 | | HTCondor définit plusieurs univers, plusieurs d'entre-eux ont été configurés sur la ferme de calcul du LPSC : |
156 | | |
157 | | - [#Universvanilla vanilla], l'univers "par défaut" avec lequel vous pouvez soumettre la plupart de vos tâches |
158 | | - [#Universparallel 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. |
159 | | - [#Universdocker docker], l'univers qui permet d'utiliser des containers docker depuis [https://hub.docker.com/ DockerHub] pour faire s’exécuter vos tâches |
160 | | - [#Universjava java], l'univers pour les tâches à base d'exécutables Java |
161 | | |
162 | | D'autres univers existent et pourront être proposés au besoin. |
163 | | |
164 | | === Univers vanilla === |
165 | | |
166 | | ==== Python ==== |
167 | | |
168 | | **test_python.submit** |
169 | | |
170 | | {{{ |
171 | | |
172 | | # Définition de l'univers |
173 | | universe = vanilla |
174 | | |
175 | | # Chemin vers l'exécutable |
176 | | executable = ./test_python.py |
177 | | |
178 | | # Fichiers à produire en sortie |
179 | | output = test_python.out |
180 | | log = test_python.log |
181 | | error = test_python.err |
182 | | |
183 | | # directive pour le transfert des fichiers de sortie depuis le nœud d’exécution |
184 | | should_transfer_files = YES |
185 | | when_to_transfer_output = ON_EXIT |
186 | | |
187 | | queue |
188 | | }}} |
189 | | |
190 | | **test_python.py** |
191 | | |
192 | | {{{ |
193 | | #!/usr/bin/env python3 |
194 | | |
195 | | import platform |
196 | | from datetime import datetime |
197 | | |
198 | | def afficher_informations_systeme(): |
199 | | # Obtenir le nom de la machine |
200 | | nom_machine = platform.node() |
201 | | |
202 | | # Obtenir la date et l'heure actuelles |
203 | | date_heure_actuelles = datetime.now().strftime("%Y-%m-%d %H:%M:%S") |
204 | | |
205 | | # Afficher les informations |
206 | | print(f"Nom de la machine: {nom_machine}") |
207 | | print(f"Date et heure actuelles: {date_heure_actuelles}") |
208 | | |
209 | | # Appeler la fonction pour afficher les informations |
210 | | afficher_informations_systeme() |
211 | | |
212 | | }}} |
213 | | |
214 | | ==== Shell ==== |
215 | | |
216 | | **nombresPremiers.submit** |
217 | | |
218 | | {{{ |
219 | | universe = vanilla |
220 | | |
221 | | executable = nombresPremiers.sh |
222 | | |
223 | | # Arguments à passer à l'executable |
224 | | arguments = 100 |
225 | | |
226 | | # Fichier de sortie à rappatrier depuis les nœuds d'execution |
227 | | output=results.output.$(Process) |
228 | | error=results.error.$(Process) |
229 | | log=results.log |
230 | | |
231 | | should_transfer_files=YES |
232 | | when_to_transfer_output = ON_EXIT |
233 | | |
234 | | queue |
235 | | |
236 | | }}} |
237 | | |
238 | | **nombresPremiers.sh** |
239 | | |
240 | | {{{ |
241 | | #!/bin/sh |
242 | | |
243 | | limit=${1} |
244 | | echo "Les nombres premiers entre 1 et ${limit} sont :" |
245 | | |
246 | | # Utilisation du crible d'Ératosthène pour trouver les nombres premiers jusqu'à "limit" |
247 | | sieve=( $(seq 2 $limit) ) |
248 | | count=0 |
249 | | |
250 | | for ((i=2; i*i<=limit; i++)) |
251 | | do |
252 | | if [ ${sieve[$i-2]} -ne 0 ] |
253 | | then |
254 | | for ((j=i*i; j<=limit; j+=i)) |
255 | | do |
256 | | sieve[$j-2]=0 |
257 | | done |
258 | | fi |
259 | | done |
260 | | |
261 | | # Affichage des nombres premiers restants |
262 | | for num in "${sieve[@]}" |
263 | | do |
264 | | if [ $num -ne 0 ] |
265 | | then |
266 | | echo -n "$num " |
267 | | count=$((count + 1)) |
268 | | |
269 | | if [ $count -eq 50 ] |
270 | | then |
271 | | echo |
272 | | count=0 |
273 | | fi |
274 | | fi |
275 | | done |
276 | | |
277 | | echo # Saut de ligne final si nécessaire |
278 | | |
279 | | }}} |
280 | | |
281 | | ==== MPI avec OpenMPI ==== |
282 | | |
283 | | 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. |
284 | | |
285 | | **run.sh** |
286 | | |
287 | | run.sh est un script shell pour faire le setup de l'environnement. |
288 | | |
289 | | {{{ |
290 | | #!/bin/bash |
291 | | |
292 | | export LD_LIBRARY_PATH=/usr/lib64/openmpi/lib/:${LD_LIBRARY_PATH} |
293 | | export PATH=/usr/lib64/openmpi/bin/:${PATH} |
294 | | |
295 | | mpirun $* |
296 | | |
297 | | }}} |
298 | | |
299 | | L'executable est compilé au préalable avec : {{{/usr/lib64/mpi/bin/mpicc simple.c -o simple}}} |
300 | | |
301 | | **simple.c** |
302 | | |
303 | | {{{ |
304 | | #include <stdio.h> |
305 | | #include <mpi.h> |
306 | | |
307 | | int main(int argc, char** argv) { |
308 | | int rank, size; |
309 | | |
310 | | // Initialisation de l'environnement MPI |
311 | | MPI_Init(&argc, &argv); |
312 | | |
313 | | // Obtention du rang du processus |
314 | | MPI_Comm_rank(MPI_COMM_WORLD, &rank); |
315 | | |
316 | | // Obtention de la taille du communicateur |
317 | | MPI_Comm_size(MPI_COMM_WORLD, &size); |
318 | | |
319 | | // Affichage du rang et de la taille |
320 | | printf("Hello from process %d of %d\n", rank, size); |
321 | | |
322 | | // Finalisation de l'environnement MPI |
323 | | MPI_Finalize(); |
324 | | |
325 | | return 0; |
326 | | } |
327 | | |
328 | | }}} |
329 | | |
330 | | **simple.submit** |
331 | | {{{ |
332 | | universe = vanilla |
333 | | executable = run.sh |
334 | | arguments = -np 8 simple |
335 | | |
336 | | output = simple.out |
337 | | error = simple.err |
338 | | log = simple.log |
339 | | |
340 | | should_transfer_files = yes |
341 | | when_to_transfer_output = on_exit |
342 | | transfer_input_files = simple |
343 | | |
344 | | # Spécifiez le nombre de slots (processus MPI) requis |
345 | | request_cpus = 1 |
346 | | request_memory = 1024M |
347 | | request_disk = 10240K |
348 | | |
349 | | #+Requirements = OpSysAndVer =?= "AlmaLinux" |
350 | | #+Requirements = machine =?= "lpsc-c21.in2p3.fr" |
351 | | +Requirements = member(machine, {"lpsc-c22.in2p3.fr", "lpsc-c23.in2p3.fr"}) |
352 | | |
353 | | queue |
354 | | |
355 | | }}} |
356 | | |
357 | | ==== MPI avec MPICH ==== |
358 | | |
359 | | 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. |
360 | | |
361 | | **run.sh** |
362 | | |
363 | | run.sh est un script shell pour faire le setup de l'environnement. |
364 | | |
365 | | {{{ |
366 | | #!/bin/bash |
367 | | |
368 | | export LD_LIBRARY_PATH=/usr/lib64/mpich/lib/:${LD_LIBRARY_PATH} |
369 | | export PATH=/usr/lib64/mpich/bin/:${PATH} |
370 | | |
371 | | mpirun $* |
372 | | |
373 | | |
374 | | }}} |
375 | | |
376 | | L'executable est compilé au préalable avec : {{{/usr/lib64/mpich/bin/mpicc simple.c -o simple}}} |
377 | | |
378 | | **simple.c** |
379 | | |
380 | | {{{ |
381 | | #include <stdio.h> |
382 | | #include <mpi.h> |
383 | | |
384 | | int main(int argc, char** argv) { |
385 | | int rank, size; |
386 | | |
387 | | // Initialisation de l'environnement MPI |
388 | | MPI_Init(&argc, &argv); |
389 | | |
390 | | // Obtention du rang du processus |
391 | | MPI_Comm_rank(MPI_COMM_WORLD, &rank); |
392 | | |
393 | | // Obtention de la taille du communicateur |
394 | | MPI_Comm_size(MPI_COMM_WORLD, &size); |
395 | | |
396 | | // Affichage du rang et de la taille |
397 | | printf("Hello from process %d of %d\n", rank, size); |
398 | | |
399 | | // Finalisation de l'environnement MPI |
400 | | MPI_Finalize(); |
401 | | |
402 | | return 0; |
403 | | } |
404 | | }}} |
405 | | |
406 | | **simple.submit** |
407 | | {{{ |
408 | | universe = vanilla |
409 | | executable = run.sh |
410 | | |
411 | | # Attention au chemin relatif pour l'executable avec MPICH: ./simple et pas simple |
412 | | arguments = -np 8 ./simple |
413 | | |
414 | | output = simple.out |
415 | | error = simple.err |
416 | | log = simple.log |
417 | | |
418 | | should_transfer_files = yes |
419 | | when_to_transfer_output = on_exit |
420 | | transfer_input_files = simple |
421 | | |
422 | | # Spécifiez le nombre de slots (processus MPI) requis |
423 | | request_cpus = 1 |
424 | | request_memory = 1024M |
425 | | request_disk = 10240K |
426 | | |
427 | | #+Requirements = OpSysAndVer =?= "AlmaLinux" |
428 | | #+Requirements = machine =?= "lpsc-c21.in2p3.fr" |
429 | | +Requirements = member(machine, {"lpsc-c22.in2p3.fr", "lpsc-c23.in2p3.fr"}) |
430 | | |
431 | | queue |
432 | | |
433 | | }}} |
434 | | |
435 | | |
436 | | === Univers parallel === |
437 | | |
438 | | ==== Shell ==== |
439 | | |
440 | | **parallel.submit** |
441 | | {{{ |
442 | | #!/bin/sh |
443 | | |
444 | | echo "Hello I am ${HOSTNAME} and I am node ${1}" |
445 | | |
446 | | }}} |
447 | | |
448 | | **parallel.submit** |
449 | | {{{ |
450 | | universe = parallel |
451 | | executable = parallel.sh |
452 | | arguments = $(Node) |
453 | | |
454 | | machine_count = 2 |
455 | | |
456 | | output = parallel.$(Node).out |
457 | | error = parallel.$(Node).err |
458 | | # Il n'est pas possible de faire un fichier de log par nœuds |
459 | | log = parallel.log |
460 | | |
461 | | should_transfer_files = yes |
462 | | when_to_transfer_output = on_exit |
463 | | |
464 | | +Requirements = member(machine, {"lpsc-c21.in2p3.fr","lpsc-c22.in2p3.fr"}) |
465 | | |
466 | | queue |
467 | | |
468 | | }}} |
469 | | |
470 | | ==== MPI avec OpenMPI ==== |
471 | | |
472 | | 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. |
473 | | |
474 | | 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}}} |
475 | | |
476 | | **example.c** |
477 | | {{{ |
478 | | #include <stdio.h> |
479 | | #include <mpi.h> |
480 | | |
481 | | int main(int argc, char **argv) { |
482 | | int rank, size; |
483 | | int local_array[5] = {1, 2, 3, 4, 5}; |
484 | | int global_sum[5] = {0, 0, 0, 0, 0}; |
485 | | |
486 | | MPI_Init(&argc, &argv); |
487 | | MPI_Comm_rank(MPI_COMM_WORLD, &rank); |
488 | | MPI_Comm_size(MPI_COMM_WORLD, &size); |
489 | | |
490 | | // Chaque processus ajoute son tableau local à global_sum |
491 | | |
492 | | MPI_Reduce(local_array, global_sum, 5, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); |
493 | | |
494 | | // Seul le processus de rang 0 affiche le résultat |
495 | | if (rank == 0) { |
496 | | printf("Résultat de la somme globale : "); |
497 | | for (int i = 0; i < 5; i++) { |
498 | | printf("%d ", global_sum[i]); |
499 | | } |
500 | | printf("\n"); |
501 | | } |
502 | | |
503 | | MPI_Finalize(); |
504 | | return 0; |
505 | | } |
506 | | |
507 | | }}} |
508 | | |
509 | | **example.submit** |
510 | | {{{ |
511 | | universe = parallel |
512 | | |
513 | | executable = /usr/share/doc/condor/examples/openmpiscript |
514 | | arguments = example |
515 | | |
516 | | machine_count = 2 |
517 | | |
518 | | output = example.out |
519 | | error = example.err |
520 | | log = example.log |
521 | | |
522 | | should_transfer_files = yes |
523 | | when_to_transfer_output = on_exit |
524 | | transfer_input_files = example |
525 | | want_io_proxy = True |
526 | | |
527 | | request_cpus = 8 |
528 | | #request_memory = 1024M |
529 | | #request_disk = 10240K |
530 | | |
531 | | #+Requirements = OpSysAndVer =?= "AlmaLinux" |
532 | | +Requirements = member(machine, {"lpsc-c21.in2p3.fr","lpsc-c22.in2p3.fr"}) |
533 | | |
534 | | queue |
535 | | |
536 | | }}} |
537 | | ==== MPI avec MPICH ==== |
538 | | |
539 | | Non disponible dans l'univers parallèle... |
540 | | |
541 | | === Univers java === |
542 | | |
543 | | 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: |
544 | | * génération d'un fichier .class |
545 | | {{{ |
546 | | javac !MachineDate.java |
547 | | }}} |
548 | | * génération d'une archive exécutable .jar (optionnel) |
549 | | {{{ |
550 | | jar cf !MachineDate.jar ./!MachineDate.class |
551 | | }}} |
552 | | |
553 | | **!MachineDate.java** |
554 | | |
555 | | {{{ |
556 | | import java.net.InetAddress; |
557 | | import java.util.Date; |
558 | | |
559 | | public class MachineDate { |
560 | | public static void main(String[] args) { |
561 | | try { |
562 | | InetAddress localMachine = InetAddress.getLocalHost(); |
563 | | System.out.println("Machine: " + localMachine.getHostName()); |
564 | | System.out.println("Date: " + new Date()); |
565 | | } catch (Exception e) { |
566 | | e.printStackTrace(); |
567 | | } |
568 | | } |
569 | | } |
570 | | }}} |
571 | | |
572 | | |
573 | | **Option 1 : !MachineDate.class** |
574 | | |
575 | | {{{ |
576 | | universe = java |
577 | | |
578 | | executable = MachineDate.class |
579 | | arguments = MachineDate |
580 | | transfer_input_files = ${PWD}/MachineDate.class |
581 | | |
582 | | log = MachineDateClass.log |
583 | | output = MachineDateClass.out |
584 | | error = MachineDateClass.err |
585 | | |
586 | | should_transfer_files = YES |
587 | | when_to_transfer_output = ON_EXIT |
588 | | |
589 | | request_cpus = 1 |
590 | | request_memory = 1024M |
591 | | request_disk = 10240K |
592 | | |
593 | | queue |
594 | | |
595 | | }}} |
596 | | |
597 | | **Option 2 : !MachineDate.jar** |
598 | | |
599 | | {{{ |
600 | | universe = java |
601 | | |
602 | | executable = MachineDate.class |
603 | | jar_files = MachineDate.jar |
604 | | arguments = MachineDate |
605 | | |
606 | | log = MachineDateJar.log |
607 | | output = MachineDateJar.out |
608 | | error = MachineDateJar.err |
609 | | |
610 | | transfer_input_files = ${PWD}/MachineDate.jar |
611 | | should_transfer_files = YES |
612 | | when_to_transfer_output = ON_EXIT |
613 | | |
614 | | queue |
615 | | }}} |
616 | | |
617 | | === Univers docker === |
618 | | |
619 | | 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. |
620 | | Les images docker peuvent être trouvées sur le site [https://hub.docker.com/ DockerHub] |
621 | | |
622 | | ==== Exécuter "cat" sur Debian ==== |
623 | | |
624 | | **docker.submit** |
625 | | |
626 | | {{{ |
627 | | #universe = docker is optional |
628 | | universe = docker |
629 | | |
630 | | # nom de l'image sur dockerHub |
631 | | docker_image = debian |
632 | | |
633 | | # programme à exécuter dans l'environnement debian avec ses arguments |
634 | | executable = /bin/cat |
635 | | arguments = /etc/hosts |
636 | | |
637 | | # fichiers de sortie à rapatrier depuis le nœud d’exécution de la ferme locale |
638 | | should_transfer_files = YES |
639 | | when_to_transfer_output = ON_EXIT |
640 | | output = out.$(Process) |
641 | | error = err.$(Process) |
642 | | log = log.$(Process) |
643 | | |
644 | | # pré-requis pour sélection d'un nœud de la ferme locale où faire tourner la tâche |
645 | | request_cpus = 1 |
646 | | request_memory = 1024M |
647 | | request_disk = 10240K |
648 | | |
649 | | queue 1 |
650 | | }}} |
651 | | |
652 | | ==== Génération d'un histogramme avec ROOT ==== |
653 | | |
654 | | ROOT est un programme d'analyse qui n'est pas forcément installé ou configuré sur les machines de la ferme locale. |
655 | | Il est cependant possible de l'utiliser via une image docker. |
656 | | |
657 | | **Candle Histo.C** : le fichier root à exécuter |
658 | | {{{ |
659 | | #include <TROOT.h> |
660 | | #include <TCanvas.h> |
661 | | #include <TH2I.h> |
662 | | #include <TRandom.h> |
663 | | |
664 | | void candlehisto() |
665 | | { |
666 | | // Définissez la taille du canevas avec une résolution plus élevée |
667 | | TCanvas *c1 = new TCanvas("c1", "Candle Presets", 1920, 1080); |
668 | | c1->Divide(3, 2); |
669 | | |
670 | | TRandom *rng = new TRandom(); |
671 | | TH2I *h1 = new TH2I("h1", "Sin", 18, 0, 360, 100, -1.5, 1.5); |
672 | | h1->GetXaxis()->SetTitle("Deg"); |
673 | | |
674 | | float myRand; |
675 | | for (int i = 0; i < 360; i += 10) |
676 | | { |
677 | | for (int j = 0; j < 100; j++) |
678 | | { |
679 | | myRand = rng->Gaus(sin(i * 3.14 / 180), 0.2); |
680 | | h1->Fill(i, myRand); |
681 | | } |
682 | | } |
683 | | |
684 | | c1->cd(1); |
685 | | for (int i = 1; i < 7; i++) |
686 | | { |
687 | | c1->cd(i); |
688 | | TString title = TString::Format("CANDLEX%d", i); |
689 | | TH2I *myhist = (TH2I *)h1->DrawCopy(title); |
690 | | myhist->SetTitle(title); |
691 | | } |
692 | | |
693 | | // Sauvegardez dans un seul fichier image dans le répertoire courant |
694 | | TString imgFileName = "output.png"; |
695 | | c1->SaveAs(imgFileName); |
696 | | } |
697 | | |
698 | | int main() |
699 | | { |
700 | | candlehisto(); |
701 | | return 0; |
702 | | } |
703 | | |
704 | | }}} |
705 | | |
706 | | **candlehisto.submit** : le fichier de soumission |
707 | | |
708 | | {{{ |
709 | | #universe = docker is optional |
710 | | universe = docker |
711 | | |
712 | | # nom de l'image sur dockerHub |
713 | | docker_image = rootproject/root |
714 | | |
715 | | # programme à exécuter dans l'environnement ROOT avec ses arguments |
716 | | executable = /opt/root/bin/root.exe |
717 | | arguments = -b -q -l candlehisto.C |
718 | | |
719 | | # Fichier d'input à transférer sur le nœud où va tourner la tâche |
720 | | transfer_input_files = candlehisto.C |
721 | | |
722 | | # Fichier de résultat à transférer depuis le nœud ou a tourner la tâche en fin d’exécution |
723 | | transfer_output_files = output.png |
724 | | |
725 | | should_transfer_files = YES |
726 | | when_to_transfer_output = ON_EXIT |
727 | | |
728 | | # Fichier de logs |
729 | | output = out.$(ClusterId).$(ProcId) |
730 | | error = err.$(ClusterId).$(ProcId) |
731 | | log = log.$(ClusterId).$(ProcId) |
732 | | |
733 | | # Pré-requis pour la sélection du nœud local ou faire tourner la tâche |
734 | | # mémoire requise |
735 | | request_memory = 2000M |
736 | | # type de système requis |
737 | | +Requirements = OpSysAndVer =?= "AlmaLinux" |
738 | | # nom du nœud d’exécution de la ferme locale |
739 | | +Requirements = machine =?= "lpsc-c27.in2p3.fr" |
740 | | |
741 | | queue 1 |
742 | | |
743 | | }}} |
744 | | |
745 | | == Le concept de jobset == |
746 | | |
747 | | 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 [https://htcondor.readthedocs.io/en/23.0/users-manual/job-sets.html#submitting-a-job-set ici] |
748 | | |
749 | | La commande HTcondor diffère également, elle est de la forme suivante : {{{htcondor jobset submit jobs.set}}} |
750 | | |
751 | | === Soumission d'un ensemble de tâches OpenMPI === |
752 | | |
753 | | 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. |
754 | | |
755 | | **jobs.set** |
756 | | {{{ |
757 | | name = ExampleJobSet |
758 | | |
759 | | # 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) |
760 | | |
761 | | iterator = table machine { |
762 | | lpsc-c0.in2p3.fr |
763 | | lpsc-c8.in2p3.fr |
764 | | lpsc-c12.in2p3.fr |
765 | | lpsc-c13.in2p3.fr |
766 | | lpsc-c15.in2p3.fr |
767 | | lpsc-c16.in2p3.fr |
768 | | lpsc-c17.in2p3.fr |
769 | | lpsc-c18.in2p3.fr |
770 | | lpsc-c19.in2p3.fr |
771 | | lpsc-c20.in2p3.fr |
772 | | lpsc-c21.in2p3.fr |
773 | | lpsc-c22.in2p3.fr |
774 | | lpsc-c23.in2p3.fr |
775 | | lpsc-c24.in2p3.fr |
776 | | lpsc-c25.in2p3.fr |
777 | | lpsc-c26.in2p3.fr |
778 | | lpsc-c27.in2p3.fr |
779 | | } |
780 | | |
781 | | # définition de la tâche de base qui va être "itérée" |
782 | | |
783 | | job { |
784 | | |
785 | | universe = vanilla |
786 | | executable = run.sh |
787 | | arguments = -np 8 example |
788 | | |
789 | | output = example_$(machine).out |
790 | | error = example_$(machine).err |
791 | | log = example.log |
792 | | |
793 | | should_transfer_files = yes |
794 | | when_to_transfer_output = on_exit |
795 | | transfer_input_files = example |
796 | | |
797 | | # renommage du fichier de sortie en fonction du nom de la machine |
798 | | transfer_output_remaps = "example_$(machine).out=$(machine)/example_$(machine).out ; example_$(machine).err=$(machine)/example_$(machine).err" |
799 | | |
800 | | # Spécifiez le nombre de slots (processus MPI) requis |
801 | | request_cpus = 1 |
802 | | request_memory = 1024M |
803 | | request_disk = 10240K |
804 | | |
805 | | +Requirements = machine =?= "$(machine)" |
806 | | +WantParallelSchedulingGroups = True |
807 | | |
808 | | queue |
809 | | } |
810 | | |
811 | | }}} |
812 | | |
813 | | |
814 | | == Autre exemples == |
815 | | {{{ |
816 | | more testprog01.sh |
817 | | #! /bin/sh |
818 | | echo "HT-condor testprog01" |
819 | | echo "I'm process id $$ on" `hostname` |
820 | | echo "This is sent to standard error" 1>&2 |
821 | | date |
822 | | echo "Running as binary $0" "$@" |
823 | | echo "My name (argument 1) is $1" |
824 | | echo "My sleep duration (argument 2) is $2" |
825 | | sleep $2 |
826 | | echo "Sleep of $2 seconds finished. Exiting" |
827 | | exit 0 |
828 | | }}} |
829 | | |
830 | | ==== exemple d'un fichier de description de soumission: ==== |
831 | | {{{ |
832 | | more testprog01.submit |
833 | | executable=testprog01.sh |
834 | | universe=vanilla |
835 | | arguments=Example.$(Cluster).$(Process) 100 |
836 | | output=results.output.$(Process) |
837 | | error=results.error.$(Process) |
838 | | log=results.log |
839 | | notification=never |
840 | | should_transfer_files=YES |
841 | | when_to_transfer_output = ON_EXIT |
842 | | queue |
843 | | }}} |
844 | | |
845 | | ==== exemple d'un fichier de description de soumission en sélectionnant !AlmaLinux9 comme Operating System:==== |
846 | | {{{ |
847 | | more testprog2.submit |
848 | | executable=testprog01.sh |
849 | | universe=vanilla |
850 | | arguments=Example.$(Cluster).$(Process) 100 |
851 | | output=results.output.$(Process) |
852 | | error=results.error.$(Process) |
853 | | log=results.log |
854 | | notification=never |
855 | | should_transfer_files=YES |
856 | | when_to_transfer_output = ON_EXIT |
857 | | requirements = (OpSysAndVer =?= "AlmaLinux9") |
858 | | queue |
859 | | }}} |
860 | | |
861 | | Pour choisir Centos7: |
862 | | {{{ |
863 | | requirements = (OpSysAndVer =?= "CentOS7") |
864 | | }}} |
865 | | Pour choisir Fedora39: |
866 | | {{{ |
867 | | requirements = (OpSysAndVer =?= "Fedora39") |
868 | | }}} |
869 | | L'utilisateur définit lui-même le groupe auxquel il appartient via une instruction de type : |
870 | | {{{ |
871 | | accounting_group = informatique |
872 | | }}} |
873 | | |
874 | | |
875 | | |
876 | | \\ |
877 | | |
878 | | |