Using Apptainer
Apptainer is a tool that allows applications to be packaged and run in containers. Apptainer containers can include a simple binary and library stack, or a complicated work flow. The packaged applications are then called Apptainer images. These images are completely portable, with their only dependency requirement being that Apptainer must be running on the target system.
NVIDIA Base Command Manager provides the cm-apptainer package. This allows users to create container images, which are run in containers. The containers can also be used with MPI applications and with different workload managers. The containers run within user space and are always executed as the run-time user (non-root). The application within the container may have access to the filesystem inside or outside the container. The package also provides a local module file called apptainer that must be loaded, before using the apptainer command line utility or running a Apptainer container.
In this chapter several examples are given of how to create container images and start Apptainer containers.
11.1 How To Build A Simple Container Image
The Apptainer image is a single file which physically contains the container filesystem. Apptainer container images can be bootstrapped with a definition file. The definition file describes how the container is to be built. There are several Apptainer keywords (parameters) in the definition file, laid out in lines. If a line does not contain a Apptainer keyword, then the line is treated as a shell script line.
The main keywords in the definition file are:
-
BootStrap: The name of the Linux distribution type module. This informs Apptainer which distribution module should be used to parse the commands in the definition file. At present the following 4 modules are supported:-
yum: Bootstraps distributions such as Red Hat, Centos, and Scientific Linux. -
debootstrap: Bootstraps Debian- and Ubuntu-based distributions. -
arch. Bootstraps Arch Linux. -
docker. Bootstraps Docker. It creates a core operating system image based on an image hosted on a particular Docker Registry server. For thedockermodule, several other keywords may also be defined:-
From: this keyword defines the string of the registry name used for this image in the format[name]:[version]. -
IncludeCmd: use the Docker-defined Cmd as the %runscript, if the Cmd is defined, -
From: sets the docker registry name, -
Token: sets the docker authorization token.
-
-
-
MirrorURL: the URL to get packages from. -
OSVersion: this keyword must be defined as the alphabet-character string associated with the version of the distribution you wish to use. For example:trustyorstable. -
Include: install additional packages. -
%setup: this section blob is a bash scriptlet that is executed on the host outside the container, during bootstrapping. -
%post: this scriptlet section is executed once from inside the container, during bootstrapping. -
%runscript: the scriptlet that is executed inside the container when it is started. -
%test: this section is run at the very end of the boostrapping process and validates the container during the bootstrap process.
A very simple image definition file for Debian 7 (Wheezy) can look like this:
Example
Once the definition file is ready then a user can run the Apptainer command build to create and install the defined operating system into the container image.
Example
The image must be built by a privileged user, even if it is always supposed to be executed with regular user permissions. If, as is the usual case, users are not allowed to use root permissions on a cluster, then they can build the new image on their own computers. The image can then be transfered to the cluster.
The image can be placed in any directory, but it makes sense to share it among compute nodes. The directory /cm/shared/appt-images is therefore a sensible location for keeping the container images.
The image created can then be executed as a regular binary:
Example
The following example shows an image definition file that adds the /etc/services file and /bin/grep binary from the filesystem of the host, to the container image. When a user runs the image that is created, it greps the services file for arguments passed to the image:
Example
The container can now be run as if it were a simple script. If a string is passed as an argument, the /etc/services file that is packaged inside the container is searched for the string:
Example
11.2 Using MPI
MPI applications can be run from a Apptainer container according to two models:
-
the Hybrid model: this relies on both the host and the container to provide MPI implementations for the application that is to run. (This is sometimes also called the Host MPI model).
-
the Bind model: this relies on only the host MPI implementation. The container itself has no MPI implementation, but bind mounts the host MPI implementation into the container, which tends to keep the container lighter.
This is discussed in the upstream Apptainer MPI documentation at https://apptainer.org/docs/user/latest/mpi.html.
BCM administrators typically keep the system software synchronized at the same version across all nodes, and use /cm/shared/ and /home as a shared directory. Containers placed on the nodes therefore usually make use of the shared, synchronized software, which means that in BCM, bind mounting is mostly not needed.
A container can be defined and built on the head node for testing purposes. After adding an MPI implementation, the container can be placed onto the node image, and execution can be carried out via a workload manager such as Slurm.
11.2.1 Runnning An MPI Example
Apptainer at the time of writing (December 2024) supports the two main MPI implementations: OpenMPI (https://www.open-mpi.org/), and MPICH (https://www.mpich.org/).
This section shows a simple example of how an MPICH and OpenMPI run can be carried out.
11.2.1.1 Container Definitions
MPICH container definition: An example of a container definition for MPICH is:
The %files section takes care of some binding that is needed for the container to install the extra YUM packages in the %post section.
OpenMPI container definition: Similarly, an example of a container definition for OpenMPI could be:
11.2.1.2 Building The Container
If the directory /cm/shared/appt-images was created earler on, then:
-
For MPICH, a Apptainer image can then be built by the
rootuser with: -
Similarly, for OpenMPI, a Apptainer image can be built by the
rootuser with:
11.2.1.3 Execution With A Container On The Head Node For Testing
A hello world binary can be run.
The hello world example from Chapter 3 was:
This can be compiled by a user (fred in this case), after loading the appropriate MPICH or OpenMPI module:
Example
For an MPICH implemented by the administrator on the regular nodes, the user can run the binary with a machine file (containing node001 and node002 here) with:
Example
For an OpenMPI implemented by the administrator, the corresponding run is:
Example
The --mca btl tcp,self option avoids warnings being displayed when Ethernet is used for MPI. The option can be dropped if InfiniBand has been configured by the cluster administrator.
11.2.1.4 Running A Binary Via MPI From A Apptainer Image
The MPICH or OpenMPI binary can be run using Apptainer with the MPICH or OpenMPI container image on the head node: Running a binary via MPI from an MPICH Apptainer image:
Example
Running a binary via MPI from an OpenMPI Apptainer image:
Example
11.2.1.5 Execution Via Workload Manager On Regular Nodes With Slurm
If the user fred is meant to submit MPI jobs via a workload manager, such as Slurm, then a standard way to execute them is to create a job script (Chapter 7). With Slurm, srun is generally recommended as an optimal way to run MPI tasks, in preference to mpirun.
A simple job script for the MPICH image is:
Example
A similar run with OpenMPI is: