Skip to content

Building Custom Images

Users with the need to build their custom images can use one of the base images provided by NERSC. Before starting custom builds, please refer to the container building basics provided at our shifter-for-beginners-tutorial page for build terminology and instructions. This instruction page assumes that you either have podman-desktop (free) or docker-desktop (may require payment) installed on your local system. It is possible to build a custom container image on Perlmutter using podman-hpc. However, there are additional needed to then push the image to a container repository and pull it from there using shifter. Therefore, we have used podman desktop for all the examples discussed on this page.

Container base images

Depending on the use case, there are several base images that are available to users to build their images on. Popular among them are the following:

dockerfile FROM ubuntu:22.04

which is typically used as a base image for CPU only application builds.

dockerfile FROM nvcr.io/nvidia/cuda:11.8.0-cudnn8-devel-ubuntu22.04

which is our preferred base image to build GPU enabled applications. There are several other base images that are available as a starting point for GPU enabled applications from the NGC repository

NERSC base image

However, any container image can be used as a base image and therefore, as a starting point. We suggest our users to start their builds with the following NERSC supported base image:

dockerfile FROM docker.io/nersc/base_cuda_mpich:11.8x4.2.2

The recipe for which is as follows:

```dockerfile FROM nvcr.io/nvidia/cuda:11.8.0-cudnn8-devel-ubuntu22.04 WORKDIR /opt ENV DEBIAN_FRONTEND noninteractive

RUN \ apt-get update && \
apt-get install –yes \
build-essential autoconf cmake flex bison zlib1g-dev \ fftw-dev fftw3 apbs libicu-dev libbz2-dev libgmp-dev \ libboost-all-dev bc libblas-dev liblapack-dev git \
libfftw3-dev automake lsb-core libxc-dev libgsl-dev \ unzip libhdf5-serial-dev ffmpeg libcurl4-openssl-dev \ libboost-dev libboost-system-dev libtool automake \
libboost-filesystem-dev libboost-graph-dev uuid-dev \ libboost-regex-dev libedit-dev libyaml-cpp-dev make \ python3-dev python3-cffi python3-ply python3-pip \
aspell aspell-en valgrind libyaml-cpp-dev wget vim \
libzmq3-dev python3-yaml time valgrind libevent-dev \ mlocate python3-jsonschema python-is-python3 &&\ apt-get clean all

Install MPICH

WORKDIR /opt ARG mpich=3.4a2 ARG mpich_prefix=mpich-$mpich RUN \ wget https://www.mpich.org/static/downloads/$mpich/$mpich_prefix.tar.gz && \ tar xvzf $mpich_prefix.tar.gz && \ cd $mpich_prefix && \ ./configure FFLAGS=-fallow-argument-mismatch FCFLAGS=-fallow-argument-mismatch \ –prefix=/opt/mpich/install && \ make -j 16 && \ make install && \ make clean && \ cd .. && \ rm -rf $mpich_prefix.tar.gz ENV PATH=$PATH:/opt/mpich/install/bin ENV PATH=$PATH:/opt/mpich/install/include ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/mpich/install/lib RUN /sbin/ldconfig

ENV PATH=$PATH:/usr/local/cuda/lib64/stubs ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64/stubs ENV PATH=$PATH:/usr/local/cuda-11.8/targets/x86_64-linux/lib/stubs ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-11.8/targets/x86_64-linux/lib/stubs

Install Eigen and Blas

WORKDIR /opt RUN git clone https://gitlab.com/libeigen/eigen.git RUN cd /opt/eigen && \ mkdir build && \ cd /opt/eigen/build && \ cmake -DCMAKE_INSTALL_PREFIX=/opt/eigen/install .. && \ make blas && \ make lapack && \ make install
ENV PATH=$PATH:/opt/eigen/install/bin ENV PATH=$PATH:/opt/eigen/install/include ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/eigen/install/lib ```

This base image should provide most of the needed libraries and compilers needed to build a GPU enabled application. It is built off of a CUDA 11.8 base image and includes MPICH 4.2.2, which will be replaced using the Cray-MPICH module injection during runtime on Perlmutter using shifter modules. This image is freely available to all NERSC users.

Guide to using custom images

To build a custom container, user can use the following Containerfile (the name of the file) template:

```dockerfile

Containerfile template to build your custom image

FROM docker.io/nersc/base_cuda_mpich:11.8x4.2.2 WORKDIR /opt ENV DEBIAN_FRONTEND noninteractive ENV CUDA_INSTALL_PATH “/usr/local/cuda-11.8/targets/x86_64-linux” ENV MPI_HOME “/opt/mpich/install”

Install your application here

WORKDIR /opt RUN RUN cd /opt/ && \ mkdir build && \ cd /build && \
ENV PATH=$PATH: ENV PATH=$PATH: ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH: ```

The custom image has to be built first on the local machine before it can be deployed on Perlmutter. To build this application on your local machine using podman, use a terminal window pointing to directory containing the Containerfile above and use the following:

bash user@localhost:~> podman build -t <username>/<application name>:<tag> -f Containerfile --progress=plain .

Upon completion of the build process, you can check the availability of this image on the local host using:

bash user@localhost:~> podman images

which will provide the user with the repository name, image tag, image ID, date it was created, and the size of the image. To use this image on Perlmutter, it has to be pushed using image ID to either dockerhub, or quay.io hub, or registry.nersc.io. For this test case, We are using dockerhub repository:

bash user@localhost:~> podman push <image ID> docker://docker.io/<your dockerhub username>/<application name>:<tag>

Users can now pull this image on Perlmutter using shifter.

bash user@loginXXXX:~> shifterimg -v pull docker:<your dockerhub username>/<application name>:<tag>

To verify if the image was pulled successfully, use the following:

bash user@loginXXXX:~> shifterimg images | grep '<application name>'

Example 1

In the following example, we are using the NERSC provided base image to build custom image to test CUDA aware MPI performance. The recipe of this image build is as follows:

```dockerfile

Containerfile for CUDA-aware-MPI using NERSC cuda-mpich base image

FROM docker.io/nersc/base_cuda_mpich:11.8x4.2.2 WORKDIR /opt ENV DEBIAN_FRONTEND noninteractive ENV CUDA_INSTALL_PATH “/usr/local/cuda-11.8/targets/x86_64-linux” ENV MPI_HOME “/opt/mpich/install”

Install MPI-ping-pong from OLCF

WORKDIR /opt RUN git clone https://github.com/olcf-tutorials/MPI_ping_pong.git test_code RUN cd /opt/test_code/cpu && \ make
RUN cd /opt/test_code/cuda_staged && \ sed -i ‘s:-arch=sm_70:-arch=sm_80:g’ Makefile && \ sed -i ‘s:OMPI_DIR:MPI_HOME:g’ Makefile && \ sed -i ‘s:-lmpi_ibm:-lmpi:g’ Makefile && \ make
RUN cd /opt/test_code/cuda_aware && \ sed -i ‘s:-arch=sm_70:-arch=sm_80:g’ Makefile && \ sed -i ‘s:OMPI_DIR:MPI_HOME:g’ Makefile && \ sed -i ‘s:-lmpi_ibm:-lmpi:g’ Makefile && \ make ENV PATH=$PATH:/opt/test_code/cpu ENV PATH=$PATH:/opt/test_code/cuda_staged ENV PATH=$PATH:/opt/test_code/cuda_aware
```

After pushing the custom image to dockerhub repository and then pulling it to Perlmutter, we can test it on Perlmutter using an interactive node and running the container image using shifter.

console user@loginXXXX:~> salloc -N 2 -G 2 -C gpu -t 01:00:00 -A nstaff -q interactive --image=neilmehta87/cuda_aware_mpi:0.1 user@nid00XXXX:~> srun -n 2 --module cuda-mpich shifter pp_cuda_aware Transfer size (B): 8, Transfer Time (s): 0.000061704, Bandwidth (GB/s): 0.000120748 Transfer size (B): 16, Transfer Time (s): 0.000062580, Bandwidth (GB/s): 0.000238114 Transfer size (B): 32, Transfer Time (s): 0.000062027, Bandwidth (GB/s): 0.000480470 Transfer size (B): 64, Transfer Time (s): 0.000062328, Bandwidth (GB/s): 0.000956308 Transfer size (B): 128, Transfer Time (s): 0.000063280, Bandwidth (GB/s): 0.001883841 Transfer size (B): 256, Transfer Time (s): 0.000109115, Bandwidth (GB/s): 0.002185023 Transfer size (B): 512, Transfer Time (s): 0.000135204, Bandwidth (GB/s): 0.003526788 Transfer size (B): 1024, Transfer Time (s): 0.000105962, Bandwidth (GB/s): 0.009000118 Transfer size (B): 2048, Transfer Time (s): 0.000107372, Bandwidth (GB/s): 0.017763905 Transfer size (B): 4096, Transfer Time (s): 0.000107688, Bandwidth (GB/s): 0.035423685 Transfer size (B): 8192, Transfer Time (s): 0.000107290, Bandwidth (GB/s): 0.071110223 Transfer size (B): 16384, Transfer Time (s): 0.000105464, Bandwidth (GB/s): 0.144681948 Transfer size (B): 32768, Transfer Time (s): 0.000108190, Bandwidth (GB/s): 0.282073040 Transfer size (B): 65536, Transfer Time (s): 0.000108950, Bandwidth (GB/s): 0.560210946 Transfer size (B): 131072, Transfer Time (s): 0.000111772, Bandwidth (GB/s): 1.092132093 Transfer size (B): 262144, Transfer Time (s): 0.000131579, Bandwidth (GB/s): 1.855467162 Transfer size (B): 524288, Transfer Time (s): 0.000828882, Bandwidth (GB/s): 0.589084209 Transfer size (B): 1048576, Transfer Time (s): 0.000294777, Bandwidth (GB/s): 3.312890393 Transfer size (B): 2097152, Transfer Time (s): 0.000863627, Bandwidth (GB/s): 2.261537311 Transfer size (B): 4194304, Transfer Time (s): 0.000297197, Bandwidth (GB/s): 13.143621926 Transfer size (B): 8388608, Transfer Time (s): 0.000483843, Bandwidth (GB/s): 16.146768619 Transfer size (B): 16777216, Transfer Time (s): 0.001428390, Bandwidth (GB/s): 10.938886329 Transfer size (B): 33554432, Transfer Time (s): 0.001944343, Bandwidth (GB/s): 16.072270060 Transfer size (B): 67108864, Transfer Time (s): 0.004310126, Bandwidth (GB/s): 14.500735198 Transfer size (B): 134217728, Transfer Time (s): 0.007852202, Bandwidth (GB/s): 15.919101115 Transfer size (B): 268435456, Transfer Time (s): 0.014746705, Bandwidth (GB/s): 16.952939254 Transfer size (B): 536870912, Transfer Time (s): 0.028526683, Bandwidth (GB/s): 17.527449602 Transfer size (B): 1073741824, Transfer Time (s): 0.058527615, Bandwidth (GB/s): 17.085951704

Example 2

To use HDF5 in a custom image, the user is required to only append source and build instructions on top of the base NERSC provided image as shown:

```dockerfile

Containerfile for HDF5 using NERSC cuda-mpich base image

FROM docker.io/nersc/base_cuda_mpich:11.8x4.2.2 WORKDIR /opt ENV DEBIAN_FRONTEND noninteractive ENV CUDA_INSTALL_PATH “/usr/local/cuda-11.8/targets/x86_64-linux” ENV MPI_HOME “/opt/mpich/install”

Install HDF5

WORKDIR /opt RUN git clone -b hdf5_1_14_3 https://github.com/HDFGroup/hdf5.git hdf5
RUN cd hdf5 && \ mkdir build && \ cd build && \ cmake -G “Unix Makefiles” -DCMAKE_C_COMPILER=mpicc -DCMAKE_CXX_COMPILER=mpicxx \ -DCMAKE_Fortran_COMPILER=mpif90 \ -DCMAKE_BUILD_TYPE:STRING=Release -DBUILD_SHARED_LIBS:BOOL=ON \ -DBUILD_TESTING:BOOL=ON -DHDF5_BUILD_TOOLS:BOOL=ON -DHDF5_BUILD_FORTRAN:BOOL=ON \ -DHDF5_ENABLE_PARALLEL=ON -D CMAKE_INSTALL_PREFIX=/opt/hdf5/install ../ && \ cmake –build . –config Release && \ cpack -C Release CPackConfig.cmake && \ make -j 4 && \ make install ENV PATH=$PATH:/opt/hdf5/install/bin ENV PATH=$PATH:/opt/hdf5/install/lib ENV PATH=$PATH:/opt/hdf5/install/include ENV PATH=$PATH:/opt/hdf5/install/share ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/hdf5/install/lib ```

!!! info “Please help us improve this page” Users are invited to contribute helpful information and corrections through our GitLab repository.