From ac329336e8d86845f0ab81bde00e3a8a5e56caaa Mon Sep 17 00:00:00 2001 From: Mateo Langston Smith Date: Wed, 14 Oct 2026 11:52:49 -0500 Subject: [PATCH 01/10] Add GPU for testing --- ood/rstudio/form.yml.erb | 8 +++++++- ood/rstudio/template/script.sh.erb | 23 ++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/ood/rstudio/form.yml.erb b/ood/rstudio/form.yml.erb index e8e1d62b..e30f66a6 100644 --- a/ood/rstudio/form.yml.erb +++ b/ood/rstudio/form.yml.erb @@ -5,6 +5,9 @@ require 'open3' require_relative "CheckQueues" require_relative "CheckAccounts" +# testing: GPU-enabled custom container (bypasses the R module) +gpu_tester = ["mlangstonsmith"].include?(User.new.name) + -%> --- @@ -65,6 +68,9 @@ attributes: <%- if CheckAccounts.Accounts.any? { |word| word.include?("kbnk") } | CheckAccounts.Accounts.any? { |word| word.include?("jlagrone") }-%> - [ "R 4.5.1 (custom kbnk)", "kbnk/R/4.5.1" ] <%- end -%> + <%- if gpu_tester -%> + - [ "GPU test container (rocker-ml-cuda)", "custom" ] + <%- end -%> environment: widget: text_area label: "Custom Environment Settings" @@ -80,7 +86,7 @@ attributes: [SLURM partition]: https://southernmethodistuniversity.github.io/hpc_docs/about/queues.html#m3-queues options: <%- CheckQueues.Queues.each do |q| -%> - <%- if q[4].to_i == 0 -%> #disable gpu queues + <%- if q[4].to_i == 0 || gpu_tester -%> #disable gpu queues (except for testers) - ["<%= q[0] %>", "<%= q[0] %>", data-max-bc-num-hours: "<%= q[1] %>", data-max-bc-num-cores: "<%= q[2] %>", diff --git a/ood/rstudio/template/script.sh.erb b/ood/rstudio/template/script.sh.erb index 7606a8f7..4e1df900 100755 --- a/ood/rstudio/template/script.sh.erb +++ b/ood/rstudio/template/script.sh.erb @@ -12,6 +12,17 @@ env <%= context.environment.encode(universal_newline: true) %> <%- end -%> +<%- if context.r_module == "custom" -%> +# testing: GPU-enabled custom container, set what the R module would +module load apptainer +export CONTAINER_IMAGE=/lustre/scratch/client/users/mlangstonsmith/rocker-ml-cuda.sif +export CONTAINER_RSERVER=/usr/lib/rstudio-server/bin/rserver +export CONTAINER_RSESSION=/usr/lib/rstudio-server/bin/rsession +export CONTAINER_R=/usr/bin/R +export R_LIBS_USER=${HOME}/R/rocker-ml-cuda +mkdir -p ${R_LIBS_USER} +unset XDG_RUNTIME_DIR +<%- else -%> # load the requested module FORM_MODULE=<%= context.r_module %> EXTENSION="${FORM_MODULE##*.}" @@ -27,6 +38,7 @@ then else module load ${FORM_MODULE} fi +<%- end -%> # # Start RStudio Server @@ -53,6 +65,15 @@ sed 's/^ \{2\}//' > "${RSESSION_WRAPPER_FILE}" << EOL # make sure the container libR is visible inside container export LD_LIBRARY_PATH="/usr/local/lib/R/lib${LD_LIBRARY_PATH:+":$LD_LIBRARY_PATH"}" +<%- if context.r_module == "custom" -%> + + # environment.txt and the line above replace the container's paths, so + # restore libR (apt-installed in this image), the host driver libs bound + # by --nv, and the image's python venv (CUDA comes from pip wheels there) + export LD_LIBRARY_PATH="/usr/lib/R/lib:/.singularity.d/libs:\${LD_LIBRARY_PATH}" + export PATH="/opt/venv/bin:\${PATH}:/usr/lib/rstudio-server/bin/quarto/bin" + export CUDA_MODULE_LOADING="\${CUDA_MODULE_LOADING:-LAZY}" +<%- end -%> # Log all output from this script export RSESSION_LOG_FILE="${PWD}/rsession.log" @@ -195,7 +216,7 @@ unset APPTAINER_BIND export APPTAINERENV_XDG_CONFIG_DIRS="/etc/" export APPTAINERENV_RSTUDIO_CONFIG_DIRS=$RSTUDIO_CONFIG_DIRS -apptainer exec \ +apptainer exec <%= context.bc_num_gpus.to_i > 0 ? "--nv" : "" %> \ -B ${TMPDIR}:/tmp \ -B ${TMPDIR}/run:/run \ -B ${TMPDIR}/var/lib:/var/lib/rstudio-server \ From b9fba9e8756c004cc5b42cafd176b484f3de0cff Mon Sep 17 00:00:00 2001 From: Mateo Langston Smith Date: Wed, 14 Oct 2026 15:13:44 -0500 Subject: [PATCH 02/10] Add build script for testing --- containers/rocker_ml_cuda/README.md | 45 ++++++++ containers/rocker_ml_cuda/build_container.sh | 109 +++++++++++++++++++ containers/rocker_ml_cuda/rocker_ml_cuda.def | 22 ++++ 3 files changed, 176 insertions(+) create mode 100644 containers/rocker_ml_cuda/README.md create mode 100755 containers/rocker_ml_cuda/build_container.sh create mode 100644 containers/rocker_ml_cuda/rocker_ml_cuda.def diff --git a/containers/rocker_ml_cuda/README.md b/containers/rocker_ml_cuda/README.md new file mode 100644 index 00000000..9ef36b5b --- /dev/null +++ b/containers/rocker_ml_cuda/README.md @@ -0,0 +1,45 @@ +# rocker_ml_cuda + +GPU-enabled RStudio container: `rocker/ml` plus CUDA PyTorch (used from R +through reticulate), `tabpfn`, and tidymodels packages. Currently in testing +for the OOD RStudio app. + +## Pins + +- **Base image:** `rocker/ml` is pinned by digest in `rocker_ml_cuda.def` + (it was `py3.14` / `latest` as of 2026-10-04). `rocker/ml` no longer + publishes R version tags, so a tag alone would change under us on rebuild. +- **PyTorch:** `torch==2.14.0` from the `cu126` index. This is the last + release with a wheel for Volta (V100) GPUs, so don't upgrade it while + V100s are in use. +- **Driver:** tested on nodes with NVIDIA driver 525.147.05 (CUDA 12.0). + +## Build + +On a cluster node with apptainer (needs network access): + +``` +./build_container.sh +``` + +This builds the image, checks that `rserver`, `rsession`, `R`, and +`rstudio-server` exist inside it, moves it to +`/hpc//containers/rocker/`, and writes +`modules/testing/rocker_ml_cuda/.lua`. + +## Check the GPU + +``` +apptainer exec --nv /hpc//containers/rocker/rocker_ml_cuda_1.0.0.sif nvidia-smi +apptainer exec --nv /hpc//containers/rocker/rocker_ml_cuda_1.0.0.sif \ + /opt/venv/bin/python -c 'import torch; print(torch.cuda.is_available())' +``` + +## Notes for OOD + +R is installed from apt in this image (`/usr/bin/R`, libR in +`/usr/lib/R/lib`), not under `/usr/local` like other rocker images. The +RStudio app's rsession wrapper must also add `/.singularity.d/libs` and +`/opt/venv/bin` back to the paths after sourcing the host environment, or +the GPU and the Python venv are not visible in RStudio. See +`ood/rstudio/template/script.sh.erb`. diff --git a/containers/rocker_ml_cuda/build_container.sh b/containers/rocker_ml_cuda/build_container.sh new file mode 100755 index 00000000..dfc05e7a --- /dev/null +++ b/containers/rocker_ml_cuda/build_container.sh @@ -0,0 +1,109 @@ +#!/bin/bash -e +# build GPU-enabled apptainer container for RStudio (rocker/ml + CUDA PyTorch) +# base image and package pins are in rocker_ml_cuda.def + +# specify version +VERSION="1.0.0" +IMAGE_NAME="rocker_ml_cuda" +CONTAINER_NAME=${IMAGE_NAME}_${VERSION}.sif + +echo "Building ${CONTAINER_NAME}" + +# module file path below is relative to this directory +cd "$(dirname "$0")" + +# build the container +module purge +module load apptainer +unset APPTAINER_BIND +apptainer build --fakeroot ${CONTAINER_NAME} ${IMAGE_NAME}.def + +# get the paths for rserver, rsession, and R inside the container. We need +# these for the portal, so it's useful to "precompute" and load +RSESSION_EXEC="/usr/lib/rstudio-server/bin/rsession" +RSERVER_EXEC="/usr/lib/rstudio-server/bin/rserver" +R_EXEC=$(apptainer exec ${CONTAINER_NAME} which R) +# verify +for EXEC in ${RSESSION_EXEC} ${RSERVER_EXEC} ${R_EXEC} +do + if ! apptainer exec ${CONTAINER_NAME} test -x ${EXEC} &> /dev/null + then + echo "${EXEC} not found in the container" + exit 12 + fi +done +if ! apptainer exec ${CONTAINER_NAME} which rstudio-server &> /dev/null +then + echo "rstudio-server not found in the container (needed for the OOD idle timeout)" + exit 12 +fi + +# move container to /hpc/{sys}/containers/ +CLUSTER=$(scontrol show config | grep ClusterName | grep -oP '= \K.+') +if [ "$CLUSTER" = "nvidia" ]; then + CLUSTER="mp" +fi +mkdir -p /hpc/${CLUSTER}/containers/rocker +mv ${CONTAINER_NAME} /hpc/${CLUSTER}/containers/rocker/${CONTAINER_NAME} + +# create a module file in ../../modules/ (in the git repo) +RUN_COMMAND="apptainer run --nv " + +mkdir -p ../../modules/testing/${IMAGE_NAME} +MODULE_FILE=../../modules/testing/${IMAGE_NAME}/${VERSION}.lua +( +sed 's/^ \{2\}//' > "$MODULE_FILE" << EOL + + help([[ + Name: R + CUDA PyTorch + Version: ${VERSION} + Website: https://www.r-project.org/ + + R is a language and environment for statistical computing and graphics. + This container includes GPU-enabled PyTorch (via reticulate) for V100 GPUs. + + To use R: + + R -- launches an R terminal + Rscript script -- run a script + + ]]) + whatis("Name: R + CUDA PyTorch") + whatis("Version: rocker/ml:py3.14 + torch 2.14.0 (cu126)") + whatis("Category: R") + whatis("URL: https://hub.docker.com/r/rocker/ml") + whatis("Description: Provides access to R and Rstudio with GPU-enabled PyTorch through a Rocker container built with Apptainer") + family("R") + + always_load('apptainer') + local sif_file = '/hpc/${CLUSTER}/containers/rocker/${CONTAINER_NAME}' + + local work_dir = '/work' + local lustre_work = '/lustre/work/client' + local scratch_dir = os.getenv("SCRATCH") + local home = os.getenv("HOME") + local user_libs = pathJoin(home, 'R/${IMAGE_NAME}/${VERSION}') + + function build_command(app) + local cmd = '${RUN_COMMAND} --env R_LIBS_USER=' .. user_libs .. ' -B ' .. scratch_dir .. ',' .. work_dir .. ',' .. lustre_work .. ' ' .. sif_file .. ' ' .. app + local sh_ending = ' "\$@"' + local csh_ending = ' \$*' + local sh_cmd = cmd .. sh_ending + local csh_cmd = cmd .. csh_ending + set_shell_function(app, sh_cmd, csh_cmd) + end + + setenv('CONTAINER_RSESSION', '${RSESSION_EXEC}') + setenv('CONTAINER_RSERVER', '${RSERVER_EXEC}') + setenv('CONTAINER_R', '${R_EXEC}') + setenv('CONTAINER_IMAGE', '/hpc/${CLUSTER}/containers/rocker/${CONTAINER_NAME}') + setenv('R_LIBS_USER', user_libs) + unsetenv('XDG_RUNTIME_DIR') + + build_command('R') + build_command('Rscript') +EOL +) + +echo "Container: /hpc/${CLUSTER}/containers/rocker/${CONTAINER_NAME}" +echo "Module: ${MODULE_FILE}" diff --git a/containers/rocker_ml_cuda/rocker_ml_cuda.def b/containers/rocker_ml_cuda/rocker_ml_cuda.def new file mode 100644 index 00000000..ada654a4 --- /dev/null +++ b/containers/rocker_ml_cuda/rocker_ml_cuda.def @@ -0,0 +1,22 @@ +Bootstrap: docker +# rocker/ml:py3.14 (== latest as of 2026-10-04), pinned by digest because +# rocker/ml no longer publishes R version tags +From: rocker/ml@sha256:77443c5ff6422556809910000db4fb3576cb00609f39474bdb9f04edc3eeddca + +%labels + Author Mateo Langston Smith + Version 1.0.0 + +%post + export DEBIAN_FRONTEND=noninteractive + + # GPU PyTorch for V100 (cu126; 2.14.0 is the last release with a Volta wheel) + /opt/venv/bin/pip install --no-cache-dir torch==2.14.0 --index-url https://download.pytorch.org/whl/cu126 + /opt/venv/bin/pip install --no-cache-dir tabpfn + + # R packages (uses the image's default CRAN/binary repo settings) + Rscript -e 'install.packages(c("tidymodels","probably","ranger","tictoc","modeldata","Lahman","tabpfn"))' + +%environment + export CUDA_MODULE_LOADING=LAZY + export RETICULATE_PYTHON=/opt/venv/bin/python From 2ffb1dfaec2e4f32157fb77593fbb63f4b93259c Mon Sep 17 00:00:00 2001 From: Mateo Langston Smith Date: Thu, 15 Oct 2026 10:25:09 -0500 Subject: [PATCH 03/10] Move to test container --- ood/rstudio/form.yml.erb | 2 +- ood/rstudio/template/script.sh.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ood/rstudio/form.yml.erb b/ood/rstudio/form.yml.erb index e30f66a6..a6d02fb3 100644 --- a/ood/rstudio/form.yml.erb +++ b/ood/rstudio/form.yml.erb @@ -69,7 +69,7 @@ attributes: - [ "R 4.5.1 (custom kbnk)", "kbnk/R/4.5.1" ] <%- end -%> <%- if gpu_tester -%> - - [ "GPU test container (rocker-ml-cuda)", "custom" ] + - [ "4.6.1 + GPU", "custom" ] <%- end -%> environment: widget: text_area diff --git a/ood/rstudio/template/script.sh.erb b/ood/rstudio/template/script.sh.erb index 4e1df900..7b5e7550 100755 --- a/ood/rstudio/template/script.sh.erb +++ b/ood/rstudio/template/script.sh.erb @@ -15,7 +15,7 @@ env <%- if context.r_module == "custom" -%> # testing: GPU-enabled custom container, set what the R module would module load apptainer -export CONTAINER_IMAGE=/lustre/scratch/client/users/mlangstonsmith/rocker-ml-cuda.sif +export CONTAINER_IMAGE=/lustre/scratch/client/users/mlangstonsmith/rocker_ml_cuda_test.sif export CONTAINER_RSERVER=/usr/lib/rstudio-server/bin/rserver export CONTAINER_RSESSION=/usr/lib/rstudio-server/bin/rsession export CONTAINER_R=/usr/bin/R From b8fbe9c8e127c9a0c0802313a07c74fe8ab359cf Mon Sep 17 00:00:00 2001 From: Mateo Langston Smith Date: Thu, 15 Oct 2026 10:37:53 -0500 Subject: [PATCH 04/10] Add module commands --- ood/rstudio/form.yml.erb | 4 ++-- ood/rstudio/template/script.sh.erb | 14 +------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/ood/rstudio/form.yml.erb b/ood/rstudio/form.yml.erb index a6d02fb3..6465a52c 100644 --- a/ood/rstudio/form.yml.erb +++ b/ood/rstudio/form.yml.erb @@ -5,7 +5,7 @@ require 'open3' require_relative "CheckQueues" require_relative "CheckAccounts" -# testing: GPU-enabled custom container (bypasses the R module) +# testing: GPU-enabled container (modules/testing/rocker_ml_cuda) gpu_tester = ["mlangstonsmith"].include?(User.new.name) -%> @@ -69,7 +69,7 @@ attributes: - [ "R 4.5.1 (custom kbnk)", "kbnk/R/4.5.1" ] <%- end -%> <%- if gpu_tester -%> - - [ "4.6.1 + GPU", "custom" ] + - [ "4.6.1 + GPU", "/hpc/m3/hpc_docs/modules/testing/rocker_ml_cuda/1.0.0.lua" ] <%- end -%> environment: widget: text_area diff --git a/ood/rstudio/template/script.sh.erb b/ood/rstudio/template/script.sh.erb index 7b5e7550..69f700dc 100755 --- a/ood/rstudio/template/script.sh.erb +++ b/ood/rstudio/template/script.sh.erb @@ -12,17 +12,6 @@ env <%= context.environment.encode(universal_newline: true) %> <%- end -%> -<%- if context.r_module == "custom" -%> -# testing: GPU-enabled custom container, set what the R module would -module load apptainer -export CONTAINER_IMAGE=/lustre/scratch/client/users/mlangstonsmith/rocker_ml_cuda_test.sif -export CONTAINER_RSERVER=/usr/lib/rstudio-server/bin/rserver -export CONTAINER_RSESSION=/usr/lib/rstudio-server/bin/rsession -export CONTAINER_R=/usr/bin/R -export R_LIBS_USER=${HOME}/R/rocker-ml-cuda -mkdir -p ${R_LIBS_USER} -unset XDG_RUNTIME_DIR -<%- else -%> # load the requested module FORM_MODULE=<%= context.r_module %> EXTENSION="${FORM_MODULE##*.}" @@ -38,7 +27,6 @@ then else module load ${FORM_MODULE} fi -<%- end -%> # # Start RStudio Server @@ -65,7 +53,7 @@ sed 's/^ \{2\}//' > "${RSESSION_WRAPPER_FILE}" << EOL # make sure the container libR is visible inside container export LD_LIBRARY_PATH="/usr/local/lib/R/lib${LD_LIBRARY_PATH:+":$LD_LIBRARY_PATH"}" -<%- if context.r_module == "custom" -%> +<%- if context.r_module.include?("rocker_ml_cuda") -%> # environment.txt and the line above replace the container's paths, so # restore libR (apt-installed in this image), the host driver libs bound From 713e3f081cf39b786c028103167df0e12817ee3d Mon Sep 17 00:00:00 2001 From: Mateo Langston Smith Date: Thu, 15 Oct 2026 12:28:07 -0500 Subject: [PATCH 05/10] Adjust to testing path --- containers/rocker_ml_cuda/README.md | 6 +++--- containers/rocker_ml_cuda/build_container.sh | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/containers/rocker_ml_cuda/README.md b/containers/rocker_ml_cuda/README.md index 9ef36b5b..a354b4f6 100644 --- a/containers/rocker_ml_cuda/README.md +++ b/containers/rocker_ml_cuda/README.md @@ -24,14 +24,14 @@ On a cluster node with apptainer (needs network access): This builds the image, checks that `rserver`, `rsession`, `R`, and `rstudio-server` exist inside it, moves it to -`/hpc//containers/rocker/`, and writes +`/hpc//containers/testing/rocker/`, and writes `modules/testing/rocker_ml_cuda/.lua`. ## Check the GPU ``` -apptainer exec --nv /hpc//containers/rocker/rocker_ml_cuda_1.0.0.sif nvidia-smi -apptainer exec --nv /hpc//containers/rocker/rocker_ml_cuda_1.0.0.sif \ +apptainer exec --nv /hpc//containers/testing/rocker/rocker_ml_cuda_1.0.0.sif nvidia-smi +apptainer exec --nv /hpc//containers/testing/rocker/rocker_ml_cuda_1.0.0.sif \ /opt/venv/bin/python -c 'import torch; print(torch.cuda.is_available())' ``` diff --git a/containers/rocker_ml_cuda/build_container.sh b/containers/rocker_ml_cuda/build_container.sh index dfc05e7a..2765c546 100755 --- a/containers/rocker_ml_cuda/build_container.sh +++ b/containers/rocker_ml_cuda/build_container.sh @@ -38,13 +38,15 @@ then exit 12 fi -# move container to /hpc/{sys}/containers/ +# move container to /hpc/{sys}/containers/testing/ (kept out of the main +# containers directory while in testing) CLUSTER=$(scontrol show config | grep ClusterName | grep -oP '= \K.+') if [ "$CLUSTER" = "nvidia" ]; then CLUSTER="mp" fi -mkdir -p /hpc/${CLUSTER}/containers/rocker -mv ${CONTAINER_NAME} /hpc/${CLUSTER}/containers/rocker/${CONTAINER_NAME} +CONTAINER_DIR=/hpc/${CLUSTER}/containers/testing/rocker +mkdir -p ${CONTAINER_DIR} +mv ${CONTAINER_NAME} ${CONTAINER_DIR}/${CONTAINER_NAME} # create a module file in ../../modules/ (in the git repo) RUN_COMMAND="apptainer run --nv " @@ -76,7 +78,7 @@ sed 's/^ \{2\}//' > "$MODULE_FILE" << EOL family("R") always_load('apptainer') - local sif_file = '/hpc/${CLUSTER}/containers/rocker/${CONTAINER_NAME}' + local sif_file = '${CONTAINER_DIR}/${CONTAINER_NAME}' local work_dir = '/work' local lustre_work = '/lustre/work/client' @@ -96,7 +98,7 @@ sed 's/^ \{2\}//' > "$MODULE_FILE" << EOL setenv('CONTAINER_RSESSION', '${RSESSION_EXEC}') setenv('CONTAINER_RSERVER', '${RSERVER_EXEC}') setenv('CONTAINER_R', '${R_EXEC}') - setenv('CONTAINER_IMAGE', '/hpc/${CLUSTER}/containers/rocker/${CONTAINER_NAME}') + setenv('CONTAINER_IMAGE', '${CONTAINER_DIR}/${CONTAINER_NAME}') setenv('R_LIBS_USER', user_libs) unsetenv('XDG_RUNTIME_DIR') @@ -105,5 +107,5 @@ sed 's/^ \{2\}//' > "$MODULE_FILE" << EOL EOL ) -echo "Container: /hpc/${CLUSTER}/containers/rocker/${CONTAINER_NAME}" +echo "Container: ${CONTAINER_DIR}/${CONTAINER_NAME}" echo "Module: ${MODULE_FILE}" From 556ebb9d902aa6a65f3091e45813c5fd3d52aa2e Mon Sep 17 00:00:00 2001 From: Mateo Langston Smith Date: Thu, 15 Oct 2026 12:43:29 -0500 Subject: [PATCH 06/10] Add module to scratch just for testing --- containers/rocker_ml_cuda/README.md | 2 +- containers/rocker_ml_cuda/build_container.sh | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/containers/rocker_ml_cuda/README.md b/containers/rocker_ml_cuda/README.md index a354b4f6..50ac5fe5 100644 --- a/containers/rocker_ml_cuda/README.md +++ b/containers/rocker_ml_cuda/README.md @@ -25,7 +25,7 @@ On a cluster node with apptainer (needs network access): This builds the image, checks that `rserver`, `rsession`, `R`, and `rstudio-server` exist inside it, moves it to `/hpc//containers/testing/rocker/`, and writes -`modules/testing/rocker_ml_cuda/.lua`. +`$SCRATCH/modules/testing/rocker_ml_cuda/.lua`. ## Check the GPU diff --git a/containers/rocker_ml_cuda/build_container.sh b/containers/rocker_ml_cuda/build_container.sh index 2765c546..ff737a05 100755 --- a/containers/rocker_ml_cuda/build_container.sh +++ b/containers/rocker_ml_cuda/build_container.sh @@ -9,9 +9,15 @@ CONTAINER_NAME=${IMAGE_NAME}_${VERSION}.sif echo "Building ${CONTAINER_NAME}" -# module file path below is relative to this directory +# the .def file path below is relative to this directory cd "$(dirname "$0")" +# module file is written to $SCRATCH for testing +if [ -z "${SCRATCH}" ]; then + echo "SCRATCH is not set" + exit 12 +fi + # build the container module purge module load apptainer @@ -48,11 +54,11 @@ CONTAINER_DIR=/hpc/${CLUSTER}/containers/testing/rocker mkdir -p ${CONTAINER_DIR} mv ${CONTAINER_NAME} ${CONTAINER_DIR}/${CONTAINER_NAME} -# create a module file in ../../modules/ (in the git repo) +# create a module file in $SCRATCH/modules/ (outside the git repo for testing) RUN_COMMAND="apptainer run --nv " -mkdir -p ../../modules/testing/${IMAGE_NAME} -MODULE_FILE=../../modules/testing/${IMAGE_NAME}/${VERSION}.lua +mkdir -p ${SCRATCH}/modules/testing/${IMAGE_NAME} +MODULE_FILE=${SCRATCH}/modules/testing/${IMAGE_NAME}/${VERSION}.lua ( sed 's/^ \{2\}//' > "$MODULE_FILE" << EOL From 1464bf9dcd89ed7df855f5fd9c00e74c00cec2ae Mon Sep 17 00:00:00 2001 From: Mateo Langston Smith Date: Thu, 15 Oct 2026 12:47:08 -0500 Subject: [PATCH 07/10] relative path --- containers/rocker_ml_cuda/README.md | 2 +- containers/rocker_ml_cuda/build_container.sh | 14 ++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/containers/rocker_ml_cuda/README.md b/containers/rocker_ml_cuda/README.md index 50ac5fe5..c6b5cf0c 100644 --- a/containers/rocker_ml_cuda/README.md +++ b/containers/rocker_ml_cuda/README.md @@ -25,7 +25,7 @@ On a cluster node with apptainer (needs network access): This builds the image, checks that `rserver`, `rsession`, `R`, and `rstudio-server` exist inside it, moves it to `/hpc//containers/testing/rocker/`, and writes -`$SCRATCH/modules/testing/rocker_ml_cuda/.lua`. +`modules/testing/rocker_ml_cuda/.lua` in this repo. ## Check the GPU diff --git a/containers/rocker_ml_cuda/build_container.sh b/containers/rocker_ml_cuda/build_container.sh index ff737a05..414ac25f 100755 --- a/containers/rocker_ml_cuda/build_container.sh +++ b/containers/rocker_ml_cuda/build_container.sh @@ -9,15 +9,9 @@ CONTAINER_NAME=${IMAGE_NAME}_${VERSION}.sif echo "Building ${CONTAINER_NAME}" -# the .def file path below is relative to this directory +# .def and module file paths below are relative to this directory cd "$(dirname "$0")" -# module file is written to $SCRATCH for testing -if [ -z "${SCRATCH}" ]; then - echo "SCRATCH is not set" - exit 12 -fi - # build the container module purge module load apptainer @@ -54,11 +48,11 @@ CONTAINER_DIR=/hpc/${CLUSTER}/containers/testing/rocker mkdir -p ${CONTAINER_DIR} mv ${CONTAINER_NAME} ${CONTAINER_DIR}/${CONTAINER_NAME} -# create a module file in $SCRATCH/modules/ (outside the git repo for testing) +# create a module file in ../../modules/ (in the git repo) RUN_COMMAND="apptainer run --nv " -mkdir -p ${SCRATCH}/modules/testing/${IMAGE_NAME} -MODULE_FILE=${SCRATCH}/modules/testing/${IMAGE_NAME}/${VERSION}.lua +mkdir -p ../../modules/testing/${IMAGE_NAME} +MODULE_FILE=../../modules/testing/${IMAGE_NAME}/${VERSION}.lua ( sed 's/^ \{2\}//' > "$MODULE_FILE" << EOL From e7345a7045aa52ce1360b44bdd07873c0109e101 Mon Sep 17 00:00:00 2001 From: mateolangston Date: Thu, 15 Oct 2026 13:03:40 -0500 Subject: [PATCH 08/10] add module file --- modules/testing/rocker_ml_cuda/1.0.0.lua | 49 ++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 modules/testing/rocker_ml_cuda/1.0.0.lua diff --git a/modules/testing/rocker_ml_cuda/1.0.0.lua b/modules/testing/rocker_ml_cuda/1.0.0.lua new file mode 100644 index 00000000..c6d8bace --- /dev/null +++ b/modules/testing/rocker_ml_cuda/1.0.0.lua @@ -0,0 +1,49 @@ + +help([[ +Name: R + CUDA PyTorch +Version: 1.0.0 +Website: https://www.r-project.org/ + +R is a language and environment for statistical computing and graphics. +This container includes GPU-enabled PyTorch (via reticulate) for V100 GPUs. + +To use R: + +R -- launches an R terminal +Rscript script -- run a script + +]]) +whatis("Name: R + CUDA PyTorch") +whatis("Version: rocker/ml:py3.14 + torch 2.14.0 (cu126)") +whatis("Category: R") +whatis("URL: https://hub.docker.com/r/rocker/ml") +whatis("Description: Provides access to R and Rstudio with GPU-enabled PyTorch through a Rocker container built with Apptainer") +family("R") + +always_load('apptainer') +local sif_file = '/hpc/m3/containers/testing/rocker/rocker_ml_cuda_1.0.0.sif' + +local work_dir = '/work' +local lustre_work = '/lustre/work/client' +local scratch_dir = os.getenv("SCRATCH") +local home = os.getenv("HOME") +local user_libs = pathJoin(home, 'R/rocker_ml_cuda/1.0.0') + +function build_command(app) + local cmd = 'apptainer run --nv --env R_LIBS_USER=' .. user_libs .. ' -B ' .. scratch_dir .. ',' .. work_dir .. ',' .. lustre_work .. ' ' .. sif_file .. ' ' .. app + local sh_ending = ' "$@"' + local csh_ending = ' $*' + local sh_cmd = cmd .. sh_ending + local csh_cmd = cmd .. csh_ending + set_shell_function(app, sh_cmd, csh_cmd) +end + +setenv('CONTAINER_RSESSION', '/usr/lib/rstudio-server/bin/rsession') +setenv('CONTAINER_RSERVER', '/usr/lib/rstudio-server/bin/rserver') +setenv('CONTAINER_R', '/usr/bin/R') +setenv('CONTAINER_IMAGE', '/hpc/m3/containers/testing/rocker/rocker_ml_cuda_1.0.0.sif') +setenv('R_LIBS_USER', user_libs) +unsetenv('XDG_RUNTIME_DIR') + +build_command('R') +build_command('Rscript') From 31aa918e4d4a3f57f91fba217719859e28189b17 Mon Sep 17 00:00:00 2001 From: Mateo Langston Smith Date: Thu, 15 Oct 2026 13:09:46 -0500 Subject: [PATCH 09/10] Update form.yml.erb --- ood/rstudio/form.yml.erb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ood/rstudio/form.yml.erb b/ood/rstudio/form.yml.erb index 6465a52c..12f21092 100644 --- a/ood/rstudio/form.yml.erb +++ b/ood/rstudio/form.yml.erb @@ -69,7 +69,8 @@ attributes: - [ "R 4.5.1 (custom kbnk)", "kbnk/R/4.5.1" ] <%- end -%> <%- if gpu_tester -%> - - [ "4.6.1 + GPU", "/hpc/m3/hpc_docs/modules/testing/rocker_ml_cuda/1.0.0.lua" ] + # TODO before PR: change base path back to /hpc/m3/hpc_docs/modules + - [ "4.6.1 + GPU", "/lustre/scratch/client/users/mlangstonsmith/hpc_docs/modules/testing/rocker_ml_cuda/1.0.0.lua" ] <%- end -%> environment: widget: text_area From c9f809bfb2a8b80f458eb48e0505a9bb28fd065f Mon Sep 17 00:00:00 2001 From: Mateo Langston Smith Date: Thu, 15 Oct 2026 13:50:42 -0500 Subject: [PATCH 10/10] Update form.yml.erb --- ood/rstudio/form.yml.erb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/ood/rstudio/form.yml.erb b/ood/rstudio/form.yml.erb index 12f21092..1eef12af 100644 --- a/ood/rstudio/form.yml.erb +++ b/ood/rstudio/form.yml.erb @@ -5,9 +5,6 @@ require 'open3' require_relative "CheckQueues" require_relative "CheckAccounts" -# testing: GPU-enabled container (modules/testing/rocker_ml_cuda) -gpu_tester = ["mlangstonsmith"].include?(User.new.name) - -%> --- @@ -68,9 +65,8 @@ attributes: <%- if CheckAccounts.Accounts.any? { |word| word.include?("kbnk") } | CheckAccounts.Accounts.any? { |word| word.include?("jlagrone") }-%> - [ "R 4.5.1 (custom kbnk)", "kbnk/R/4.5.1" ] <%- end -%> - <%- if gpu_tester -%> - # TODO before PR: change base path back to /hpc/m3/hpc_docs/modules - - [ "4.6.1 + GPU", "/lustre/scratch/client/users/mlangstonsmith/hpc_docs/modules/testing/rocker_ml_cuda/1.0.0.lua" ] + <%- if CheckAccounts.Accounts.any? { |word| word.include?("csouth") } | CheckAccounts.Accounts.any? { |word| word.include?("jlagrone") }-%> + - [ "4.6.1 + GPU", "/hpc/m3/hpc_docs/modules/testing/rocker_ml_cuda/1.0.0.lua" ] <%- end -%> environment: widget: text_area @@ -87,7 +83,7 @@ attributes: [SLURM partition]: https://southernmethodistuniversity.github.io/hpc_docs/about/queues.html#m3-queues options: <%- CheckQueues.Queues.each do |q| -%> - <%- if q[4].to_i == 0 || gpu_tester -%> #disable gpu queues (except for testers) + <%- if q[4].to_i == 0 || CheckAccounts.Accounts.any? { |word| word.include?("csouth") } || CheckAccounts.Accounts.any? { |word| word.include?("jlagrone") } -%> #disable gpu queues (except for GPU testers) - ["<%= q[0] %>", "<%= q[0] %>", data-max-bc-num-hours: "<%= q[1] %>", data-max-bc-num-cores: "<%= q[2] %>",