Poás es un clúster de computadoras experimental. A la fecha cuenta con 7 nodos esclavos, cada uno con un procesador de 4 núcleos de 3.40GHz, para un total de 28 núcleos. En el futuro se podrían agregar más computadoras.
Nota: La siguiente guía está ideada para sistemas basados en Unix como Linux o macOS. Si se encuentra en un sistema Windows, puede usar el cliente de SSH PuTTY.
Si no se encuentra conectado(a) físicamente a la red UCR, conéctese primero a la VPN de la ECCI.
Una vez conectado a la red UCR o a la VPN de la ECCI, abra una terminal en su sistema basado en Unix y ejecute el siguiente comando:
$ ssh -Y usuario@10.84.19.15
usuario: debe reemplazarlo por su carnet, con la letra inicial en MAYÚSCULA (ejemplo: B69900
).
-Y
: La opción -Y
es útil si ejecuta en el clúster comandos que generan salida gráfica, de tal forma que las pueda ver en su computadora local. Por ejemplo, si corre un programa en Python que genera un gráfico de barras. Si no va a correr comandos de esta naturaleza, puede omitir el argumento -Y
.
Ingrese sus credenciales. Si es la primera vez que ingresa al clúster:
passwd
, que le pedirá la contraseña actual y luego la nueva.Una vez que haya ingresado, se encontrará en la máquina frontal o máster. Esta máquina está diseñada para preparar trabajos, pero no para correrlos.
Una vez que haya ingresado al clúster, querrá colocar trabajos a ejecutar. Es poco probable que escriba programas o prepare datos desde la terminal en el clúster. En su lugar es probable que ya disponga de los programas en un repositorio de control de versiones, y los datos en archivos en su máquina local. Si tiene un repositorio de Git, puede crear un clon en el clúster:
$ git clone repo-url
Para transferir archivos o directorios entre su computador local y el clúster, puede usar el comando scp
(security copy) en Linux/macOS, y WinSCP en Windows. Desde otra terminal en su máquina local puede copiar un archivo de esta forma:
$ scp archivo_origen usuario@10.84.19.15:~/ruta_destino
Si necesita transferir directorios recursivamente, agregue la opción -r
:
$ scp -r directorio_origen usuario@10.84.19.15:~/ruta_destino
Los siguientes son tres ejemplos de una persona con carnet A99999 que transfiere un archivo de datos y una carpeta de proyecto desde su computadora local al clúster. Luego copia de regreso un archivo comprimido de resultados desde el clúster a su computadora local:
# Copiar el archivo local datos.zip al clúster en su /home/A99999:
$ scp datos.zip A99999@10.84.19.15:
# Copiar el directorio proy01/ al clúster en /home/A99999/curso/proy01:
$ scp -r proy01 A99999@10.84.19.15:curso
# Copiar un archivo remoto result.7z desde el clúster a su computador local:
$ scp A99999@10.84.19.15:curso/proy01/result.7z .
Es muy probable que mientras ejecuta programas en el clúster, necesite modificar archivos. Por ninguna razón debe usar su repositorio de control de versiones para hacer los cambios en su máquina local, enviarlos al servidor de alojamiento de Git (git push
) y halarlos desde clúster (git pull
). Eso creará un ciclo de prueba-y-error lento y contaminará su historial de control de versiones.
A través de una terminal puede modificar archivos con editores de línea de comandos como vi
o nano
. Pero también puede utilizar un ambiente integrado de desarrollo (IDE). Por ejemplo, Visual Studio Code dispone de la extensión Remote - SSH.
Una vez instalada en su máquina local, presione F1, busque por "RemoteSSH: Connect to host...", escriba su nombre de usuario seguido por el IP del clúster: usuario@10.84.19.15
. Es probable que se abra una nueva ventana de VSCode donde ingrese su contraseña. VSCode le indicará cuando se establezca la conexión en la esquina inferior izquierda (con fondo verde). A partir de este momento, podrá abrir la carpeta del proyecto en el clúster. Note que los archivos que está editando en esta ventana estarán en el clúster. Cada vez que guarde cambios, estos modificarán sus archivos en el clúster. Puede dentro de VSCode abrir una terminal y correr comandos en el clúster. También puede usar el control de versiones con VSCode y emitir commits desde el clúster.
El clúster Poas cuenta con varios ejemplos de programas en MPI y OpenMP ubicados en la siguiente ruta:
/opt/ohpc/pub/examples/ejemplosECCI/
Estos ejemplos proveen archivos que puede aprovechar para crear sus programas y preparar sus ejecuciones.
Es común que un clúster sea usado por un grupo de personas con necesidades diversas. Por ejemplo, unas personas pueden necesitar Python v3 mientras que otras podrían tener programas heredados con Python v2. Ambas esperan que al correr el comando python
aparezca la versión que necesitan. Para hacer coexistir configuraciones incompatibles, se usan módulos de ambiente. Un modulos de ambiente simplemente es una configuración del sistema que habilita ciertos comandos, compiladores o bibliotecas.
Para obtener la lista de módulos de ambiente disponibles en el clúster use el comando module avail
. Obtendrá una lista de módulos agrupadas por proveedores. Los módulos con (L)
están ya cargados:
$ module avail
...
mpich/3.4.3-ofi (L)
openmpi4/4.1.4
valgrind/3.19.0
...
En Poás de manera predeterminada, se cargan los módulos para GNU GCC v12 y MPICH, entre otros. Para consultar la lista de de módulos que tiene actualmente cargados (activados) use module list
.
$ module list
1) autotools 3) gnu12/12.2.0 5) mpich/3.4.3-ofi
2) prun/2.2 4) libfabric/1.13.0 6) ohpc
Para cargar (activar) un módulo use module load nombre_modulo
. Para desactivar un módulo use module unload nombre_modulo
. El siguiente ejemplo muestra como cambiar de la implementación MPICH a Open-MPI.
# Mostrar la versión actual de MPICH
$ mpiexec --version
Version: 3.4.3
# Desactivar MPICH
$ module unload mpich/3.4.3-ofi
# Ya no hay un ambiente de MPI activo
$ mpiexec --version
mpiexec: command not found...
# Activar la implementación de Open-MPI
$ module load openmpi4/4.1.4
$ mpiexec --version
mpiexec (OpenRTE) 4.1.4
Supóngase que usted envió un trabajo importante que está corriendo en el clúster. De pronto, otras personas envían más trabajos que afectan el suyo, en especial el tiempo de respuesta de las máquinas. Supóngase que más adelante usted necesita enviar otro trabajo pesado, ¿cómo puede saber si en los nodos esclavos están corriendo tareas pesadas que usted no quiere afectar? ¿Cómo saber cuándo han terminado los trabajos de otras personas.
Para responder preguntas como estas, facilitar el envío de trabajos, y el uso democrático del clúster, se usan colas de trabajo. El clúster Poas utiliza SLURM como sistema de gestión de colas para ejecutar trabajos.
Cuando usted ingresa al clúster, lo hace en la máquina frontal o máster. Esta computadora está diseñada para preparar trabajos (compilar programas, ajustar archivos de datos...), pero no correrlos. Sus programas deben correr en los nodos esclavos. Sin embargo, el clúster no debería permitir que usted ingrese a los nodos esclavos y correr programas ahí directamente. El sistema de colas lo hará por usted.
Cuando usted envía un trabajo para ejecución, el sistema de colas lo pondrá al final de una cola de trabajos pendientes. El sistema de colas monitorea el clúster. Cuando suficientes recursos queden libres, el sistema de colas correrá su programa, asegurándole que no será interferido por ejecuciones de otras personas. Cuando su programa termine de ejecutarse, el sistema de colas le podría enviar un correo electrónico. Usted ingresa a la máquina frontal donde podrá consultar los resultados que su programa haya dejado en archivos.
Los clúster normalmente ofrecen varias colas, con diferentes capacidades escogidas por las personas que crearon el clúster. SLURM llama a estas colas particiones, porque son subconjuntos de los recursos disponibles del clúster. Cuando usted pone un programa a correr, debe escoger la cola en que correrá.
El cluster Poás tiene 7 nodos de trabajo (computadoras esclavas), cada una con 1 procesador (socket) de 4 núcleos (CPU cores). Cada nodo tiene 16GB de memoria principal (RAM). Poás dispone de dos colas que particionan estos recursos:
La particion debug
es para trabajos de prueba y solo consta de dos nodos (compute-7
y compute-8
). Impone un tiempo máximo de ejecución de 30 minutos. Sirve para hacer pruebas con datos pequeños, antes de enviar su trabajo real a la cola normal.
La particion normal
es para trabajar con la totalidad de nodos y con un tiempo limite de 1 hora.
Si indica en un trabajo más tiempo del que permite una particion fallará al tratar de ponerlo en cola.
Para consultar las particiones (colas) disponibles del clúster use el comando scontrol show partition
:
$ scontrol show partition
PartitionName=debug
[...] MaxTime=00:30:00 Nodes=compute-[7-8]
TRES=cpu=8,mem=31780M,node=2,billing=8 [...]
PartitionName=normal
[...] MaxTime=01:00:00 Nodes=compute-[1-4,6-8]
TRES=cpu=28,mem=111230M,node=7,billing=28
Para correr un programa en los nodos esclavos, es necesario indicarle a SLURM los requerimientos de recursos que usted tenga, de tal forma que SLURM monitoree el clúster y cuando estos recursos se liberen, pueda correr su programa. Para indicar estas restricciones, prepare un archivo de texto para el intérprete de comandos con extensión .sh
. Las restricciones se indican en comentarios de la forma #SBATCH args
.
El siguiente es un ejemplo para correr el comando hostname
en cada uno de los nodos esclavos del clúster. El archivo se le llamará hostname.sh
.
#!/bin/bash
#SBATCH --overcommit # Allow more processes than CPUs per node
#SBATCH -J hostname # Job name
#SBATCH -o job.%j.txt # Stdout output file (%j expands to jobId)
#SBATCH -N 7 # Total number of nodes requested
#SBATCH -n 7 # Total number of processes requested
#SBATCH -t 00:01:00 # Run time (hh:mm:ss) - 1 min
#SBATCH -p normal # Partition to use (default, normal, debug)
prun hostname
Explicación de las opciones:
Por defecto, SLURM no correrá más procesos que CPUs disponibles en un nodo esclavo. La opción --overcommit
(abreviada -O
), permite correr más procesos que CPUs. L
La opción -J
da un nombre al trabajo, que le ayudará a identificarlo entre otros trabajos que haya puesto en cola.
Los datos que su programa envíe a la salida estándar serán capturados por SLURM y los colocará en un archivo de texto. La opción -o
indica el nombre que tendrá este archivo, donde %j
indica el número de trabajo en cola. Por ejemplo, si su trabajo es el 770, la salida que su programa produzca será enviada al archivo job.770.txt
.
La opción -N
indica la cantidad de nodos (máquinas esclavas) que querrá usar del clúster. Debe ser un número entre 1 y 7. Si escribe un número mayor, el trabajo será rechazado al tratar de ponerlo en cola.
La opción -n
indica la cantidad de procesos que quiere correr en los nodos solicitados con la opción anterior. Este número deberá ser mayor o igual que la cantidad de nodos, de tal forma, que cada nodo tenga al menos un proceso que correr. Si el número de procesos es mayor que el número de nodos, SLURM los repartirá usando un mapeo estático por bloque.
La opción -t
indica el tiempo máximo permitido a su trabajo para correr. Si su trabajo no ha finalizado en este tiempo, será detenido por SLURM. En la cola debug
este tiempo no puede exceder la media hora, y en la cola normal
este tiempo no puede exceder la hora.
La opción -p
indica la cola (partición) en que quiere colocar el trabajo.
Después de escribir en comentarios los requerimientos anteriores para el sistema de colas, escriba los comandos que deberán ejecutarse en los nodos esclavos. Aquí llamará a su programa antecedido por prun
, el cual se encarga de correrlo entre los nodos esclavos. Es un comando provisto por la tecnología OpenHPC. Si invocara a su programa sin el prun
, éste correrá en la máquina frontal, lo cual es contraproducente.
Para correr un trabajo en los nodos esclavos, invoque a sbatch
seguido del nombre del archivo de trabajo (archivo .sh
). SLURM le responderá con el número de trabajo (job id) si lo logró colocar en cola, o un mensaje de error en caso contrario.
$ sbatch hostname.sh
Submitted batch job 598
$ squeue
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
365 debug test bryan.ul PD 0:22 2 (PartitionTimeLimit)
582 debug test A99999 PD 0:13 1 (PartitionTimeLimit)
598 normal test jeisson. PD 0:00 7 (PartitionTimeLimit)
$ squeue
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
365 debug test bryan.ul PD 0:23 2 (PartitionTimeLimit)
582 debug test A99999 PD 0:14 1 (PartitionTimeLimit)
El comando squeue
le permitirá ver los trabajos actualmente en cola, sea que se estén ejecutando o a la espera de ser ejecutados. Mientras un trabajo esté en cola puede obtener más información con el comando scontrol show job id_trabajo
. Si su trabajo dura en ejecutarse o en espera por procesamiento, usted puede cerrar su sesión con el clúster. El trabajo no se detenderá, sino que seguirá en cola hasta finalizar su ejecución.
Una vez que su trabajo ha finalizado, desaparecerá de la cola. En este momento usted puede estudiar el resultado de la ejecución del programa. La salida que su trabajo genere en todos los nodos es enviada al archivo job.jobId.txt
tal como se especificó en el archivo de trabajo con la opción #SBATCH -o
. El siguiente comando muestra la salida estándar que generó la ejecución de hotname
en todos los nodos esclavos de Poás:
$ cat job.598.txt
[prun] Master compute host = compute-1
[prun] Resource manager = slurm
[prun] Launch cmd = mpiexec.hydra -bootstrap slurm hostname (family=mpich)
compute-1
compute-8
compute-6
compute-7
compute-4
compute-2
compute-3
Al inicio de la salida aparecen algunos mensajes generados por [prun]
, mientras distribuía su trabajo entre los nodos con mpiexec
. Después viene la salida de su programa con el característico indeterminismo de la concurrencia. Tome en cuenta que esta salida es la concatenación de lo que escribieron en la salida estádar todos los procesos de su programa aunque hayan corrido en diferentes máquinas. En esta salida de ejemplo se ve que cada uno de los 7 nodos esclavos indicaron su nombre correctamente.
MPI (Message Passing Interface) es una tecnología propuesta por la academia para facilitar la ejecución de programas en clústers de computadoras. El siguiente es un ejemplo en C donde cada proceso saluda en la salida estándar, indicando su número de proceso (rank), la cantidad de procesos en el equipo (conocido en MPI como world size), y el nombre de la máquina donde corre el proceso. Este archivo se llamará hello.c
:
// Copyright 2024 Jeisson Hidalgo ECCI-UCR CC-BY 4.0
#include <mpi.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
if (MPI_Init(&argc, &argv) == MPI_SUCCESS) {
int process_number = -1;
MPI_Comm_rank(MPI_COMM_WORLD, &process_number);
int process_count = -1;
MPI_Comm_size(MPI_COMM_WORLD, &process_count);
char process_hostname[MPI_MAX_PROCESSOR_NAME] = { '\0' };
int hostname_length = -1;
MPI_Get_processor_name(process_hostname, &hostname_length);
printf("Hello from main thread of process %d of %d on %s\n",
process_number, process_count, process_hostname);
MPI_Finalize();
} else {
perror("error: could not init MPI");
}
return 0;
}
El programa se debe compilar habilitando la biblioteca MPI. Para esto se usa el compiler wrapper mpicc
:
$ mpicc -g -Wall -Wextra hello.c -o hello
Luego se crea un archivo de trabajo (job file). El siguiente solicita correr 10 procesos en tres nodos esclavos.
#!/bin/bash
#SBATCH --overcommit # Allow more processes than CPUs per node
#SBATCH -J hostname # Job name
#SBATCH -o job.%j.txt # Stdout output file (%j expands to jobId)
#SBATCH -N 3 # Total number of nodes requested
#SBATCH -n 10 # Total number of processes requested
#SBATCH -t 00:01:00 # Run time (hh:mm:ss) - 1 min
#SBATCH -p normal # Partition to use (default, normal, debug)
prun ./hello
Correr el trabajo en los nodos secundarios, y analizar su resultado. Como puede verse, prun
distribuye los procesos entre los nodos usando un mapeo estático por bloque.
$ sbatch helloc.sh
Submitted batch job 650
$ cat job.650.txt
[prun] Master compute host = compute-6
[prun] Resource manager = slurm
[prun] Launch cmd = mpiexec.hydra -bootstrap slurm ./hello (family=mpich)
Hello from main thread of process 8 of 10 on compute-8
Hello from main thread of process 9 of 10 on compute-8
Hello from main thread of process 0 of 10 on compute-6
Hello from main thread of process 7 of 10 on compute-8
Hello from main thread of process 5 of 10 on compute-7
Hello from main thread of process 1 of 10 on compute-6
Hello from main thread of process 3 of 10 on compute-6
Hello from main thread of process 4 of 10 on compute-7
Hello from main thread of process 2 of 10 on compute-6
Hello from main thread of process 6 of 10 on compute-7
$ cp /opt/ohpc/pub/examples/ejemplosECCI/MPI/cpi.c . # copia el archivo cpi.c a su directorio actual
$ mpicc cpi.c -o cpi # compila el archivo cpi.c
$ sbatch batch-job-example.mpi # envía el trabajo a la cola de ejecución
Archivo de batch de ejemplo (batch-job-example.mpi)
#!/bin/bash
#SBATCH --overcommit # Ejecuta más trabajos de los definidos en los nodos SLURM
#SBATCH -J test # Nombre del trabajo
#SBATCH -o jobtest.%j.out # Nombre del archivo de salida (%j expande al jobId)
#SBATCH -N 2 # Número total de nodos solicitados
#SBATCH -n 2 # Número total de tareas MPI solicitadas (número de núcleos)
#SBATCH -t 00:10:00 # Tiempo de ejecución (hh:mm:ss) - 10 minutos
#SBATCH -p normal # Partición a utilizar: debug o normal
# Ejecutar el programa MPI compilado
prun ./cpi # aqui va el programa que quiere correr compilado en su formato distribuido.
El archivo jobtest.%j.out
muestra información sobre la ejecución de un trabajo:
[admin@poas test-batch]$ cat jobtest.343.out
[prun] Master compute host = compute-6
[prun] Resource manager = slurm
[prun] Launch cmd = mpiexec.hydra -bootstrap slurm ./cpi (family=mpich)
Process 0 of 2 is on compute-6
Process 1 of 2 is on compute-8
pi is approximately 3.1415926544231318, Error is 0.0000000008333387
wall clock time = 0.134944
$ sinfo
$ sbatch archivo.sh
$ scancel id_trabajo
$ scontrol show job id_trabajo
$ scontrol show job id_trabajo
$ scontrol show job id_trabajo -dd -o
$ scontrol show node
El disco duro del clúster es limitado (900 GB compartidos). Se recomienda eliminar archivos temporales o no utilizados para evitar saturar el almacenamiento.
$ du -sh
Este documento está ahora estructurado con secciones claras, lo que facilita encontrar la información relevante sobre conexión, transferencia de archivos, y ejecución de trabajos en el clúster.