Resource Management
Exercises¶
- Configure a GPU GRES
Add a new GPU node gpu09 with 2 NVIDIA A100 GPUs to the cluster. Define the GRES in both slurm.conf and gres.conf. Use AutoDetect=nvml in gres.conf if gpu09 is a persistent node; show the explicit form you would need if it were a State=CLOUD node.
Hint / Solution
# In slurm.conf, add the node definition:
# NodeName=gpu09 CPUs=64 RealMemory=512000 Gres=gpu:a100:2
# In gres.conf (on gpu09 or globally):
# AutoDetect=nvml
# Or explicitly:
# Name=gpu Type=a100 File=/dev/nvidia0 Cores=0-31
# Name=gpu Type=a100 File=/dev/nvidia1 Cores=32-63
# Restart slurmctld (new node requires restart)
systemctl restart slurmctld
# Start slurmd on the new node
ssh gpu09 systemctl start slurmd
slurm-training-val (Slurm 25.11.8, ParallelCluster 3.16.1).
$ echo 'NodeName=batch-dy-compute-1 CPUs=8 RealMemory=31129 State=CLOUD Feature=dynamic,t3.2xlarge,compute Weight=1000 Gres=val_fpga:2'
NodeName=batch-dy-compute-1 CPUs=8 RealMemory=31129 State=CLOUD Feature=dynamic,t3.2xlarge,compute Weight=1000 Gres=val_fpga:2
$ echo 'NodeName=batch-dy-compute-1 Name=val_fpga Count=2'
NodeName=batch-dy-compute-1 Name=val_fpga Count=2
$ ssh batch-dy-compute-1 'sudo systemctl restart slurmd'
Warning: Permanently added 'batch-dy-compute-1' (ED25519) to the list of known hosts.
$ grep val_fpga /opt/slurm/etc/pcluster/slurm_parallelcluster_batch_partition.conf /opt/slurm/etc/pcluster/slurm_parallelcluster_batch_gres.conf
/opt/slurm/etc/pcluster/slurm_parallelcluster_batch_gres.conf:NodeName=batch-dy-compute-1 Name=val_fpga Count=2
$ scontrol show node batch-dy-compute-1 | grep -E '(NodeName|Gres|CfgTRES)'
NodeName=batch-dy-compute-1 Arch=x86_64 CoresPerSocket=1
Gres=(null)
CfgTRES=cpu=8,mem=31129M,billing=8
- Verify GRES with scontrol show node
Inspect a GPU node to confirm the GRES are correctly detected and available. Check both the configured GRES count and how many are currently allocated.
Hint / Solution
# Show full node details including GRES
scontrol show node gpu01
# Look for these fields in the output:
# Gres=gpu:a100:4
# CfgTRES=cpu=64,mem=512000M,billing=64,gres/gpu=4
# AllocTRES= (empty if no jobs running)
# GresUsed=gpu:a100:0(IDX:)
# Check all GPU nodes at once
scontrol show nodes gpu[01-08] | grep -E "NodeName|Gres|AllocTRES"
slurm-training-val (Slurm 25.11.8, ParallelCluster 3.16.1).
- Add a license resource
Add 20 Schrodinger Glide licenses as a cluster-wide resource. Submit a test job requesting one license and verify that Slurm tracks the allocation.
Hint / Solution
# In slurm.conf, add or update the Licenses line:
# Licenses=schrodinger_glide:20
scontrol reconfigure
# Verify the licenses are registered
scontrol show licenses
# Should show: LicenseName=schrodinger_glide Total=20 Used=0 Free=20
# Submit a test job requesting a license
sbatch --licenses=schrodinger_glide:1 --wrap="sleep 60"
# Check that the license is allocated
scontrol show licenses
# Used should now be 1, Free should be 19
slurm-training-val (Slurm 25.11.8, ParallelCluster 3.16.1).
- Test cgroup memory enforcement
Verify that ConstrainRAMSpace=yes is set in cgroup.conf. Submit a job that intentionally tries to allocate more memory than requested and confirm that Slurm kills it with an OOM exit code.
Hint / Solution
# Verify cgroup settings on a compute node
ssh cpu001 cat /etc/slurm/cgroup.conf | grep ConstrainRAMSpace
# Should show: ConstrainRAMSpace=yes
# Submit a job requesting 100 MB but trying to allocate 500 MB
sbatch --mem=100M --wrap="python3 -c \"x = bytearray(500 * 1024 * 1024); import time; time.sleep(60)\""
# Wait for it to be killed, then check
sacct -j <jobid> --format=JobID,State,ExitCode,MaxRSS
# State should be OUT_OF_MEMORY
# ExitCode shows 0:125 for a cgroup OOM kill (see the validated output below);
# a program inside the script killed by SIGKILL shows 137:0 instead
slurm-training-val (Slurm 25.11.8, ParallelCluster 3.16.1).
$ cat /opt/slurm/etc/cgroup.conf 2>/dev/null || echo '(cgroup.conf not at /opt/slurm/etc/)'
###
# Slurm cgroup support configuration file
###
ConstrainCores=yes
#
# WARNING!!! The slurm_parallelcluster_cgroup.conf file included below can be updated by the pcluster process.
# Please do not edit it.
include slurm_parallelcluster_cgroup.conf
$ sbatch --mem=100M --time=00:05:00 --output=/dev/null --job-name=val_oom --wrap='/shared/python/3.12/bin/python3 -c "x = bytearray(500 * 1024 * 1024); import time; time.sleep(60)"'
Submitted batch job 42
$ sacct -j 42 --format=JobID,JobName%-12,State%-20,ExitCode,MaxRSS
JobID JobName State ExitCode MaxRSS
------------ ------------ -------------------- -------- ----------
42 val_oom OUT_OF_MEMORY 0:125
42.batch batch OUT_OF_MEMORY 0:125 102196K
- Verify node features and test constraint matching
Check which features are configured on the GPU nodes. Submit a job that requires the a100 feature using --constraint and verify it lands on an A100 node.
Hint / Solution
# Check features on GPU nodes
scontrol show nodes gpu[01-08] | grep -E "NodeName|AvailableFeatures|ActiveFeatures"
# Submit a job requiring a100 feature
sbatch --constraint=a100 --gres=gpu:1 --wrap="nvidia-smi; hostname" -o feature_test.out
# After completion, check which node it ran on
sacct -j <jobid> --format=JobID,NodeList,State
# Should show one of the A100 GPU nodes
slurm-training-val (Slurm 25.11.8, ParallelCluster 3.16.1).
$ scontrol show nodes | grep -E '(NodeName|AvailableFeatures)' | head -20
NodeName=batch-dy-compute-1 Arch=x86_64 CoresPerSocket=1
AvailableFeatures=dynamic,t3.2xlarge,compute
NodeName=batch-dy-compute-2 CoresPerSocket=1
AvailableFeatures=dynamic,t3.2xlarge,compute
NodeName=batch-dy-compute-3 CoresPerSocket=1
AvailableFeatures=dynamic,t3.2xlarge,compute
NodeName=batch-dy-compute-4 CoresPerSocket=1
AvailableFeatures=dynamic,t3.2xlarge,compute
NodeName=debug-dy-small-1 CoresPerSocket=1
AvailableFeatures=dynamic,t3.medium,small
NodeName=debug-dy-small-2 CoresPerSocket=1
AvailableFeatures=dynamic,t3.medium,small
NodeName=gpu-dy-t4-1 CoresPerSocket=1
AvailableFeatures=dynamic,g4dn.xlarge,t4,gpu
NodeName=highmem-dy-large-1 CoresPerSocket=1
AvailableFeatures=dynamic,t3.2xlarge,large
- Classify a cluster as enforcing or non-enforcing
Read the five signature settings from the running config and decide whether memory and CPU limits are enforced.
Hint / Solution
scontrol show config | grep -E 'SelectTypeParameters|DefMemPerNode|TaskPlugin|ProctrackType|JobAcctGatherType'
# Not enforcing: CR_CPU, DefMemPerNode=UNLIMITED, task/affinity only,
# proctrack/linuxproc, jobacct_gather/none
# Enforcing: CR_*_Memory, task/cgroup,task/affinity, proctrack/cgroup,
# jobacct_gather/cgroup (and ConstrainRAMSpace=yes in cgroup.conf)
# Mixed output is still "not enforcing" for whichever layer is missing.
slurm-training-val (Slurm 25.11.8, ParallelCluster 3.16.1).
$ scontrol show config | grep -E 'SelectTypeParameters|DefMemPerNode|TaskPlugin|ProctrackType|JobAcctGatherType'
DefMemPerNode = UNLIMITED
JobAcctGatherType = jobacct_gather/cgroup
ProctrackType = proctrack/cgroup
SelectTypeParameters = CR_CPU_MEMORY
TaskPlugin = task/cgroup,task/affinity
TaskPluginParam = (null type)
- Compare
nprocwith the pinned CPU set
Inside a 2-CPU step, compare the CPU count the OS reports with the width of Cpus_allowed_list. Explain which one os.cpu_count() follows.
Hint / Solution
srun -c 2 bash -c 'nproc; grep Cpus_allowed_list /proc/self/status; echo "SLURM_CPUS_ON_NODE=$SLURM_CPUS_ON_NODE"'
# nproc honours the affinity mask (glibc sched_getaffinity), so it should equal the
# width of Cpus_allowed_list (e.g. 0-1 -> 2). Python os.cpu_count() and R
# parallel::detectCores() ignore the mask and report the whole node.
srun -c 2 python3 -c 'import os; print(os.cpu_count(), len(os.sched_getaffinity(0)))'
slurm-training-val (Slurm 25.11.8, ParallelCluster 3.16.1).
$ srun -J val_rm_e7 --chdir=/tmp -c 2 bash -c 'nproc; grep Cpus_allowed_list /proc/self/status; echo SLURM_CPUS_ON_NODE=$SLURM_CPUS_ON_NODE'
2
Cpus_allowed_list: 0,42
SLURM_CPUS_ON_NODE=2
$ srun -J val_rm_e7 --chdir=/tmp -c 2 python3 -c 'import os; print(os.cpu_count(), len(os.sched_getaffinity(0)))'
8 2
- Make ParallelCluster track
gres/gpu
Enable accounting on a stock ParallelCluster, add gres/gpu to AccountingStorageTRES, apply the change, and show that a GPU job's AllocTRES now contains the GPU.
Hint / Solution
# cluster YAML (HeadNode and external MySQL/RDS details omitted)
# Scheduling:
# SlurmSettings:
# Database:
# Uri: <rds-endpoint>:3306
# UserName: <db-user>
# PasswordSecretArn: <secret-arn>
# CustomSlurmSettings:
# - AccountingStorageTRES: gres/gpu
pcluster update-cluster --cluster-name <name> --cluster-configuration cluster.yaml
# After the update completes, on the head node:
sacctmgr show tres # gres/gpu must be listed
sbatch --gres=gpu:1 --wrap="nvidia-smi -L; sleep 5"
sacct -j <jobid> --format=JobID,ReqTRES%40,AllocTRES%60 # AllocTRES contains gres/gpu=1
# Tracking starts at the change; earlier jobs stay without gres/gpu.
# gres/gpumem and gres/gpuutil stay empty on cloud nodes (no NVML data source).
slurm-training-val (Slurm 25.11.8, ParallelCluster 3.16.1).