Containerisation: Docker & Apptainer#

What are containers?#

Containers are a technology used to package and run software in a lightweight, portable, and consistent manner across different computing environments. They encapsulate an application along with all its dependencies, including libraries, tools, configuration files, and runtime into a single unit known as a container image.

When this image is run as a container, it behaves as if the software is being executed in its native environment, regardless of where the container is deployed. This means that containers allow developers and researchers to build once and run anywhere, whether on a personal laptop, a public or private cloud, a remote server, or a high-performance computing (HPC) cluster.

Container Platforms#

Container platforms like Docker and Apptainer (formerly known as Singularity) provide the tools and runtime environment to create, manage, and execute containers. While Docker is widely used in industry and development environments, Apptainer is designed specifically with scientific computing and HPC in mind, offering features like user-level execution (no root privileges required to run containers) and native integration with job schedulers..

Key Benefits of Using Containers#

  • Portability: Because all dependencies are packaged within the container, the same container image can run identically on any system with a compatible container runtime. This reduces the traditional “it works on my machine” problem.

  • Reproducibility: Scientific workflows and analyses can be fully encapsulated in containers, ensuring that experiments can be repeated with the exact same software environment after many years.

  • Compatibility: Containers isolate the application from the underlying host system, avoiding conflicts with system-installed software or libraries.

  • Efficiency: Unlike virtual machines, containers share the host operating system kernel, making them more lightweight, faster to start, and less resource-intensive.

  • Version Control and Sharing: Container images can be versioned and shared through container registries like Docker Hub or Apptainer libraries, promoting collaboration and easier dissemination of research tools.

More information specific to Apptainer is available within the Apptainer Guide .

How to Run a Job in a Container on Slurm Cluster#

Because of its security and HPC-friendly design, Apptainer is the only container platform supported on all University clusters (Eureka2 and AISurrey).

To run a containerised job you must either:

  • Build a native Apptainer image (.sif), or

  • Convert an existing Docker image to Apptainer.

Good news: Apptainer is fully compatible with Docker and it can automatically convert any Docker image into a .sif file.

Note

Several step-by-step examples of building Docker and Apptainer images — for running locally or on the university clusters — are available on GitLab.

Available examples include:

  1. Super simple Docker and Apptainer images.

  2. Conda environments in Docker and Apptainer.

  3. Apptainer CNN model with PyTorch and GPU support.

  4. Apptainer MPI distributed execution across multiple compute nodes on Eureka2.

  5. Debugging code inside an Apptainer container directly from your local VS Code IDE.

Docker & Apptainer Examples

Building Container Images#

There are several options available for building container images, depending on your workflow and access:

  1. Build the image locally (using Docker or Apptainer), then transfer the resulting native or Docker-converted .sif file to the cluster.

  2. Build the image directly on the cluster using interactive session on one of the compute nodes.

  3. Use GitLab CI/CD to build the image, which can be automatically pushed to the University’s container registry and accessed from the clusters.

Note

Only native Apptainer images can be built directly on the cluster—Docker is not supported.

An example for running a single-node (multi-GPU) containerised job on the AISurrey cluster is available in the form of Running a Multi-GPU job in Apptainer. Also refer to Quick Getting Started Guide for more information.

An example for running a multi-node (MPI) containerised job on the Eureka2 cluster will be coming soon however, at the time of writing this, majority of Eureka2 jobs are still bare-metal (i.e. non-containerised) jobs.

1. Docker / Apptainer Local Build#

Docker builds images from a Dockerfile a text file listing the commands needed to assemble an image. Official reference: https://docs.docker.com/engine/reference/builder/

Build command reference: https://docs.docker.com/engine/reference/commandline/build/

Following is a simple Python-in-a-container example:

Example Dockerfile#
  # Small Python base image
  FROM python:3.12-slim

  # Set a working directory (optional but tidy)
  WORKDIR /app

  # Copy the script into the image
  COPY app.py .

  # Default command: run the script
  CMD ["python", "app.py"]

Example app.py#
  import sys

  name = sys.argv[1] if len(sys.argv) > 1 else "world"
  print(f"Hello, {name}! This is Python running inside Docker.")

Build & Run#
  # Build the image (from the folder containing Dockerfile and app.py)
  docker build -t hello-python .

  # Run it
  docker run --rm hello-python
  # Hello, world! This is Python running inside Docker.

  # Run it with a parameter
  docker run --rm hello-python container
  # Hello, container! This is Python running inside Docker.

2. Apptainer Cluster Build#

Since Apptainer is the only container tool available on the clusters, you can build custom Apptainer images on the cluster, but not Docker images.

To prevent users from building images on the login node, and overwhelming it with resource-intensive tasks, the apptainer command is available only on the compute nodes. Start an interactive session on a compute node using srun, then follow the example in point 1 above to build and run the Apptainer image.

Note

.sif files can be quite large, and saving them in your home directory can quickly exhaust your quota. It is recommended to build the image using GitLab as in the following point (3.) which automatically store the image in the University’s container registry.

3. GitLab CI/CD Build#

You can use GitLab CI/CD to build your container images and push them to the University’s container registry. This allows you to automate the build process and manage your images more efficiently. To do this, you will need to create a .gitlab-ci.yml file in your GitLab repository that defines the build process.

Luckely there are templates available for building both docker and apptainer images in the University’s GitLab repository that you can use as a starting point.

To create a Docker image using GitLab CI/CD, you can use the following steps:

  1. On the main GitLab page (after login), click the + icon next to your avatar and choose New project/repository.

  2. Click on Create from template.

  3. Open the Group tab and navigate to Surrey Shared Containers / templates.

  4. Choose the template Docker Image Project Template → includes a Dockerfile you can edit.

  5. The new repository already contains a .gitlab-ci.yml that defines a pipeline to build the Docker image and push it to the Container Registry. (You can review it under Build / Pipeline.)

  6. You can now either clone the repository to your local machine and push changes with Git, or edit files directly in GitLab (Web IDE).

  7. To build the same docker example image as above, add the same Dockerfile and app.py to the repository.

  8. Commit your changes to the main branch, or create a new branch and merge later; pushing any branch will trigger the pipeline.

  9. The .gitlab-ci.yml will automatically build the Docker image and push it to the Container Registry when you commit changes to the repository.

To pull and use the docker image (e.g. mydocker):

Using docker (on your local machine since docker is not available on the clusters):

  1. Download the image to your local machine:
    docker pull container-registry.surrey.ac.uk/shared-containers/mydocker
  2. Verify the image is pulled by running:
    docker images
  3. Run the image:
    docker run --rm container-registry.surrey.ac.uk/shared-containers/mydocker

Using Apptainer (on your local machine or on the cluster):

  1. Download and convert the image to a .sif file:
    apptainer pull docker://container-registry.surrey.ac.uk/shared-containers/mydocker
  2. Verify the image is pulled by listing the file:
    ls mydocker.sif
  3. Run the image:
    apptainer run mydocker.sif

Docker / Apptainer commands cheat sheet#

docker pull <dockerhub_image>
Download an docker image from a registry (Docker Hub by default) to your machine. e.g.,
docker pull python:3.12-slim
docker build <imageID>
Build new container from a Dockerfile. The Dockerfile must be in the current directory. e.g.,
docker build -t mydocker .
docker run <containerID>
Create and run a new container from an image. To run your code or a one-off command in an isolated environment. e.g.,
docker run mydocker
docker exec <containerID> <user-defined-parameters>
Run container (with user-defined run-time parameters)
Run a command inside an already running container.(no new container)
docker run -dit –name mypython python:3.12-slim # Start a container in background named mypython
docker exec -it mypython python # Attach to the background container and start a python shell
docker run -it <containerID>
Drop straight into an interactive shell. Exploration or debugging of an image’s filesystem and tools. e.g.,
docker run -it python:3.12-slim /bin/bash
Dockerfile
Text file used to build container

More information on Container Registry is available at Container registry

Guide on how to tag container images, see Tagging container images