diff --git a/containers/rocker_ml_cuda/README.md b/containers/rocker_ml_cuda/README.md new file mode 100644 index 00000000..c6b5cf0c --- /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/testing/rocker/`, and writes +`modules/testing/rocker_ml_cuda/.lua` in this repo. + +## Check the GPU + +``` +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())' +``` + +## 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..414ac25f --- /dev/null +++ b/containers/rocker_ml_cuda/build_container.sh @@ -0,0 +1,111 @@ +#!/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}" + +# .def and module file paths below are 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/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 +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 " + +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 = '${CONTAINER_DIR}/${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', '${CONTAINER_DIR}/${CONTAINER_NAME}') + setenv('R_LIBS_USER', user_libs) + unsetenv('XDG_RUNTIME_DIR') + + build_command('R') + build_command('Rscript') +EOL +) + +echo "Container: ${CONTAINER_DIR}/${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 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') diff --git a/ood/rstudio/form.yml.erb b/ood/rstudio/form.yml.erb index e8e1d62b..1eef12af 100644 --- a/ood/rstudio/form.yml.erb +++ b/ood/rstudio/form.yml.erb @@ -65,6 +65,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 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 label: "Custom Environment Settings" @@ -80,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 -%> #disable gpu queues + <%- 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] %>", diff --git a/ood/rstudio/template/script.sh.erb b/ood/rstudio/template/script.sh.erb index 7606a8f7..69f700dc 100755 --- a/ood/rstudio/template/script.sh.erb +++ b/ood/rstudio/template/script.sh.erb @@ -53,6 +53,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.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 + # 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 +204,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 \