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:
Super simple Docker and Apptainer images.
Conda environments in Docker and Apptainer.
Apptainer CNN model with PyTorch and GPU support.
Apptainer MPI distributed execution across multiple compute nodes on Eureka2.
Debugging code inside an Apptainer container directly from your local VS Code IDE.
Building Container Images#
There are several options available for building container images, depending on your workflow and access:
Build the image locally (using Docker or Apptainer), then transfer the resulting native or Docker-converted .sif file to the cluster.
Build the image directly on the cluster using interactive session on one of the compute nodes.
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:
# 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"]
import sys
name = sys.argv[1] if len(sys.argv) > 1 else "world"
print(f"Hello, {name}! This is Python running inside Docker.")
# 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.
Apptainer builds images from a definition file (.def), a text file listing the steps needed to assemble a container image.
Official docs: https://apptainer.org/docs/
Following is the same docker simple Python-in-a-container example written in Apptainer def. file:
Bootstrap: docker
From: python:3.12-slim
%files
app.py /app/app.py
%environment
export PYTHONUNBUFFERED=1
%runscript
exec python /app/app.py "$@"
import sys
name = sys.argv[1] if len(sys.argv) > 1 else "world"
print(f"Hello, {name}! This is Python running inside Apptainer.")
# Build the image (produces hello-python.sif)
apptainer build hello-python.sif hello-python.def
# Run it
apptainer run hello-python.sif
# Hello, world! This is Python running inside Apptainer.
# Run it with a parameter
apptainer run hello-python.sif container
# Hello, container! This is Python running inside Apptainer.
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:
On the main GitLab page (after login), click the + icon next to your avatar and choose New project/repository.
Click on Create from template.
Open the Group tab and navigate to Surrey Shared Containers / templates.
Choose the template Docker Image Project Template → includes a Dockerfile you can edit.
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.)
You can now either clone the repository to your local machine and push changes with Git, or edit files directly in GitLab (Web IDE).
To build the same docker example image as above, add the same Dockerfile and app.py to the repository.
Commit your changes to the main branch, or create a new branch and merge later; pushing any branch will trigger the pipeline.
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):
Download the image to your local machine:docker pull container-registry.surrey.ac.uk/shared-containers/mydocker Verify the image is pulled by running:docker images Run the image:docker run --rm container-registry.surrey.ac.uk/shared-containers/mydockerUsing Apptainer (on your local machine or on the cluster):
Download and convert the image to a .sif file:apptainer pull docker://container-registry.surrey.ac.uk/shared-containers/mydocker Verify the image is pulled by listing the file:ls mydocker.sif Run the image:apptainer run mydocker.sif
To create an Apptainer image using GitLab CI/CD, follow these steps:
On the main GitLab page (after login), click the + icon next to your avatar and choose New project/repository.
Click on Create from template.
Open the Group tab and navigate to Surrey Shared Containers / templates.
Select the template Apptainer Image Project Template → includes an example .def file you can edit.
Choose a name for your new repository and set the visibility (public, internal or private).
The new repository already contains a .gitlab-ci.yml that defines a pipeline to build the Apptainer image.
You can now either clone the repository to your local machine and push changes with Git, or edit files directly in GitLab (Web IDE).
To build the same Apptainer example image as above, edit the content of ‘container.def’ with the above example myapp.def and add app.py to the repository.
Commit your changes to the main branch, or create a new branch and merge later; pushing any branch will trigger the pipeline.
The .gitlab-ci.yml will automatically build the Apptainer image and push it to the Container Registry when you commit changes to the repository.
Monitor the build and check for errors by reviewing it under Build / Pipeline.
To pull and use the Apptainer image (e.g. myapptainer) (on your local machine or on the cluster):
Download the image to your local machine or cluster:apptainer pull oras://container-registry.surrey.ac.uk/shared-containers/myapptainer:latest Verify the image is pulled by listing the file:ls myapptainer_latest.sif Run the image:apptainer run myapptainer_latest.siforapptainer exec myapptainer_latest.sif python app.py 'Container!'# Hello, Container! This is Python running inside Apptainer.# To use exec the app.py needs to be available in the current directory where the .sif file is located.
Docker / Apptainer commands cheat sheet#
docker pull <dockerhub_image>docker build <imageID>docker run <containerID>docker exec <containerID> <user-defined-parameters>docker run -it <containerID>Dockerfileapptainer pull docker://<dockerhub_image>apptainer build <example>.sif <example>.defapptainer run <example>.sifapptainer exec <image>.sif <command> [args]apptainer shell <example>.sif<image>.def (Definition file)More information on Container Registry is available at Container registry
Guide on how to tag container images, see Tagging container images