From 8e9b67f3e53df004b06ae15b7453cd30bc7149c2 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Mon, 2 Mar 2026 15:02:53 +0100 Subject: [PATCH] Add support for building containerized samples This change adds support for building per-sample containers. Two examples are added for the deviceQuery and conjugateGradient samples. Signed-off-by: Evan Lezar --- .dockerignore | 15 +++ .gitignore | 1 + README.md | 5 + Samples/1_Utilities/deviceQuery/Containerfile | 32 ++++++ .../conjugateGradient/Containerfile | 34 ++++++ cmake/ContainerSamples.cmake | 67 ++++++++++++ container/CMakeLists.txt | 24 +++++ container/README.md | 102 ++++++++++++++++++ 8 files changed, 280 insertions(+) create mode 100644 .dockerignore create mode 100644 Samples/1_Utilities/deviceQuery/Containerfile create mode 100644 Samples/4_CUDA_Libraries/conjugateGradient/Containerfile create mode 100644 cmake/ContainerSamples.cmake create mode 100644 container/CMakeLists.txt create mode 100644 container/README.md diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..98ad70f6c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,15 @@ +# Exclude large directories that are never needed inside the container build. +# The Containerfiles use explicit COPY commands for the three things they need: +# Common/, cmake/, and the specific sample directory. + +# Git internals +.git/ + +# CMake build trees +build*/ + +# Container build trees +build-container*/ + +# Windows-only pre-built binaries +bin/win64/ diff --git a/.gitignore b/.gitignore index 4b782397e..f17a24ca0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build +build-container .vs .clangd test diff --git a/README.md b/README.md index 4686e11d5..b3ff6eacc 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,11 @@ To build samples with new CUDA Toolkit(CUDA 13.0 or later) and UMD(Version 580 o cmake -DCMAKE_PREFIX_PATH=/usr/local/cuda/lib64/stubs/ .. ``` +### Building Container Images + +Individual samples can be built and run as OCI container images without a local +CUDA installation. See [container/README.md](./container/README.md) for details. + ## Install Samples ### Installation Path Structure diff --git a/Samples/1_Utilities/deviceQuery/Containerfile b/Samples/1_Utilities/deviceQuery/Containerfile new file mode 100644 index 000000000..0aec30e8a --- /dev/null +++ b/Samples/1_Utilities/deviceQuery/Containerfile @@ -0,0 +1,32 @@ +ARG CUDA_IMAGE_VERSION=12.6.0 + +# ---- Build stage ---- +FROM nvidia/cuda:${CUDA_IMAGE_VERSION}-devel-ubuntu22.04 AS builder + +RUN apt-get update && apt-get install -y --no-install-recommends \ + cmake \ + make \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /cuda-samples + +# Copy only what is needed to build this sample. +# The directory structure must be preserved so that the relative paths in +# the sample's CMakeLists.txt (e.g. ../../../Common) resolve correctly. +COPY Common/ Common/ +COPY cmake/ cmake/ +COPY Samples/1_Utilities/deviceQuery/ Samples/1_Utilities/deviceQuery/ + +RUN cmake -S Samples/1_Utilities/deviceQuery \ + -B Samples/1_Utilities/deviceQuery/build \ + -DCMAKE_BUILD_TYPE=Release \ + && cmake --build Samples/1_Utilities/deviceQuery/build --parallel $(nproc) + +# ---- Runtime stage ---- +FROM nvidia/cuda:${CUDA_IMAGE_VERSION}-base-ubuntu22.04 + +COPY --from=builder \ + /cuda-samples/Samples/1_Utilities/deviceQuery/build/deviceQuery \ + /usr/local/bin/deviceQuery + +ENTRYPOINT ["deviceQuery"] diff --git a/Samples/4_CUDA_Libraries/conjugateGradient/Containerfile b/Samples/4_CUDA_Libraries/conjugateGradient/Containerfile new file mode 100644 index 000000000..af8859094 --- /dev/null +++ b/Samples/4_CUDA_Libraries/conjugateGradient/Containerfile @@ -0,0 +1,34 @@ +ARG CUDA_IMAGE_VERSION=12.6.0 + +# ---- Build stage ---- +FROM nvidia/cuda:${CUDA_IMAGE_VERSION}-devel-ubuntu22.04 AS builder + +RUN apt-get update && apt-get install -y --no-install-recommends \ + cmake \ + make \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /cuda-samples + +# Copy only what is needed to build this sample. +# The directory structure must be preserved so that the relative paths in +# the sample's CMakeLists.txt (e.g. ../../../Common) resolve correctly. +COPY Common/ Common/ +COPY cmake/ cmake/ +COPY Samples/4_CUDA_Libraries/conjugateGradient/ Samples/4_CUDA_Libraries/conjugateGradient/ + +RUN cmake -S Samples/4_CUDA_Libraries/conjugateGradient \ + -B Samples/4_CUDA_Libraries/conjugateGradient/build \ + -DCMAKE_BUILD_TYPE=Release \ + && cmake --build Samples/4_CUDA_Libraries/conjugateGradient/build --parallel $(nproc) + +# ---- Runtime stage ---- +# The 'runtime' image (vs 'base') includes the CUDA math library runtimes +# (cublas, cusparse, cufft, etc.), so no extra packages are needed here. +FROM nvidia/cuda:${CUDA_IMAGE_VERSION}-runtime-ubuntu22.04 + +COPY --from=builder \ + /cuda-samples/Samples/4_CUDA_Libraries/conjugateGradient/build/conjugateGradient \ + /usr/local/bin/conjugateGradient + +ENTRYPOINT ["conjugateGradient"] diff --git a/cmake/ContainerSamples.cmake b/cmake/ContainerSamples.cmake new file mode 100644 index 000000000..0852ad59c --- /dev/null +++ b/cmake/ContainerSamples.cmake @@ -0,0 +1,67 @@ +# ContainerSamples.cmake +# +# Provides add_container_sample() for creating container build and run targets +# for individual CUDA samples. No CUDA toolkit is required on the host; all +# compilation happens inside the container using the devel base image. +# +# Any OCI-compatible CLI (docker, podman, nerdctl, …) is supported. The tool +# is located automatically, or can be overridden by setting CONTAINER_EXECUTABLE +# before including this module. +# +# Usage (from a CUDA-free CMakeLists.txt): +# +# include(cmake/ContainerSamples.cmake) +# add_container_sample(Samples/1_Utilities/deviceQuery) +# +# This creates two targets per sample: +# container_build_ -- builds the image from the sample's Containerfile +# container_run_ -- runs the image with GPU access + +find_program(CONTAINER_EXECUTABLE + NAMES docker podman nerdctl + REQUIRED + DOC "OCI-compatible container CLI used to build and run sample images" +) + +# CMAKE_CURRENT_LIST_DIR is the directory of this file (cmake/), so the repo +# root is one level up regardless of where the including CMakeLists.txt lives. +set(_CUDA_SAMPLES_ROOT "${CMAKE_CURRENT_LIST_DIR}/..") + +function(add_container_sample SAMPLE_REL_PATH) + get_filename_component(SAMPLE_NAME "${SAMPLE_REL_PATH}" NAME) + + # Accept either 'Containerfile' (preferred) or 'Dockerfile' (legacy name). + set(_CONTAINERFILE "${_CUDA_SAMPLES_ROOT}/${SAMPLE_REL_PATH}/Containerfile") + if(NOT EXISTS "${_CONTAINERFILE}") + set(_CONTAINERFILE "${_CUDA_SAMPLES_ROOT}/${SAMPLE_REL_PATH}/Dockerfile") + endif() + + if(NOT EXISTS "${_CONTAINERFILE}") + message(WARNING "add_container_sample: no Containerfile found for '${SAMPLE_NAME}' " + "(expected ${_CUDA_SAMPLES_ROOT}/${SAMPLE_REL_PATH}/Containerfile)") + return() + endif() + + string(TOLOWER "${SAMPLE_NAME}" _SAMPLE_NAME_LOWER) + set(_IMAGE_TAG "cuda-samples/${_SAMPLE_NAME_LOWER}:latest") + + set(_BUILD_ARGS --file "${_CONTAINERFILE}" --tag "${_IMAGE_TAG}") + if(CUDA_IMAGE_VERSION) + list(APPEND _BUILD_ARGS --build-arg "CUDA_IMAGE_VERSION=${CUDA_IMAGE_VERSION}") + endif() + + add_custom_target(container_build_${SAMPLE_NAME} + COMMAND "${CONTAINER_EXECUTABLE}" build + ${_BUILD_ARGS} + "${_CUDA_SAMPLES_ROOT}" + WORKING_DIRECTORY "${_CUDA_SAMPLES_ROOT}" + COMMENT "Building container image ${_IMAGE_TAG}" + VERBATIM + ) + + add_custom_target(container_run_${SAMPLE_NAME} + COMMAND "${CONTAINER_EXECUTABLE}" run --rm --runtime=nvidia --gpus all "${_IMAGE_TAG}" + COMMENT "Running ${_IMAGE_TAG}" + VERBATIM + ) +endfunction() diff --git a/container/CMakeLists.txt b/container/CMakeLists.txt new file mode 100644 index 000000000..7333640c9 --- /dev/null +++ b/container/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.20) + +# This project generates container build and run targets for individual CUDA +# samples. It does NOT require a CUDA toolkit or GPU on the host; all +# compilation happens inside the container. +# +# Configure and build from the repo root: +# +# cmake -B build-container -S container/ +# cmake --build build-container --target container_build_deviceQuery + +project(cuda-samples-container LANGUAGES NONE) + +set(CUDA_IMAGE_VERSION "12.6.0" CACHE STRING + "CUDA image version used as the base for container builds (e.g. 12.6.2, 13.1.0)") + +include(../cmake/ContainerSamples.cmake) + +# --------------------------------------------------------------------------- +# Register samples that have a Containerfile / Dockerfile. +# Add further samples here as container support is extended. +# --------------------------------------------------------------------------- +add_container_sample(Samples/1_Utilities/deviceQuery) +add_container_sample(Samples/4_CUDA_Libraries/conjugateGradient) diff --git a/container/README.md b/container/README.md new file mode 100644 index 000000000..ba75546c5 --- /dev/null +++ b/container/README.md @@ -0,0 +1,102 @@ +# Building CUDA Samples as Container Images + +Individual CUDA samples can be built and run as OCI container images. This +requires no CUDA toolkit or GPU driver on the build host — all compilation +happens inside the container using the CUDA devel base image. + +## Prerequisites + +- An OCI-compatible container CLI: [Docker](https://docs.docker.com/engine/install/), + [Podman](https://podman.io/docs/installation), or + [nerdctl](https://github.com/containerd/nerdctl) +- CMake 3.20 or later + +## Configure + +From the repository root, configure the container build project: + +```bash +cmake -B build-container -S container/ +``` + +CMake will automatically detect `docker`, `podman`, or `nerdctl` (in that +order). To use a specific tool, pass it explicitly: + +```bash +cmake -B build-container -S container/ -DCONTAINER_EXECUTABLE=/usr/bin/podman +``` + +## Build a sample image + +```bash +cmake --build build-container --target container_build_deviceQuery +cmake --build build-container --target container_build_conjugateGradient +``` + +Each target runs the equivalent of: + +```bash +docker build \ + --file Samples///Containerfile \ + --tag cuda-samples/:latest \ + . +``` + +using the repo root as the build context. + +## Run a sample + +Running a sample requires a GPU on the current machine and the +[NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/) +installed on the host. + +```bash +cmake --build build-container --target container_run_deviceQuery +``` + +Or directly: + +```bash +docker run --rm --runtime=nvidia --gpus=all cuda-samples/devicequery:latest +``` + +## CUDA image version + +The Containerfiles default to the version set by `CUDA_IMAGE_VERSION` in +[CMakeLists.txt](CMakeLists.txt). Override it at configure time to build +against a different CUDA base image: + +```bash +cmake -B build-container -S container/ -DCUDA_IMAGE_VERSION=13.1.0 +``` + +To tag the image to reflect the version, pass `--tag` directly: + +```bash +docker build \ + --build-arg CUDA_IMAGE_VERSION=13.1.0 \ + --file Samples/1_Utilities/deviceQuery/Containerfile \ + --tag cuda-samples/devicequery:cuda13.1.0 \ + . +``` + +## Adding container support for a new sample + +1. Add a `Containerfile` to the sample's directory (see an existing sample for + reference). The build context is always the repository root, so the + directory structure under `/cuda-samples/` inside the container must mirror + the repository layout so that relative paths in the sample's `CMakeLists.txt` + resolve correctly. + +2. Register the sample in [CMakeLists.txt](CMakeLists.txt): + + ```cmake + add_container_sample(Samples//) + ``` + +## Samples with container support + +| Sample | Category | +|--------|----------| +| [deviceQuery](../Samples/1_Utilities/deviceQuery/) | Utilities | +| [conjugateGradient](../Samples/4_CUDA_Libraries/conjugateGradient/) | CUDA Libraries |