What is Singularity?

Singularity is an open-source container system that is widely used on HPC systems, including several machines at the Texas Advanced Computing Center (TACC). Rather than installing dependencies and compiling code for each unique system, Singularity containers are portable packages that bundle your code and dependencies together. As of the writing of this page, Singularity containers only run on Linux/Unix platforms, with a MacOS beta.

In addition, the Singularity build process creates a portable Singularity Image Format (sif) file that creates a snapshot of your code in time. You can upload this image to the Singularity Cloud Library for others to use, or keep a sif with experiment files so an experiment is always reproducible even if your source code has been modified.

Singularity vs. Docker

Singularity is preferred on HPC systems because Docker essentially gives superuser privileges, which is not desirable on a multiuser system. In addition, Singularity containers can be incorporated into SLURM scheduling, which is significantly more difficult to do with the Docker daemon.

However, Singularity is compatible with many Docker containers. You can use a Docker container as a base in a Singularity definition file (as we do in our example), or you can build a Singularity container directly from a Docker image pulled from Docker Hub.

The Singularity Definition File
The Singularity definition file is similar to a Dockerfile. It provides build instructions for a Singularity container.
Follow Along On Github
This section will take you through container_definitions/singularity_serial.def on the Container Example GitHub repository.
The Singularity Definition Header
The Singularity definition header specifies the base image and operating system to use for the container. A base image can either be a docker image, as shown in the example below, or another Singularity image.
Boostrap: docker From: ubuntu:20.04
In this example, we use the ubuntu:20.04 docker container as the base image and operating system.
Copying Files
Often, building your image will involve copying in source code, build scripts and other files. You can copy files from the current directory or any path you have bound with %files.
%files src/main.cpp /src/main.cpp CMakeLists.txt
In this section, src/main.cpp from the host's current directory is copied to /src/main.cpp in the container and CMakeLists.txt is copied to /CMakeLists.txt in the container.
Installing Dependencies and Code
Now we need to install dependencies and our source code in the container using the %post section.
%post export TZ="America/Chicago" ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone apt-get update && apt-get -y install libhdf5-dev libeigen3-dev cmake g++ mkdir build && cd build && cmake .. && make && make install && cd .. apt-get remove -y libhdf5-dev libeigen3-dev cmake g++
Lets tackle this one (or two) lines at a time. The first two lines set the timezone for the container, which is required by some packages (see this blog post for more information).
You only need to do this if a package requires tzdata to be installed!
export TZ="America/Chicago" ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
The next line installs the necessary packages, libhdf5-dev (HDF5), libeigen3-dev (Eigen), cmake (build tool Cmake), and g++ (C++ compiler), using the apt package manager on Ubuntu. If you are using a different Linux base, you might use another package manager instead.
apt-get update && apt-get -y install libhdf5-dev libeigen3-dev cmake g++
Always execute apt-get update first and add -y after apt-get in order to respond "yes" to any installation confirmations.
Finally, we can install our random_matrix code (from the GitHub Example Repository).
mkdir build && cd build && cmake .. && make && make install && cd ..
The command mkdir build && cd build creates a build directory and changes directory into it, cmake .. reads CMakeLists.txt from the previous directory and creates the build files in /build, make && make install builds the random_matrix executable and installs it into /usr/bin, and then cd .. moves back to the parent directory.

Finally, we need to clean up the unnecessary build requirements to make our image size samller:
apt-get remove -y libhdf5-dev libeigen3-dev cmake g++ && rm -r build && rm CMakeLists.txt && rm -r src
Label Section
Singularity containers can be embedded with informational labels. In our example, we have the following labels:
%labels Author Georgia Stuart, Computational Hydraulics Group, UT Austin Year 2021 License MIT
We see three labels: Author, Year, and License. Once the container is built as random_matrix.sif, we can access the labels with
singularity inspect random_matrix.sif
Help Section
The help section inscribes a help string into the container that can be run with:
singularity run-help random_matrix.sif
In our example, the help section is:
%help This is a container to demonstrate building singularity and docker containers. It contains one main executable which takes one or two arguments: random_matrix m n creates an mxn matrix with random numbers and saves it to matrix.hdf5 random_matrix n creates an nxn matrix with random numbers and saves it to matrix.hdf5
Other Sections in the Definition File
Other sections that can be defined in the Singularity definition file include %startscript, %runscript, and %environment. Descriptions of these sections can be found in the Singularity documentation.
Building Singularity Containers
Full build instructions can be found in the Singularity build documentation. This tutorial will take you through a basic build of the random_matrix code found in the tutorial GitHub repository.
Pre-built singularity containers can be downloaded from Singularity Cloud or Docker Hub with the build command. See the build documentation for more details.
The Basic Build
To build from a definition file, the basic command is:
sudo singularity build <destination.sif> <definition_file.def>
For our specific tutorial, from the root directory of the repository (container-example), run the code
sudo singularity build random_matrix.sif container_definitions/singularity_serial.def
This will create a Singularity Image Format (sif) container file in the current directory.
Warning! Singularity requires root privileges (sudo) in order to build the container. If you're building on a system where you don't have sudo privileges, you may be able to use the --fakeroot option discussed in the containers in HPC section.
Running Singularity Containers
In order to run our random_matrix generating code, we need to execute the container we just built with singularity exec. The following command will create a 20 by 30 random matrix in matrix.hdf5 in the current working directory.
singularity exec random_matrix.sif random_matrix 20 30
Singularity binds $HOME and $PWD by default, which is how it is able to write matrix.hdf5 to the host filesystem outside the container.
Using MPI in Singularity Containers (not for HPC)
We can also install MPI in the container and use MPI functionality for a single machine. This section will refer to the container_definitions/singularity_mpi.def file within the example GitHub repository.
This will not work on multi-node HPC systems. It is intended for use on a single node or computer. For HPC instructions see the HPC section.
To update the container to use MPI, we simply add the mpich package to the apt-get line in %post and change libhdf5-dev to libhdf5-mpich-dev:
apt-get update && apt-get -y install libeigen3-dev cmake g++ mpich libhdf5-mpich-dev
Then we build the container in the same manner as above:
sudo singularity build random_matrix_parallel.sif container_definitions/singularity_mpi.def
And run with the mpirun call after the singularity exec command. The following example uses 2 process (the number following -np).
singularity exec random_matrix_parallel.sif mpirun -np 2 random_matrix 20 30