In this article, we’ll be talking about Apptainer. We will look at what Apptainer is, what is does and how to use it. Let’s get into it then. If you have worked with containers and containerization technologies such as Docker and Kubernetes, then you are most familiar with Apptainer. However if you haven’t, I’ve got your back. Apptainer is a container management platform designed for easier deployment and management of containerized applications. It is a platform that enables users to manage and orchestrate containers in a distributed environment. It also offers a user-friendly interface which allows users to manage containers from a single dashboard. It offers a comprehensive solution to big companies and organizations that use containers in deployment of their applications.
The platform offers a number of features to make container administration simple and supports a variety of containerization platforms, including Docker, Kubernetes, and others. Apptainer helps businesses deploy and run containerized applications across multiple environments, including cloud, on-premise, and hybrid environments. It provides a simple, intuitive interface for deploying containers, managing container networks, and scaling applications up or down based on demand.
Let’s have a look at some of the features of Apptainer.
- Compatibility : Apptainer is 100% compatible with Docker and depends on it for full functionality. It is also compatible with Kubernetes, which is a major containerization platform.
- Absolute Portability: From workstations to HPC to the edge, you can construct, transfer, and archive your workload in a repeatable manner using the single-file SIF container format.
- Encryption: For the security of the apps, models, and data, Apptainer can encrypt containers and interfaces with Vault and other secret management platforms.
- Absolute Trust and Security: Apptainer is the only containerization platform system that enables public/private key signing, offers trust and assurances of immutability.
- Security and compliance: The extensive security measures offered by Apptainer guarantee the safety and protection of container-based applications from potential threats. Additionally, the platform aids companies in meeting legal standards.
- Scalability: Apptainer provides automatic application scalability, enabling organizations to react swiftly to shifting client demands.
- Monitoring and logging: Apptainer offers monitoring and logging features that let companies to keep an eye on container performance, solve problems, and enhance application performance.
Install and Use Apptainer on CentOS 8 Stream / CentOS 9 Stream
Apptainer as a platform supports multiple containerization technologies. These containerization technologies have become popular due to their ability to provide a standardized way of packaging and deploying applications, enabling organizations to build and deploy applications faster and more efficiently. Here, am going to show you how to install Apptainer and use it on CentOS 8 Stream and CentOS 9 Stream. Lets dive into it.
Before installing Apptainer, you need to have Docker and Docker Composed installed on your system. This is because Apptainer is built on top of Docker, and it uses Docker and Docker Composed to build and manage the containers that it creates. When you install Apptainer, it pulls in the necessary dependencies, including Docker and Docker Compose, as part of the installation process.
Step 1: Update the System
Before installing any new package on your system, it’s always a good idea to update your system to the latest packages. To do that, run the following command:
sudo yum check-update
Step 2: Install Docker
We have discussed that docker is a very important component/dependency in the installation and functioning of Apptainer. If you do not have docker installed on your system, you can install it using the repository.
Before you install Docker Engine for the first time on a new host machine, you need to set up the Docker repository. Afterward, you can install and update Docker from the repository.
First, Install the yum-utils package (which provides the yum-config-manager utility) and set up the repository.
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
Then install the Docker engine(latest version) by running the command below:
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
The docker service starts automatically on Debian based distributions, but on RPM based distributions, such as CentOS, Fedora, RHEL or SLES, you need to start it manually using the appropriate systemctl or service command.
Use the commands below to start the Docker daemon and verify that it’s running:
sudo systemctl start docker
sudo systemctl status docker
To make sure it starts at every server boot, run the following command:
sudo systemctl enable docker
Verify that Docker Engine installation is successful by running the hello-world image:
sudo docker run hello-world
This command downloads a test image and runs it in a container. When the container runs, it prints a confirmation message and exits.
[admin@cloudspinx ~]$ sudo docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
Step 3: Install Apptainer
We are going to be installing Apptainer using pre-built packages available for the already released versions. Using this method, there are two ways we can install Apptainer:
- Install RPM from EPEL(Extra Packages for Enterprise Linux)
- Install from GitHub release RPMs
Method 1: Installing RPM from EPEL(Extra Packages for Enterprise Linux)
EPEL (Extra Packages for Enterprise Linux) repositories are a set of additional software packages for Enterprise Linux distributions, including CentOS, RHEL, and Oracle Linux. EPEL is a community-driven project that provides high-quality add-on packages that are not included in the official distribution’s default repositories.
Enable the EPEL repositories by running the following command:
sudo yum install -y epel-release
After enabling the repositories, run the command below to install apptainer:
sudo yum install -y apptainer
Method 2: Install from GitHub release RPMs
Alternatively, x86_64 RPMs are available on GitHub immediately after each Apptainer release and they can be installed directly from there. This command will do the trick for you:
VER=$(curl -s https://api.github.com/repos/apptainer/apptainer/releases/latest|grep tag_name|cut -d '"' -f 4|sed 's/v//')
wget https://github.com/apptainer/apptainer/releases/download/v${VER}/apptainer-${VER}-1.x86_64.rpm
Install it.
sudo yum localinstall ./apptainer-${VER}-1.x86_64.rpm
After successful installation, use the command below to verify the installation:
[admin@cloudspinx ~]$ apptainer version
1.3.4-1.el9
Step 4: Overview of Apptainer
The command line interface for Apptainer enables transparent container building and interaction. Programs can be run inside a container just like they would on your host machine. Within a container, you may quickly redirect IO, utilize pipes, pass arguments, and access files, sockets, and ports on the host system.
Use the help command to get an overview of the Apptainer options and subcommands as follows:
[admin@cloudspinx ~]$ apptainer --help
Linux container platform optimized for High Performance Computing (HPC) and
Enterprise Performance Computing (EPC)
Usage:
apptainer [global options...]
Description:
Apptainer containers provide an application virtualization layer enabling
mobility of compute via both application and environment portability. With
Apptainer one is capable of building a root file system that runs on any
other Linux system where Apptainer is installed.
Options:
--build-config use configuration needed for building containers
-c, --config string specify a configuration file (for root or
unprivileged installation only) (default
"/etc/apptainer/apptainer.conf")
-d, --debug print debugging information (highest verbosity)
-h, --help help for apptainer
--nocolor print without color output (default False)
-q, --quiet suppress normal output
-s, --silent only print errors
-v, --verbose print additional information
--version version for apptainer
Available Commands:
build Build an Apptainer image
cache Manage the local cache
capability Manage Linux capabilities for users and groups
checkpoint Manage container checkpoint state (experimental)
completion Generate the autocompletion script for the specified shell
config Manage various apptainer configuration (root user only)
delete Deletes requested image from the library
exec Run a command within a container
help Help about any command
inspect Show metadata for an image
instance Manage containers running as services
key Manage OpenPGP keys
oci Manage OCI containers
overlay Manage an EXT3 writable overlay image
plugin Manage Apptainer plugins
pull Pull an image from a URI
push Upload image to the provided URI
remote Manage apptainer remote endpoints, keyservers and OCI/Docker registry credentials
run Run the user-defined default command within a container
run-help Show the user-defined help for an image
search Search a Container Library for images
shell Run a shell within a container
sif Manipulate Singularity Image Format (SIF) images
sign Attach digital signature(s) to an image
test Run the user-defined tests within a container
verify Verify cryptographic signatures attached to an image
version Show the version for Apptainer
Examples:
$ apptainer help <command> [<subcommand>]
$ apptainer help build
$ apptainer help instance start
For additional help or support, please visit https://apptainer.org/help/
[admin@cloudspinx ~]$
Apptainer uses positional syntax (i.e. the order of commands and options matters). The primary apptainer command is followed by global options that alter the behavior of all commands. The parameters and arguments for sub-commands come next.
Example:
$ apptainer --debug run docker://alpine
.....DEBUG [U=1000,P=11298] mountGeneric() Remounting /var/apptainer/mnt/session/final
DEBUG [U=1000,P=11298] setPropagationMount() Set RPC mount propagation flag to SLAVE
VERBOSE [U=1000,P=11298] Passwd() Checking for template passwd file: /var/apptainer/mnt/session/rootfs/etc/passwd
VERBOSE [U=1000,P=11298] Passwd() Creating passwd content
VERBOSE [U=1000,P=11298] Passwd() Creating template passwd file and appending user data: /var/apptainer/mnt/session/rootfs/etc/passwd
DEBUG [U=1000,P=11298] addIdentityMount() Adding /etc/passwd to mount list
VERBOSE [U=1000,P=11298] addIdentityMount() Default mount: /etc/passwd:/etc/passwd
VERBOSE [U=1000,P=11298] Group() Checking for template group file: /var/apptainer/mnt/session/rootfs/etc/group
VERBOSE [U=1000,P=11298] Group() Creating group content
DEBUG [U=1000,P=11298] addIdentityMount() Adding /etc/group to mount list
VERBOSE [U=1000,P=11298] addIdentityMount() Default mount: /etc/group:/etc/group
DEBUG [U=1000,P=11298] mountGeneric() Mounting /dev to /var/apptainer/mnt/session/final/dev
DEBUG [U=1000,P=11298] mountGeneric() Mounting /etc/localtime to /var/apptainer/mnt/session/final/etc/localtime
DEBUG [U=1000,P=11298] mountGeneric() Remounting /var/apptainer/mnt/session/final/etc/localtime
DEBUG [U=1000,P=11298] mountGeneric() Mounting /etc/hosts to /var/apptainer/mnt/session/final/etc/hosts
DEBUG [U=1000,P=11298] mountGeneric() Remounting /var/apptainer/mnt/session/final/etc/hosts
DEBUG [U=1000,P=11298] mountGeneric() Mounting /proc to /var/apptainer/mnt/session/final/proc
DEBUG [U=1000,P=11298] mountGeneric() Mounting /sys to /var/apptainer/mnt/session/final/sys
DEBUG [U=1000,P=11298] mountGeneric() Mounting /home/gedion to /var/apptainer/mnt/session/home/gedion
DEBUG [U=1000,P=11298] mountGeneric() Remounting /var/apptainer/mnt/session/home/gedion
DEBUG [U=1000,P=11298] mountGeneric() Mounting /var/apptainer/mnt/session/home/gedion to /var/apptainer/mnt/session/final/home/gedion
DEBUG [U=1000,P=11298] mountGeneric() Mounting /tmp to /var/apptainer/mnt/session/final/tmp
DEBUG [U=1000,P=11298] mountGeneric() Remounting /var/apptainer/mnt/session/final/tmp
DEBUG [U=1000,P=11298] mountGeneric() Mounting /var/tmp to /var/apptainer/mnt/session/final/var/tmp
DEBUG [U=1000,P=11298] mountGeneric() Remounting /var/apptainer/mnt/session/final/var/tmp
DEBUG [U=1000,P=11298] mountGeneric() Mounting /var/apptainer/mnt/session/etc/resolv.conf to /var/apptainer/mnt/session/final/etc/resolv.conf
DEBUG [U=1000,P=11298] mountGeneric() Mounting /var/apptainer/mnt/session/etc/passwd to /var/apptainer/mnt/session/final/etc/passwd
DEBUG [U=1000,P=11298] mountGeneric() Mounting /var/apptainer/mnt/session/etc/group to /var/apptainer/mnt/session/final/etc/group
DEBUG [U=1000,P=11298] addCwdMount() Using /home/gedion as current working directory
VERBOSE [U=1000,P=11298] addCwdMount() /home/gedion found within container
DEBUG [U=1000,P=11298] create() Chroot into /var/apptainer/mnt/session/final
DEBUG [U=1000,P=11360] Chroot() Hold reference to host / directory
DEBUG [U=1000,P=11360] Chroot() Called pivot_root on /var/apptainer/mnt/session/final
DEBUG [U=1000,P=11360] Chroot() Change current directory to host / directory
DEBUG [U=1000,P=11360] Chroot() Apply slave mount propagation for host / directory
DEBUG [U=1000,P=11360] Chroot() Called unmount(/, syscall.MNT_DETACH)
DEBUG [U=1000,P=11360] Chroot() Changing directory to / to avoid getpwd issues
DEBUG [U=1000,P=11298] create() Chdir into / to avoid errors
VERBOSE [U=1000,P=11359] wait_child() rpc server exited with status 0
DEBUG [U=1000,P=11359] init() Set container privileges
DEBUG [U=1000,P=11359] apply_privileges() Effective capabilities: 0x0000000000000000
DEBUG [U=1000,P=11359] apply_privileges() Permitted capabilities: 0x0000000000000000
DEBUG [U=1000,P=11359] apply_privileges() Bounding capabilities: 0x0000000000000000
DEBUG [U=1000,P=11359] apply_privileges() Inheritable capabilities: 0x0000000000000000
DEBUG [U=1000,P=11359] apply_privileges() Ambient capabilities: 0x0000000000000000
DEBUG [U=1000,P=11359] apply_privileges() Set user ID to 1000
DEBUG [U=1000,P=11359] set_parent_death_signal() Set parent death signal to 9
DEBUG [U=1000,P=11359] startup() apptainer runtime engine selected
VERBOSE [U=1000,P=11359] startup() Execute stage 2
DEBUG [U=1000,P=11359] StageTwo() Entering stage 2
DEBUG [U=1000,P=11359] StartProcess() Setting umask in container to 0022
DEBUG [U=1000,P=11359] func1() Not exporting "BASH_FUNC_which%%" to container environment: invalid key
DEBUG [U=1000,P=11359] sylogBuiltin() Sourcing /.singularity.d/env/01-base.sh
DEBUG [U=1000,P=11359] sylogBuiltin() Sourcing /.singularity.d/env/10-docker2singularity.sh
DEBUG [U=1000,P=11359] sylogBuiltin() Sourcing /.singularity.d/env/90-environment.sh
DEBUG [U=1000,P=11359] sylogBuiltin() Sourcing /.singularity.d/env/94-appsbase.sh
DEBUG [U=1000,P=11359] sylogBuiltin() Sourcing /.singularity.d/env/95-apps.sh
DEBUG [U=1000,P=11359] sylogBuiltin() Sourcing /.singularity.d/env/99-base.sh
DEBUG [U=1000,P=11359] sylogBuiltin() Sourcing /.singularity.d/env/99-runtimevars.sh
DEBUG [U=1000,P=11359] sylogBuiltin() Running action command run
DEBUG [U=1000,P=11298] PostStartProcess() Post start process
Apptainer>
These command passes the –debug option to the main apptainer command and runs apptainer with debugging messages on.
Working with images
A container image is a static file with executable code that can create a container on a computing system. It is a core component of a containerized architecture and cannot be changed and can be deployed consistently in any environment.
The Docker or CoreOS container engine, system libraries, utilities, configuration options, and the particular workloads that should run in the container are all included in the container image. The image does not need to include a full operating system because it shares the host’s kernel.
The pull and build commands are commands that can be used to download images from an external resource such as the Oracle Cloud Infrastructure container registry. The OCI registry is an open standards-based, Oracle-managed Docker registry service for securely storing and sharing container images. The well-known Docker Command Line Interface makes it simple for engineers to push and pull Docker images.
You can use pull with the docker:// uri to reference OCI images served from an OCI registry.
[admin@cloudspinx ~]$ apptainer pull docker://alpine
INFO: Using cached SIF image
Also, you can use the build command to download pre-built images from external resources. Note that when using build, you have to specify the name of your container as follows:
[admin@cloudspinx ~]$ apptainer build alpine.sif docker://alpine
INFO: Starting build...
Getting image source signatures
Copying blob 63b65145d645 skipped: already exists
Copying config 6a2bcc1c7b done
Writing manifest to image destination
Storing signatures
2023/03/10 09:55:34 info unpack layer: sha256:63b65145d645c1250c391b2d16ebe53b3747c295ca8ba2fcb6b0cf064a4dc21c
INFO: Creating SIF file...
INFO: Build complete: alpine.sif
[admin@cloudspinx ~]$
Run the command below to pull an image lolcow_latest.sif from ghcr.io:
[admin@cloudspinx ~]$ apptainer pull docker://ghcr.io/apptainer/lolcow
INFO: Converting OCI blobs to SIF format
INFO: Starting build...
Getting image source signatures
Copying blob 5ca731fc36c2 done
Copying blob 16ec32c2132b done
Copying config fd0daa4d89 done
Writing manifest to image destination
Storing signatures
2023/03/10 10:06:03 info unpack layer: sha256:16ec32c2132b43494832a05f2b02f7a822479f8250c173d0ab27b3de78b2f058
2023/03/10 10:06:05 info unpack layer: sha256:5ca731fc36c28789c5ddc3216563e8bfca2ab3ea10347e07554ebba1c953242e
INFO: Creating SIF file...
[admin@cloudspinx ~]$
The Shell Command
The shell command allows you to launch a new and fresh shell inside of your container and interact with it as though it were a tiny virtual machine. The syntax for the apptainer shell command is as follows:
$ apptainer shell [shell options...] <container>
To open a shell in your container, run the shell command together with apptainer command and the name of your container.
[admin@cloudspinx ~]$ apptainer shell lolcow_latest.sif
INFO: underlay of /etc/localtime required more than 50 (77) bind mounts
Apptainer>
When you run the command, you will notice that there is a change in the command prompt, indicating that you have entered the container. In the container, you are the same user as you are on the host system. Run the following command to confirm.
Apptainer> whoami
Apptainer> whoami
admin
Apptainer>
The shell command also works with the docker://, oras://, library://, and shub:// URIs. This creates an ephemeral container that disappears when the shell is exited.
The exec Command
The exec command allows you to execute a custom command within a container by specifying an image file. For instance, lets execute the cowsay command within the lolcow_latest.sif container.
[admin@cloudspinx ~]$ apptainer exec lolcow_latest.sif cowsay "Howdy mate am Cow, what's up?"
INFO: underlay of /etc/localtime required more than 50 (77) bind mounts
_______________________________
< Howdy mate am Cow, what's up? >
-------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
[admin@cloudspinx ~]$
The exec command also works with the docker://, oras://, library://, and shub:// URIs. This creates an ephemeral container that executes a command and disappears. For example lets run the cowsay command using the docker:// URI
[admin@cloudspinx ~]$ apptainer exec docker://ghcr.io/apptainer/lolcow cowsay "Straight from the web"
INFO: Using cached SIF image
INFO: underlay of /etc/localtime required more than 50 (77) bind mounts
_______________________
< Straight from the web >
-----------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
[admin@cloudspinx ~]$
Running a Container
Apptainer containers contain runscripts. These are user-defined scripts that specify the operations a container ought to carry out when it is executed. The run command can be used to start the runscript, or you can just call the container like it were an executable.
Using the run command:
apptainer run lolcow_latest.sif
Running as executable:
[admin@cloudspinx ~]$ apptainer run lolcow_latest.sif
INFO: underlay of /etc/localtime required more than 50 (77) bind mounts
______________________________
< Fri Oct 18 10:18:44 EAT 2024 >
------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
[admin@cloudspinx ~]$ ./lolcow_latest.sif
______________________________
< Fri Oct 18 10:18:09 EAT 2024 >
------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
[admin@cloudspinx ~]$
Arguments can also be passed to the runscript of a container but only if it accepts them. For instance, the default runscript of the docker://alpine container passes any arguments to a shell. For example, we can ask the container to run echo commands in this shell.
[admin@cloudspinx ~]$ apptainer run docker://alpine echo "Hello, how are you doing today?"
INFO: Using cached SIF image
Hello, how are you doing today?
[admin@cloudspinx ~]$
Files in the host system are also reachable from inside the container. For instance, lets echo a phrase into a text file in the home directory of the host system.
echo "This message is from inside the container" > $HOME/containerfile.txt
Now, while inside the container we can concatenate and display the contents of the text file in the host system. To do so, we need to use the cat command while at the same time executing the container. Use the command below:
$ apptainer exec lolcow_latest.sif cat $HOME/containerfile.txt
[admin@cloudspinx ~]$ echo "This message is from inside the container" > $HOME/containerfile.txt
[admin@cloudspinx ~]$ apptainer exec lolcow_latest.sif cat $HOME/containerfile.txt
INFO: underlay of /etc/localtime required more than 50 (77) bind mounts
This message is from inside the container
[admin@cloudspinx ~]$
Creating Containers From Scratch
Apptainer creates images in the Singularity Image File (SIF) format that cannot be changed. This guarantees replicable and verifiable images and offers numerous additional advantages, such as the capability to sign and authenticate your containers. Yet, you might need an image format that is readable for testing and debugging. By using this method, you can install software and dependencies and shell into the image until you are confident that your container will meet your demands. Apptainer supports sandbox(a directory) for such scenarios.
To build into a sandbox (container in a directory) use the build –sandbox command and option:
$ apptainer build --sandbox ubuntu/ docker://ubuntu
[admin@cloudspinx ~]$ apptainer build --sandbox ubuntu/ docker://ubuntu
INFO: Starting build...
Getting image source signatures
Copying blob 76769433fd8a done
Copying config 74f2314a03 done
Writing manifest to image destination
Storing signatures
2024/10/18 10:29:30 info unpack layer: sha256:76769433fd8a87dd77a6ce33db12156b1ea8dad3da3a95e7c9c36a47ec17b24c
INFO: Creating sandbox directory...
INFO: Build complete: ubuntu/
[admin@cloudspinx ~]$
This command creates a directory called ubuntu/ with an entire Ubuntu Operating System and some Apptainer metadata in your current working directory. You can use commands like shell, exec , and run with this directory just as you would with an Apptainer image.
You can create a container from an existing container by using the build command. It can therefore be used to convert a container between different file formats. For example, you can do the following if you already built a sandbox (directory) and wish to convert it to the standard immutable image format (squashfs). But since we have already created an ubuntu directory, we will build from it.
[admin@cloudspinx ~]$ apptainer build new-sif ubuntu
INFO: Starting build...
INFO: Creating SIF file...
INFO: Build complete: new-sif
Apptainer containers can also be build from definition files. Creating a SIF file using apptainer definition files makes the installation of customized software simple. Starting with base images from Docker Hub, you can move on to using images directly from official repositories for operating systems including Ubuntu, Debian, CentOS, Arch, and BusyBox.
In a definition file, there is a header and a body. The header establishes the first base container while the body is further divided into sections that carry out tasks like software installation, environment setup, moving files into the container from host system, etc.
Here is an example of a definition file:
BootStrap: docker
From: ubuntu:22.04
%post
apt-get -y update
apt-get -y install cowsay lolcat
%environment
export LC_ALL=C
export PATH=/usr/games:$PATH
%runscript
date | cowsay | lolcat
%labels
Author Alice
To build a container from the above definition file, you will run build as shown:
$ apptainer build lolcow.sif container_name
Conclusion
Users don’t need to have a lot of knowledge or experience with containerization technologies to manage and deploy their containerized applications using Apptainer. Users don’t have to worry about lengthy and complicated manual procedures while creating and managing containers.
Users can also easily create packages and deploy portable, scalable, and dependable applications. Utimately, Apptainer is a valuable tool for developers and organizations looking to improve their application deployment and management processes.