Skip to content

Singularity Containers

This page was originally based on the documentation at the University of Sheffield HPC service.

Designed around the notion of mobility of compute and reproducible science, Singularity enables users to have full control of their operating system environment. This means that a non-privileged user can "swap out" the Linux operating system and environment on the host for a Linux OS and environment that they control. So if the host system is running CentOS Linux but your application runs in Ubuntu Linux with a particular software stack, you can create an Ubuntu image, install your software into that image, copy the image to another host (e.g. Cirrus), and run your application on that host in its native Ubuntu environment.

Singularity also allows you to leverage the resources of whatever host you are on. This includes high-speed interconnects (e.g. Infiniband), file systems (e.g. Lustre) and potentially other resources (such as the licensed Intel compilers on Cirrus).

Note

Singularity only supports Linux containers. You cannot create images that use Windows or macOS (this is a restriction of the containerisation model rather than of Singularity).

About Singularity Containers (Images)

Similar to Docker, a Singularity container (or, more commonly, image) is a self-contained software stack. As Singularity does not require a root-level daemon to run its images (as is required by Docker) it is suitable for use on a multi-user HPC system such as Cirrus. Within the container/image, you have exactly the same permissions as you do in a standard login session on the system.

In principle, this means that an image created on your local machine with all your research software installed for local development will also run on Cirrus.

Pre-built images (such as those on DockerHub or SingularityHub) can simply be downloaded and used on Cirrus (or anywhere else Singularity is installed); see use_image_singularity).

Creating and modifying images requires root permission and so must be done on a system where you have such access (in practice, this is usually within a virtual machine on your laptop/workstation); see create_image_singularity.

Using Singularity Images on Cirrus

Singularity images can be used on Cirrus in a number of ways.

  1. Interactively on the login nodes
  2. Interactively on compute nodes
  3. As serial processes within a non-interactive batch script
  4. As parallel processes within a non-interactive batch script

We provide information on each of these scenarios. First, we describe briefly how to get existing images onto Cirrus so that you can use them.

Getting existing images onto Cirrus

Singularity images are simply files, so if you already have an image file, you can use scp to copy the file to Cirrus as you would with any other file.

If you wish to get a file from one of the container image repositories then Singularity allows you to do this from Cirrus itself.

For example, to retrieve an image from SingularityHub on Cirrus we can simply issue a Singularity command to pull the image.

[user@cirrus-login1 ~]$ module load singularity
[user@cirrus-login1 ~]$ singularity pull hello-world.sif shub://vsoch/hello-world

The image located at the shub URI is written to a Singularity Image File (SIF) called hello-world.sif.

Interactive use on the login nodes

The container represented by the image file can be run on the login node like so.

[user@cirrus-login1 ~]$ singularity run hello-world.sif 
RaawwWWWWWRRRR!! Avocado!
[user@cirrus-login1 ~]$

We can also shell into the container.

[user@cirrus-login1 ~]$ singularity shell hello-world.sif
Singularity> ls /
bin  boot  dev  environment  etc  home  lib  lib64  lustre  media  mnt  opt  proc  rawr.sh  root  run  sbin  singularity  srv  sys  tmp  usr  var
Singularity> exit
exit
[user@cirrus-login1 ~]$

For more information see the Singularity documentation.

Interactive use on the compute nodes

The process for using an image interactively on the compute nodes is very similar to that for using them on the login nodes. The only difference is that you first have to submit an interactive serial job to get interactive access to the compute node.

First though, move to a suitable location on /work and re-pull the hello-world image. This step is necessary as the compute nodes do not have access to the /home file system.

[user@cirrus-login1 ~]$ cd ${HOME/home/work}
[user@cirrus-login1 ~]$ singularity pull hello-world.sif shub://vsoch/hello-world

Now reserve a full node to work on interactively by issuing an salloc command, see below.

[user@cirrus-login1 ~]$ salloc --exclusive --nodes=1 \
  --tasks-per-node=36 --cpus-per-task=1 --time=00:20:00 \
  --partition=standard --qos=standard --account=[budget code] 
salloc: Pending job allocation 14507
salloc: job 14507 queued and waiting for resources
salloc: job 14507 has been allocated resources
salloc: Granted job allocation 14507
salloc: Waiting for resource configuration
salloc: Nodes r1i0n8 are ready for job
[user@cirrus-login1 ~]$ ssh r1i0n8

Note the prompt has changed to show you are on a compute node. Once you are logged in to the compute node (you may need to submit your account password), move to a suitable location on /work as before. You can now use the hello-world image in the same way you did on the login node.

[user@r1i0n8 ~]$ cd ${HOME/home/work}
[user@r1i0n8 ~]$ singularity shell hello-world.sif
Singularity> exit
exit
[user@r1i0n8 ~]$ exit
logout
Connection to r1i0n8 closed.
[user@cirrus-login1 ~]$ exit
exit
salloc: Relinquishing job allocation 14507
salloc: Job allocation 14507 has been revoked.
[user@cirrus-login1 ~]$

Note we used exit to leave the interactive container shell and then called exit twice more to close the interactive job on the compute node.

Serial processes within a non-interactive batch script

You can also use Singularity images within a non-interactive batch script as you would any other command. If your image contains a runscript then you can use singularity run to execute the runscript in the job. You can also use singularity exec to execute arbitrary commands (or scripts) within the image.

An example job submission script to run a serial job that executes the runscript within the hello-world.sif we built above on Cirrus would be as follows.

#!/bin/bash --login

# job options (name, compute nodes, job time)
#SBATCH --job-name=hello-world
#SBATCH --ntasks=1
#SBATCH --exclusive
#SBATCH --time=0:20:0
#SBATCH --partition=standard
#SBATCH --qos=standard

# Replace [budget code] below with your project code (e.g. t01)
#SBATCH --account=[budget code]

# Load any required modules
module load singularity

# Run the serial executable
srun --cpu-bind=cores singularity run ${HOME/home/work}/hello-world.sif

Submit this script using the sbatch command and once the job has finished, you should see RaawwWWWWWRRRR!! Avocado! in the Slurm output file.

Parallel processes within a non-interactive batch script

Running a Singularity container on the compute nodes isn't too different from launching a normal parallel application. The submission script below shows that the srun command now contains an additional singularity clause.

#!/bin/bash --login

# job options (name, compute nodes, job time)
#SBATCH --job-name=[name of application]
#SBATCH --nodes=4
#SBATCH --tasks-per-node=36
#SBATCH --cpus-per-task=1
#SBATCH --exclusive
#SBATCH --time=0:20:0
#SBATCH --partition=standard
#SBATCH --qos=standard

# Replace [budget code] below with your project code (e.g. t01)
#SBATCH --account=[budget code]

# Load any required modules
module load mpt
module load singularity

# The host bind paths for the Singularity container.
BIND_ARGS=/work/y07/shared/cirrus-software,/opt/hpe,/etc/libibverbs.d,/path/to/input/files

# The file containing environment variable settings that will allow
# the container to find libraries on the host, e.g., LD_LIBRARY_PATH . 
ENV_PATH=/path/to/container/environment/file

CONTAINER_PATH=/path/to/singularity/image/file

APP_PATH=/path/to/containerized/application/executable
APP_PARAMS=[application parameters]

srun --distribution=block:block --hint=nomultithread \
    singularity exec --bind ${BIND_ARGS} --env-file ${ENV_PATH} ${IMAGE_PATH}
        ${APP_PATH} ${APP_PARAMS}

The script above runs a containerized application such that each of the four nodes requested is fully populated. In general, the containerized application's input and output will be read from and written to a location on the host; hence, it is necessary to pass a suitable bind path to singularity (/path/to/input/files).

Note

The paths in the submission script that begin /path/to should be provided by the user. All but one of these paths are host specific. The exception being APP_PATH, which should be given a path relative to the container file system.

If the Singularity image file was built according to the Bind model, you will need to specify certain paths (--bind) and environment variables (--env-file) that allow the containerized application to find the required MPI libraries.

Otherwise, if the image follows the Hybrid model and so contains its own MPI implementation, you instead need to be sure that the containerized MPI is compatible with the host MPI, the one loaded in the submission script. In the example above, the host MPI is HPE MPT 2.25, but you could also use OpenMPI (with mpirun), either by loading a suitable openmpi module or by referencing the paths to an OpenMPI installation that was built locally (i.e., within your Cirrus work folder).

Creating Your Own Singularity Images

You can create Singularity images by importing from DockerHub or Singularity Hub directly to Cirrus. If you wish to create your own custom image then you must install Singularity on a system where you have root (or administrator) privileges - often your own laptop or workstation.

We provide links below to instructions on how to install Singularity locally and then cover what options you need to include in a Singularity definition file in order to create images that can run on Cirrus and access the software development modules. This can be useful if you want to create a custom environment but still want to compile and link against libraries that you only have access to on Cirrus such as the Intel compilers and HPE MPI libraries.

Installing Singularity on Your Local Machine

You will need Singularity installed on your machine in order to locally run, create and modify images. How you install Singularity on your laptop/workstation depends on the operating system you are using.

If you are using Windows or macOS, the simplest solution is to use Vagrant to give you an easy to use virtual environment with Linux and Singularity installed. The Singularity website has instructions on how to use this method to install Singularity.

If you are using Linux then you can usually install Singularity directly.

Accessing Cirrus Modules from Inside a Container

You may want your custom image to be able to access the modules environment on Cirrus so you can make use of custom software that you cannot access elsewhere. We demonstrate how to do this for a CentOS 7 image but the steps are easily translated for other flavours of Linux.

For the Cirrus modules to be available in your Singularity container you need to ensure that the environment-modules package is installed in your image.

In addition, when you use the container you must invoke access as a login shell to have access to the module commands.

Below, is an example Singularity definition file that builds a CentOS 7 image with access to TCL modules already installed on Cirrus.

BootStrap: docker
From: centos:centos7

%post
    yum update -y
    yum install environment-modules -y
    echo 'module() { eval `/usr/bin/modulecmd bash $*`; }' >> /etc/bashrc
    yum install wget -y
    yum install which -y
    yum install squashfs-tools -y

If we save this definition to a file called centos7.def, we can use the following build command to build the image (remember this command must be run on a system where you have root access, not on Cirrus).

me@my-system:~> sudo singularity build centos7.sif centos7.def

The resulting image file (centos7.sif) can then be copied to Cirrus using scp; such an image already exists on Cirrus and can be found in the /work/y07/shared/cirrus-software/singularity/images folder.

When you use that image interactively on Cirrus you must start with a login shell and also bind /work/y07/shared/cirrus-software so that the container can see all the module files, see below.

[user@cirrus-login1 ~]$ module load singularity
[user@cirrus-login1 ~]$ singularity exec -B /work/y07/shared/cirrus-software \
  /work/y07/shared/cirrus-software/singularity/images/centos7.sif \
    /bin/bash --login
Singularity> module avail intel-*/compilers

--------- /work/y07/shared/cirrus-modulefiles -------------
intel-19.5/compilers  intel-20.4/compilers
Singularity> exit
logout
[user@cirrus-login1 ~]$

Altering a Container on Cirrus

A container image file is immutable but it is possible to alter the image if you convert the file to a sandbox. The sandbox is essentially a directory on the host system that contains the full container file hierarchy.

You first run the singularity build command to perform the conversion followed by a shell command with the --writable option. You are now free to change the files inside the container sandbox.

user@cirrus-login1 ~]$ singularity build --sandbox image.sif.sandbox image.sif
user@cirrus-login1 ~]$ singularity shell -B /work/y07/shared/cirrus-software --writable image.sif.sandbox
Singularity>

In the example above, the /work/y07/shared/cirrus-software bind path is specified, allowing you to build code that links to the Cirrus module libraries.

Finally, once you are finished with the sandbox you can exit and convert back to the original image file.

Singularity> exit
exit
user@cirrus-login1 ~]$ singularity build --force image.sif image.sif.sandbox

Note

Altering a container in this way will cause the associated definition file to be out of step with the current image. Care should be taken to keep a record of the commands that were run within the sandbox so that the image can be reproduced.