Tabla de Contenidos

Página visible a externos sin autenticación

Envío de trabajos al clúster con sbatch

Esta página explica cómo lanzar trabajos al clúster ANTS usando Slurm y una plantilla base de sbatch pensada para entrenamientos de modelos en GPU. La plantilla está optimizada para usar el scratch local del nodo durante la ejecución y persistir los resultados en tu home al terminar.

Conceptos previos

Antes de lanzar un trabajo conviene tener claro lo siguiente:

Estructura del directorio de trabajo

La plantilla espera que tu proyecto siga esta estructura en el directorio desde el que lances sbatch:

mi-proyecto/
├── code/                  # Código fuente (main.py y módulos)
│   └── main.py            # Punto de entrada del entrenamiento
├── data/                  # Dataset
├── lib/
│   └── install_env.sh     # Script que crea/activa el entorno (venv, módulos, CUDA)
├── artifacts/             # (opcional) checkpoints previos para reanudar
└── train.sbatch           # La plantilla de este documento

La plantilla

Guarda el siguiente contenido como train.sbatch en la raíz de tu proyecto.

train.sbatch
#!/usr/bin/env bash
#SBATCH --job-name=base-train          # Nombre identificador del trabajo en la cola
#SBATCH --partition=gpu                # Particion (cola) a usar — CPU o GPU
#SBATCH --nodes=1                      # Numero de nodos del cluster a reservar
#SBATCH --ntasks=1                     # Numero de tareas MPI (procesos paralelos)
#SBATCH --cpus-per-task=19             # Hilos de CPU por tarea
#SBATCH --gres=gpu:1                   # Recurso generico: 1 GPU H100 NVL o gpu:1 para cualquiera
#SBATCH --mem=31G                      # Memoria RAM total reservada en el nodo
#SBATCH --time=24:00:00                # Tiempo maximo de ejecucion (HH:MM:SS)
#SBATCH --output=/slurm/home/%u/output/%j/terminal.out  # stdout
#SBATCH --error=/slurm/home/%u/output/%j/terminal.err   # stderr
 
set -euo pipefail
 
OUTDIR="/slurm/home/$USER/output/$SLURM_JOB_ID"
mkdir -p "$OUTDIR"
 
SCRATCH="${SLURM_TMPDIR:-/scratch/slurm/$USER/$SLURM_JOB_ID/tmp}"
JOBSCRATCH="${SCRATCH}/base-${SLURM_JOB_ID}"
mkdir -p "$JOBSCRATCH"
 
SUBMIT_DIR="${SLURM_SUBMIT_DIR:-$PWD}"
 
cleanup() {
  rsync -a --ignore-missing-args \
    "$JOBSCRATCH/code/artifacts/" "$SUBMIT_DIR/artifacts/" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
 
rsync -a "$SUBMIT_DIR/code/" "$JOBSCRATCH/code/"
rsync -a "$SUBMIT_DIR/data/" "$JOBSCRATCH/data/"
 
if [[ -d "$SUBMIT_DIR/artifacts" ]]; then
  mkdir -p "$JOBSCRATCH/code/artifacts"
  rsync -a "$SUBMIT_DIR/artifacts/" "$JOBSCRATCH/code/artifacts/"
fi
 
rsync -a "$SUBMIT_DIR/lib/" "$JOBSCRATCH/lib/"
source "$JOBSCRATCH/lib/install_env.sh"
 
export ARTIFACTS_DIR="$JOBSCRATCH/code/artifacts"
export PERSISTENT_ARTIFACTS_DIR="/slurm/home/$USER/artifacts"
 
cd "$JOBSCRATCH/code"
python main.py 2>&1 | tee "$OUTDIR/training.log"

Explicación bloque a bloque

1. Directivas #SBATCH

Estas líneas configuran el trabajo antes de empezar a ejecutarse. Slurm las usa para reservar recursos y planificar el job en la cola.

Directiva Significado
#SBATCH –job-name=base-train Nombre que verás en squeue. Útil para identificar el trabajo.
#SBATCH –partition=gpu Partición (cola). Usa sinfo para ver las disponibles (típicamente cpu, gpu).
#SBATCH –nodes=1 Número de nodos físicos a reservar. Para un único proceso, 1.
#SBATCH –ntasks=1 Número de tareas MPI. Sin MPI deja 1.
#SBATCH –cpus-per-task=19 Hilos de CPU por tarea. Ajusta a los workers de tu DataLoader.
#SBATCH –gres=gpu:1 1 GPU H100 NVL. Para cualquier GPU: gpu:1. Para 2: gpu:2.
#SBATCH –mem=31G RAM total del nodo. Pide solo lo necesario para no bloquear a otros.
#SBATCH –time=24:00:00 Límite de tiempo en HH:MM:SS. Al superarlo, Slurm mata el job.
#SBATCH –output=…/%j/terminal.out Fichero para stdout. %u=usuario, %j=Job ID.
#SBATCH –error=…/%j/terminal.err Fichero para stderr.

Pide siempre el mínimo de recursos que necesites. Cuanto más pidas, más tardará Slurm en planificarte y peor fair-share tendrás frente a otros usuarios. Si no estás seguro, lanza pruebas cortas con –time=00:30:00.

2. Modo estricto de Bash

set -euo pipefail

Hace que el script aborte ante cualquier error: -e (sale si un comando falla), -u (error si usas una variable no definida), -o pipefail (un fallo dentro de un pipe propaga el código de error). Imprescindible para no continuar ejecutando si, por ejemplo, rsync del dataset falla.

3. Directorio de logs persistente

OUTDIR="/slurm/home/$USER/output/$SLURM_JOB_ID"
mkdir -p "$OUTDIR"

Crea un directorio por Job ID en tu home donde se guardarán los logs (terminal.out, terminal.err y training.log). Sobrevive al borrado del scratch.

4. Scratch del nodo

SCRATCH="${SLURM_TMPDIR:-/scratch/slurm/$USER/$SLURM_JOB_ID/tmp}"
JOBSCRATCH="${SCRATCH}/base-${SLURM_JOB_ID}"
mkdir -p "$JOBSCRATCH"

SLURM_TMPDIR lo define automáticamente Slurm cuando hay scratch local configurado; el ${VAR:-fallback} usa una ruta alternativa si no existe. Aquí trabajará el job: lectura/escritura rápidas porque suele ser NVMe local del nodo, no NFS.

5. Limpieza automática (trap)

cleanup() {
  rsync -a --ignore-missing-args \
    "$JOBSCRATCH/code/artifacts/" "$SUBMIT_DIR/artifacts/" 2>/dev/null || true
}
trap cleanup EXIT INT TERM

trap registra la función cleanup para que se ejecute siempre al terminar el script: tanto si finaliza con éxito (EXIT), si lo cancelas con Ctrl+C (INT), o si Slurm lo mata por tiempo (TERM). Así nunca pierdes los checkpoints del scratch.

Si Slurm mata el job con SIGKILL (señal 9, no capturable), el trap no se ejecuta. Esto ocurre típicamente al agotar el –time tras un periodo de gracia. Configura tu entrenamiento para guardar checkpoints periódicamente al PERSISTENT_ARTIFACTS_DIR como red de seguridad.

6. Copia de código, datos y artefactos al scratch

rsync -a "$SUBMIT_DIR/code/" "$JOBSCRATCH/code/"
rsync -a "$SUBMIT_DIR/data/" "$JOBSCRATCH/data/"
 
if [[ -d "$SUBMIT_DIR/artifacts" ]]; then
  mkdir -p "$JOBSCRATCH/code/artifacts"
  rsync -a "$SUBMIT_DIR/artifacts/" "$JOBSCRATCH/code/artifacts/"
fi

Trae el código y el dataset al scratch local antes de empezar a entrenar. Si existen artefactos previos (checkpoint de un entrenamiento anterior) los restaura, permitiendo reanudar sin volver a empezar.

7. Instalación del entorno

rsync -a "$SUBMIT_DIR/lib/" "$JOBSCRATCH/lib/"
source "$JOBSCRATCH/lib/install_env.sh"

install_env.sh es responsabilidad tuya: típicamente carga módulos (module load cuda/12.x), crea/activa un venv, e instala dependencias con pip. Debe ser idempotente.

Un comando o instrucción es idempotente si puedes ejecutarlo una o varias veces y el resultado final siempre será exactamente el mismo. No importa cuántas veces lo repitas, no causará efectos secundarios no deseados después de la primera ejecución ni depende del resultado de ejecuciones anteriores.

8. Variables para tu código

export ARTIFACTS_DIR="$JOBSCRATCH/code/artifacts"
export PERSISTENT_ARTIFACTS_DIR="/slurm/home/$USER/artifacts"

Tu main.py debe leerlas para decidir dónde escribir:

9. Lanzamiento

cd "$JOBSCRATCH/code"
python main.py 2>&1 | tee "$OUTDIR/training.log"

2>&1 fusiona stderr en stdout y tee escribe a la vez por pantalla (que va a terminal.out) y al log persistente en el home.

Cómo lanzar el trabajo

Desde la raíz de tu proyecto:

sbatch train.sbatch

Slurm devolverá algo como:

Submitted batch job 12345

Ese número es el Job ID ($SLURM_JOB_ID). Lo necesitarás para consultar el estado y leer los logs.

Personalizar la plantilla

Cosas que cambiarás casi siempre:

Variantes de GPU

Necesidad Directiva
Cualquier GPU disponible #SBATCH –gres=gpu:1
1 GPU H100 NVL específica #SBATCH –gres=gpu:nvidia_h100_nvl:1
2 GPUs del mismo nodo #SBATCH –gres=gpu:2
Sin GPU (partición CPU) Eliminar –gres y cambiar –partition=cpu

Lista las GPUs disponibles con:

sinfo -o "%P %N %G"

Pasar argumentos a main.py

Modifica la última línea:

python main.py --epochs 50 --batch-size 64 --lr 1e-4 2>&1 | tee "$OUTDIR/training.log"

O mejor, parametriza el sbatch aceptando variables de entorno:

python main.py \
  --epochs "${EPOCHS:-50}" \
  --batch-size "${BATCH_SIZE:-64}" \
  2>&1 | tee "$OUTDIR/training.log"

Y lánzalo así:

sbatch --export=ALL,EPOCHS=100,BATCH_SIZE=128 train.sbatch

Monitorización del trabajo

Ver la cola

squeue -u $USER                # Solo tus trabajos
squeue --me                    # Equivalente moderno
squeue -p gpu                  # Todos los trabajos de la particion gpu

Códigos de estado más habituales: R (running), PD (pending), CG (completing), F (failed).

Detalles de un trabajo

scontrol show job 12345

Seguir los logs en vivo

tail -f /slurm/home/$USER/output/12345/terminal.out
tail -f /slurm/home/$USER/output/12345/terminal.err

Estadísticas tras terminar

sacct -j 12345 --format=JobID,JobName,State,Elapsed,MaxRSS,ReqMem,ReqCPUS,AllocTRES%40

Útil para ajustar peticiones en el siguiente lanzamiento: si MaxRSS fue de 8G y pediste –mem=31G, estás desperdiciando memoria.

Cancelar un trabajo

scancel 12345              # Cancela un job concreto
scancel -u $USER           # Cancela TODOS tus jobs (uso con cuidado)
scancel -n base-train      # Cancela por nombre

Pruebas interactivas

Para depurar sin pasar por la cola, abre una sesión interactiva con los mismos recursos:

srun --partition=gpu --gres=gpu:1 --cpus-per-task=4 --mem=8G \
     --time=01:00:00 --pty bash

Te dará una shell dentro de un nodo del clúster. Cuando termines, exit libera los recursos.

Errores comunes

Síntoma Causa probable
Invalid partition specified El nombre de la partición no existe. Comprueba con sinfo.
Requested node configuration is not available Pides más recursos de los que tiene cualquier nodo (p. ej. 5 GPUs en un nodo de 4).
Job pasa horas en PD con razón Resources No hay nodos libres con tus requisitos; espera o reduce la petición.
Job pasa horas en PD con razón Priority Otros trabajos van por delante. Tu fair-share se restaura con el tiempo.
CUDA error: no CUDA-capable device Olvidaste –gres=gpu:… o estás en la partición cpu.
DUE TO TIME LIMIT Tu job superó –time. Aumenta el tiempo o guarda checkpoints.
oom-kill Te pasaste de –mem. Aumenta o reduce batch size.

Buenas prácticas

Variables útiles de Slurm

Disponibles dentro del script:

Variable Contenido
$SLURM_JOB_ID ID numérico del trabajo.
$SLURM_JOB_NAME Valor de –job-name.
$SLURM_SUBMIT_DIR Directorio desde el que se lanzó sbatch.
$SLURM_TMPDIR Scratch local del nodo (si está configurado).
$SLURM_CPUS_PER_TASK Valor de –cpus-per-task; útil para OMP_NUM_THREADS.
$SLURM_GPUS_ON_NODE Nº de GPUs asignadas al job en este nodo.
$SLURM_NTASKS Valor de –ntasks.
$SLURM_NODELIST Lista de nodos asignados.

Véase también