Página visible a externos sin autenticación
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.
Antes de lanzar un trabajo conviene tener claro lo siguiente:
#SBATCH son directivas que se leen antes de ejecutar nada y configuran el trabajo (partición, recursos, tiempo, etc.).$SLURM_TMPDIR o /scratch/…) es almacenamiento rápido pero temporal: se borra al terminar el job. El home (/slurm/home/$USER) es persistente pero más lento.
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
Guarda el siguiente contenido como train.sbatch en la raíz de tu proyecto.
#!/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"
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.
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.
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.
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.
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.
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.
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.
export ARTIFACTS_DIR="$JOBSCRATCH/code/artifacts" export PERSISTENT_ARTIFACTS_DIR="/slurm/home/$USER/artifacts"
Tu main.py debe leerlas para decidir dónde escribir:
ARTIFACTS_DIR: checkpoints frecuentes durante el entrenamiento (scratch, rápido).PERSISTENT_ARTIFACTS_DIR: el checkpoint final o copias periódicas de seguridad (home, persistente).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.
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.
Cosas que cambiarás casi siempre:
–job-name: pon un nombre descriptivo (resnet50-imagenet, llama-finetune-v3…).–cpus-per-task, –mem, –time: ajusta a tus necesidades reales.–gres: ver tabla siguiente.main.py: añade los argumentos que necesites después.| 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"
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
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).
scontrol show job 12345
tail -f /slurm/home/$USER/output/12345/terminal.out tail -f /slurm/home/$USER/output/12345/terminal.err
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.
scancel 12345 # Cancela un job concreto scancel -u $USER # Cancela TODOS tus jobs (uso con cuidado) scancel -n base-train # Cancela por nombre
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.
| 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. |
PERSISTENT_ARTIFACTS_DIR, no solo al final.–time=00:15:00) antes de uno de 24 horas.sbatch junto al código en Git para reproducibilidad.sbatch o srun.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. |