Content from Introduction
Last updated on 2023-10-09 | Edit this page
Overview
Questions
- “What issues motivated the creation of Apptainer/Singularity?”
- “What are the differences between Docker, Apptainer and Singularity?”
Objectives
- “Learn the design goals behind Apptainer/Singularity.”
Working with containers
Containers are packages of software that encapsulates a system environment. An OS-level virtualization is delivered in a container, and any program running on it will use the contextualization isolated inside the container. They have the advantage that you can build a container on any system, your laptop for example, and then execute it anywhere as far as the platform compatible with the container is available.
Concepts such as reproducibility, preservation, and distribution are important in the HEP community, and the containers provide a solution totally compatible with such concepts:
- The version of some specific software used to perform an analysis can be preserved in a container with exactly the same environment used at that time.
- A “legacy software” with binaries only available for an outdated OS can be executed inside a container.
- All the necessary packages to process data can be easily distributed in containers, independently of the operating system available on the sites.
What about virtual machines?
Virtual Machines (VMs) provide the same isolation and reproducibility. However, they emulate the hardware, so they are computationally heavier to run, require bigger files when distributed and are less flexible than containers, that run only what you require to be different.
Why Apptainer/Singularity?
Many solutions are available to work with containers, for example Docker, one of the most popular platforms. However, the enterprise-based container frameworks were motivated to provide micro-services, a solution that fits well in the models of the industry, where system administrators with root privilege install and run applications, each in its own container. This is not so compatible with the workflow in the High-Performance Computing (HPC) and High Throughput Computing (HTC), in which usually complex applications run exhaustively using all the available resources and without any special privilege.
Apptainer/Singularity is a container platform created for the HPC/HTC use case. It allows to build and run containers with just a few steps in most of the cases, and its design presents key concepts for the scientific community:
- Single-file based container images, facilitating the distribution, archiving and sharing.
- Ability to run, and in modern systems also to be installed, without any root daemon or setuid privileges. This makes it safer for large computer centers with shared resources.
- Preserves the permissions in the environment. The user outside the container can be the same user inside.
- Simple integration with resource managers and distributed computing frameworks because it runs as a regular application.
Docker and Apptainer/Singularity
As you will learn in this training module, Apptainer/Singularity can be used with Docker images. If you want to learn more about docker, check out our docker training.
Apptainer vs Singularity
In these lessons you see the name Apptainer or
Apptainer/Singularity, and the command apptainer
.
As stated in the move
and renaming announcement, “Singularity IS Apptainer”. Currently
there are three products derived from the original Singularity project
from 2015:
- Singularity: commercial software by Sylabs.
- SingularityCE: open source Singularity supported by Sylabs.
- Apptainer: open source Singularity, recently renamed and hosted by the Linux Foundation.
As of Fall 2022 all three Apptainer/Singularity versions are compatible and practically the same, but have different roadmaps. There is hope that in the future they will join forces, but this is not currently the case. To understand how this came to be you can read the Singularity history on Wikipedia.
We are following Apptainer, the most adopted variation in the
scientific community, so we are using the apptainer
command. If you are using Singularity or SingularityCE, just replace the
command apptainer
with singularity
and the
APPTAINER_
and APPTAINERENV_
variable prefixes
with SINGULARITY_
and SINGULARITYENV_
. But
since its previous version was named Singularity and it If you have
older scripts still using the singularity
command they will
work also in Apptainer because it is providing the
singularity
alias and full
compatibility with the previous Singularity environment.
Documentation
The official Apptainer documentation is available online. Contains basic and advanced usage of Apptainer/Singularity beyond the scope of this training document. Take a look and read the nice introduction, explaining the motivation behind the creation of Apptainer/Singularity.
Key Points
- “Apptainer/Singularity is a container platform designed by and for scientists.”
- “Single-file based container images facilitates the distribution.”
- “Secure. User inside the container = user outside.”
Content from Containers and Images
Last updated on 2023-08-14 | Edit this page
Overview
Questions
- “How to pull Apptainer images from the libraries?”
- “How to run commands inside the containers?”
Objectives
- “Learn to search and pull images from the Sylabs Singularity library and Docker Hub.”
- “Interact with the containers using the command-line interface.”
The Apptainer Command Line Interface
Apptainer provides a command-line interface (CLI) to interact with the containers. You can search, build or run containers in a single line.
You can check the version of the Apptainer or Singularity command you
are using with the --version
option:
BASH
singularity --version
# This works for both Singularity and Apptainer, which installs a link named `singularity` to maintain compatibility.
# In the future you may need to use `apptainer --version`
For this training we recommend Apptainer >= 1.0 or Singularity >= 3.5. Older versions may not have some of the features or behave differently. If you need to install or upgrade Apptainer/Singularity please refer to the Setup section. When asking for support please remember to include the version of Apptainer or Singularity being used, as in the output of the above commands.
You can check the available options and subcommands using
--help
:
Downloading Images
Apptainer/Singularity can store, search and retrieve images in registries. Images built by other users can be accessible using the CLI, can be pulled down, and become containers at runtime.
Sylabs, the developers of one Singularity flavor, hosts a public image registry, the Singularity Container Library where many user built images are available.
The Linux Foundation flavor, Apptainer, does not point by default to the Sylab registry as previous versions did. You can change that running these commands (documented here):
OUTPUT
INFO: Remote "SylabsCloud" added.
OUTPUT
INFO: Remote "SylabsCloud" now in use.
OUTPUT
Cloud Services Endpoints
========================
NAME URI ACTIVE GLOBAL EXCLUSIVE
DefaultRemote cloud.apptainer.org NO YES NO
SylabsCloud cloud.sycloud.io YES NO NO
...
Once you have setup a working registry you can use search and pull.
The command search
lists containers of interest and shows
information about groups and collections. For example:
OUTPUT
No users found for 'centos7'
Found 1 collections for 'centos7'
library://shahzebmsiddiqui/easybuild-centos7
Found 15 containers for 'centos7'
library://gmk/default/centos7-devel
Tags: latest
...
Downloading an image from the Container Library is pretty straightforward:
and the image is stored locally as a .sif
file
(centos7-devel_latest.sif
, in this case).
Docker Images
Fortunately, Apptainer is also compatible with Docker images. There
are many more registries with Docker images. Docker Hub is one of the largest
libraries available, and any image hosted on the hub can be easily
downloaded with the docker://
URL as reference:
Running Containers
There are several ways to interact with images and start containers. Here we will review how to initialize a shell environment and how to execute directly a command.
Initializing a shell and exiting it
The shell
command initializes a new interactive shell
inside the container.
OUTPUT
Apptainer>
In this case, the container works as a lightweight virtual machine in which you can execute commands. Remember, inside the container you have the same user and permissions.
OUTPUT
uid=1001(myuser) gid=1001(myuser) groups=1001(myuser),500(myothergroup)
Now quit the container by typing
or hitting Ctrl + D
. Note that when exiting from the
Apptainer image all the running processes are killed (stopped). Changes
saved into bound directories are preserved. By default anything else in
the container is lost (we’ll see later about writable images).
Bound directories
When an outside directory is accessible also inside Apptainer we say
it is bound, or bind mounted. The path to access it may differ
but anything you do to its content outside is visible inside and
vice-versa. By default, Apptainer binds the home of the user,
/tmp
and $PWD
into the container. This means
your files at hostname:~/
are accessible inside the
container. You can specify additional bind mounts using the
--bind
option. For example, let’s say /cvmfs
is available in the host, and you would like to have access to CVMFS
inside the container (here, host refers to the computer/server
that you are running apptainer on). Then let’s do
Here, the colon :
separates the path to the directory on
the host (/cvmfs/
) from the mounting point
(/mnt/
) inside of the container. If you do not have CVMFS,
you can try the command with /opt
, for
example. More information on binding is provided later.
Let’s check that this works:
OUTPUT
Apptainer> ls /mnt/cms.cern.ch
bin etc SITECONF slc7_aarch64_gcc530
bootstrap.sh external slc5_amd64_gcc434 slc7_aarch64_gcc700
...
URLs as input
Each of the different commands to set a container from a local
.sif
also accepts the URL of the image as input. For
example, starting a shell with Scientific Linux 6 is as easy as
OUTPUT
2020/12/17 21:42:46 info unpack layer: sha256:e0a6b33502f39d76f7c70213fa5b91688a46c2217ad9ba7a4d1690d33c6675ef
INFO: Creating SIF file...
Apptainer>
Executing commands
The command exec
starts the container from a specified
image and executes a command inside it. Let’s use the official Docker image of
ROOT to start ROOT inside a container:
OUTPUT
INFO: Converting OCI blobs to SIF format
INFO: Starting build...
------------------------------------------------------------------
| Welcome to ROOT 6.22/06 https://root.cern |
| (c) 1995-2020, The ROOT Team; conception: R. Brun, F. Rademakers |
| Built for linuxx8664gcc on Nov 27 2020, 15:14:08 |
| From tags/v6-22-06@v6-22-06 |
| Try '.help', '.demo', '.license', '.credits', '.quit'/'.q' |
------------------------------------------------------------------
/bin/bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
root [0]
And just like that, ROOT can be used in any laptop, large-scale cluster or grid system with Apptainer available.
Execute Python with PyROOT available
Using the official Docker image of ROOT, start a Python session with PyROOT available.
Key Points
- Use
singularity --version
to know what you are using and to communicate it if asking for support - “A container can be started from a local
.sif
or directly with the URL of the image.” - “Apptainer is also compatible with Docker images, providing access to the large collection of images hosted by Docker Hub.”
- Get a shell inside of your container with
apptainer shell <path/URL to image>
- Execute a command inside of your container with
apptainer exec <path/URL> <command>
- Bind outside directories with
--bind
Content from Coffee break!
Last updated on 2023-08-14 | Edit this page
Overview
Questions
- “Get up, stretch out, and dance!”
Objectives
- “Refresh your mind!”
Content from Building Containers
Last updated on 2023-08-14 | Edit this page
Overview
Questions
- “How to build containers with my requirements?”
Objectives
- “Download and assemble containers from available images in the repositories.”
Running containers from the available public images is not the only
option. In many cases, it is required to modify an image or even to
create a new one from scratch. For such purposes, Apptainer provides the
command build
, defined in the documentation as the
Swiss army knife of container creation.
The usual workflow is to prepare and test a container in a build environment (like your laptop), either with an interactive session or from a definition file, and then to deploy the container into a production environment for execution (as your institutional cluster). Interactive sessions are great to experiment and test your new container. If you want to distribute the container or use it in production, then we recommend to build it from a definition file, as described in the next episode. This ensures the greatest possibility of reproducibility and transparency.
Build a container in an interactive session
While images contained in the .sif
files are more
compact and immutable objects, ideal for reproducibility, for building
and testing images is more convenient the use of a sandbox,
which can be easily modified.
The command build
provides a flag --sandbox
that will create a writable directory, myCentOS7
, in your
work directory:
The container name is myCentOS7
, and it has been
initialized from the official
Docker image of CentOS7. To initialize an interactive session use
the shell
command. And to write files within the sandbox
directory use the --writable
option. Finally, the
installation of new components will require superuser access inside the
container, so use also the --fakeroot
option, unless you
are already root also outside.
OUTPUT
root
Depending on the Apptainer/Singularity installation (privileged or
unprivileged) and the version, you may have some requirements, like the
fakeroot
utility or newuidmap
and
newgidmap
. If you get an error when using
--fakeroot
have a look at the fakeroot
documentation. ::::::::::::::::::::::::::::::::::::::::: callout ##
--fakeroot
is not root ATTENTION! --fakeroot
allows you to be root inside a container that you own but is not
changing who you are outside. All the outside actions and the writing on
bound files and directories will happen as your outside user, even if in
the container is done by root.
::::::::::::::::::::::::::::::::::::::::::::::::::
As an example, let’s create a container with Pythia8 available using
the myCentOS7
sandbox. First, we need to install the
development tools (remember that in this interactive session we are
superuser):
Where yum
is the package manager used
in RHEL distributions (like CentOS).
We will follow the installation steps described in the Pythia website. Here is a summary of the commands you will need (you may need to adjust link and commands if there is a new Pythia version):
BASH
Apptainer> mkdir /opt/pythia && cd /opt/pythia
Apptainer> curl -o pythia8307.tgz https://pythia.org/download/pythia83/pythia8307.tgz
Apptainer> tar xvfz pythia8307.tgz
Apptainer> ln -s pythia8307 pythia8
To enable the Python interface, we add the flag
--with-python-include
during the configuration, pointing to
the location of the Python headers:
BASH
Apptainer> cd pythia8307
Apptainer> ./configure --with-python-include=/usr/include/python3.6m/
Apptainer> make
Apptainer> exit
Now, open an interactive session with your user (no
--fakeroot
). You can use now the container with Pythia8
ready in a few steps. Let’s use the Python interface:
BASH
apptainer shell myCentOS7
Apptainer> export PYTHONPATH=/opt/pythia/pythia8/lib:$PYTHONPATH
Apptainer> export LD_LIBRARY_PATH=/opt/pythia/pythia8/lib:$LD_LIBRARY_PATH
Apptainer> python3
>>> import pythia8
>>> pythia = pythia8.Pythia()
OUTPUT
*------------------------------------------------------------------------------------*
| |
| *------------------------------------------------------------------------------* |
| | | |
| | | |
| | PPP Y Y TTTTT H H III A Welcome to the Lund Monte Carlo! | |
| | P P Y Y T H H I A A This is PYTHIA version 8.307 | |
| | PPP Y T HHHHH I AAAAA Last date of change: 25 Feb 2022 | |
| | P Y T H H I A A | |
| | P Y T H H III A A Now is 03 Nov 2022 at 18:38:40 | |
...
| | Copyright (C) 2022 Torbjorn Sjostrand | |
| | | |
| *------------------------------------------------------------------------------* |
| |
*------------------------------------------------------------------------------------*
Notice that we need to define the environment variables
PYTHONPATH
and LD_LIBRARY_PATH
in order to use
Pythia on Python. We will automate this in the next section.
Execute Python with PyROOT available
Build a container to use uproot in Python 3.9.
Start from the Python 3.9
Docker image and create the myPython
sandbox:
Once inside the container, you can install Uproot.
BASH
Apptainer> python3 -m pip install --upgrade pip
Apptainer> python3 -m pip install uproot awkward
Exit the container and use it as you like:
OUTPUT
Uproot: ROOT I/O in pure Python and NumPy.
...
Notice how we did not need neither --writable
nor
--fakeroot
for the installation, but everything worked fine
since pip installs user packages in the user $HOME directory. In
addition, Apptainer/Singularity by default mounts the user home
directory as read+write, even if the container is read-only. This is why
a sandbox is great to test and experiment locally, but should
not be used for containers that will be shared or deployed. Manual
changes and local directories are difficult to reproduce and control.
Once you are happy with the content, you should use definition files,
described in the next episode.
Key Points
- “The command
build
is the basic tool for the creation of containers.” - “A sandbox is a writable directory where containers can be built interactively.”
- “Superuser permissions are required to build containers if you need to install packages or manipulate the operating system.”
- “Use interactive builds only for development and tests, use definition files for production or publicly distributed containers.”
Content from Containers from definition files
Last updated on 2023-10-09 | Edit this page
Overview
Questions
- “How to easily build and deploy containers from a single definition file?”
Objectives
- “Create a container from a definition file.”
As shown in the previous chapter, building containers with an interactive session may take several steps, and it can become as complicated as the setup is needed. An Apptainer definition file provides an easy way to build and deploy containers.
Hello World Apptainer
The following recipe shows how to build a hello-world container, and run the container on your local computer.
-
Step 1: Open a text editor (e.g., nano, vim, or gedit in a graphical environment)
-
Step 2: Include the following script in the
hello-world.def
file to define the environmentBASH
BootStrap: docker From: ubuntu:20.04 %runscript echo "Hello World" # Print Hello world when the image is loaded
In the above script, the first line -
BootStrap: docker
indicates that apptainer will use the docker protocol to retrieve the base OS to start the image. TheFrom: ubuntu:20.04
is given to apptainer to start from a specific image/OS in Docker Hub. Any content within the%runscript
will be written to a file that is executed when one runs the apptainer image. Theecho "Hello World"
command will print theHello World
on the terminal. Finally the#
hash is used to include comments within the definition file. -
Step 3: Build the image
The
hello-world.sif
file specifies the name of the output file that is built when using theapptainer build
command. -
Step 4: Run the image
Deleting Apptainer image
To delete the hello-world Apptainer image, simply delete the
hello-world.sif
file.
apptainer delete
Note that there is also a apptainer delete
command, but
it is to delete an image from a remote library. To learn more about
using remote endpoints and pulling and pushing images from or to
libraries, read Remote
Endpoints and Library API
Registries.
Example of a more elaborated definition file
Let’s look at the structure of the definition file with another example. Let’s prepare a container from an official Ubuntu image, but this time we will install ROOT with RooFit and Python integration.
Following the ROOT instructions to download a pre-compiled binary distribution, the definition file will look like
BootStrap: docker
From: ubuntu:20.04
%post
apt-get update -y
apt-get install wget -y
export DEBIAN_FRONTEND=noninteractive
apt-get install dpkg-dev cmake g++ gcc binutils libx11-dev libxpm-dev \
libxft-dev libxext-dev python libssl-dev libgsl0-dev libtiff-dev -y
cd /opt
wget https://root.cern/download/root_v6.22.06.Linux-ubuntu20-x86_64-gcc9.3.tar.gz
tar -xzvf root_v6.22.06.Linux-ubuntu20-x86_64-gcc9.3.tar.gz
%environment
export PATH=/opt/root/bin:$PATH
export LD_LIBRARY_PATH=/opt/root/lib:$LD_LIBRARY_PATH
export PYTHONPATH=/opt/root/lib
%runscript
python /opt/root/tutorials/roofit/rf101_basics.py
%labels
Author HEPTraining
Version v0.0.1
%help
Example container running the RooFit tutorial and producing the rf101_basics.png image.
The container provides ROOT with RooFit and Python integration running on Ubuntu.
Let’s take a look at the definition file:
- The first two lines define the base image. In this case, the image
ubuntu:20.04
from Docker Hub is used. -
%post
are lines to execute inside the container after the OS has been set. In this example, we are listing the steps that we would follow to install ROOT with a precompiled binary in an interactive session. Notice that the binary used corresponds with the Ubuntu version defined at the second line. -
%environment
is used to define environment variables available inside the container. Here we are setting the env variables required to execute ROOT and PyROOT. - Apptainer containers can be executable.
%runscript
define the actions to take when the container is executed. To illustrate the functionality, we will just run rf101_basics.py from the RooFit tutorial. -
%labels
add custom metadata to the container. -
%help
it is the container documentation: what it is and how to use it. Can be displayed usingapptainer run-help
Save this definition file as rootInUbuntu.def
. To build
the container, just provide the definition file as argument (executing
as superuser):
Then, an interactive shell inside the container can be initialized
with apptainer shell
, or a command executed with
apptainer exec
. A third option is execute the actions
defined inside %runscript
simply by calling the container
as an executable
OUTPUT
RooFit v3.60 -- Developed by Wouter Verkerke and David Kirkby
Copyright (C) 2000-2013 NIKHEF, University of California & Stanford University
All rights reserved, please read http://roofit.sourceforge.net/license.txt
...
PARAMETER CORRELATION COEFFICIENTS
NO. GLOBAL 1 2
1 0.02723 1.000 0.027
2 0.02723 0.027 1.000
[#1] INFO:Minization -- RooMinimizer::optimizeConst: deactivating const optimization
RooRealVar::mean = 1.01746 +/- 0.0300144 L(-10 - 10)
RooRealVar::sigma = 2.9787 +/- 0.0219217 L(0.1 - 10)
Info in <TCanvas::Print>: png file rf101_basics.png has been created
You will find the output file rf101_basics.png
in the
location where the container was executed. If you don’t have a DISPLAY
setup, Root may complain. Ignore the error messages, the image will be
created anyway,
Here we have covered the basics with a few examples focused to HEP software. Check the Apptainer docs to see all the available options and more details related to the container creation.
A few best practices for your containers to make them more usable, portable, and secure:
- Always install packages, programs, data, and files into operating
system locations (e.g. not
/home
,/tmp
, or any other directories that might get commonly binded on). - Document your container. If your runscript doesn’t supply help,
write a
%help
or%apphelp
section. A good container tells the user how to interact with it. - If you require any special environment variables to be defined, add
them to the
%environment
and%appenv
sections of the build recipe. - Files should always be owned by a system account (UID less than 500).
- Ensure that sensitive files like
/etc/passwd
,/etc/group
, and/etc/shadow
do not contain secrets. - Build production containers from a definition file instead of a sandbox that has been manually changed. This ensures the greatest possibility of reproducibility and mitigates the “black box” effect.
Deploying your containers
Keep in mind that, while building a container may be time consuming, the execution can be immediate and anywhere your image is available. Once your container is built with the requirements of your analysis, you can deploy it in a large cluster and execute it as far as Apptainer is available on the site.
Libraries like Sylabs Cloud Library ease the distribution of images. Organizations like OSG provide instructions to use available images and distribute custom images via CVMFS.
Be smart, and this will open endless possibilities in your workflow.
Write a definition file to build a container with Pythia8 available in Python
Following the example of the first section in which a container is built with an interactive session, write a definition file to deploy a container with Pythia8 available.
Take a look at /opt/pythia/pythia8307/examples/main01.py
and define the %runscript
to execute it using
python3
.
(Tip: notice that main01.py requires Makefile.inc
).
BootStrap: docker
From: centos:centos7
%post
yum -y groupinstall 'Development Tools'
yum -y install python3-devel
mkdir /opt/pythia && cd /opt/pythia
curl -o pythia8307.tgz https://pythia.org/download/pythia83/pythia8307.tgz
tar xvfz pythia8307.tgz
cd pythia8307
./configure --with-python-include=/usr/include/python3.6m/
make
%environment
export PYTHONPATH=/opt/pythia/pythia8307/lib:$PYTHONPATH
export LD_LIBRARY_PATH=/opt/pythia/pythia8307/lib:$LD_LIBRARY_PATH
%runscript
cp /opt/pythia/pythia8307/Makefile.inc .
python3 /opt/pythia/pythia8307/examples/main01.py
%labels
Author HEPTraining
Version v0.0.1
%help
Container providing Pythia 8.307. Execute the container to run an example.
Open it in a shell to use the Pythia installation with Python 3.6
Build your container executing
And finally, execute the container to run main01.py
Key Points
- “An Apptainer definition file provides an easy way to build and deploy containers.”
Content from Coffee break!
Last updated on 2023-08-14 | Edit this page
Overview
Questions
- “Get up, stretch out, and dance!”
Objectives
- “Refresh your mind!”
Content from Sharing files between host and container
Last updated on 2023-10-09 | Edit this page
Overview
Questions
- “How to read and write files on the host system from within the container?”
Objectives
- “Map directories on your host system to directories within your container.”
- “Learn about the bind paths included automatically in all containers.”
One of the key features about containers is the isolation of the processes running inside them. It means, files on the host system are not accessible within the container. However, it is very common that some files on the host system are needed inside the container, or you want to write files from the container to some directory in the host.
We have already used the option --bind
earlier in the
module when exploring the options available to run Apptainer containers.
In this chapter we will explore further options to bind directories from
your host system to directories within your container.
Remember that in Apptainer, your user outside is the same inside the container (except when using fakeroot). And the same happens with permissions and ownership for files in bind directories.
Bind paths included by default
For each container executed, Apptainer binds automatically some directories by default, and other defined by the system admin in the Apptainer configuration. By default, Apptainer binds:
- The user’s home directory ($HOME)
- The current directory when the container is executed ($PWD)
- System-defined paths:
/tmp
,/proc
,/dev
, etc.
Since this is defined in the configuration, it may vary from site to site.
Let’s use for example the container built during the last chapter
called rootInUbuntu.sif
. Take a look at your current
directory
OUTPUT
/home/myuser/somedirectory
Open a shell inside the container and try to use pwd
again
OUTPUT
/home/myuser/somedirectory
you will notice that the files stored on the host are located inside
the container! As we explained above, Apptainer mounts automatically
your $HOME
inside the container.
Try this time with
and you will notice that $HOME
is not mounted
anymore
OUTPUT
ls: cannot access '/home/myuser': No such file or directory
User-defined bind paths
Apptainer provides mechanisms to specify additional binds when executing a container via command-line or environment variables. Apptainer offers a complex set of mechanism for binds or other mounts. Here we present the main points, refer to the Bind Paths and Mounts documentation for more.
Bind with command-line options
The command-line option --bind (-B)
will specify the
directories that must be linked between the host and the container. It
is available for run
, exec
and
shell
(as well for instance
that is not
covered yet).
The syntax for using the bind option is
"source:destination"
, and the paths must be absolute
(relative paths will be rejected). For example, let’s create a directory
in the host containing a constant that can be useful for your
analysis
It is very, very important in your analysis workflow to know the mass
of the muon, right? It may have sense to put the data in a high-level
directory within the container, like /data
This will bind the directory mydata/
from the host as
/data
inside the container:
OUTPUT
-rw-rw-r-- 1 myuser myuser 20 Jan 2 12:46 muonMass.txt
Now you can use the mass of the muon from a root-level directory!
If multiple directories must be available in the container, you can
repeat the option or they can be defined with a comma between each pair
of directories, i.e. using the syntax
source1:destination1,source2:destination2
.
Also. If the destination is not specified, it will be set as equal as the source. For example
Will mount /cvmfs
inside the container. Try it!
Binding directories with Docker-like syntax
using --mount
The flag –mount provides a method to bind directories using the
syntax of Docker. The bind is specified with the format
type=bind,src=<source>,dst=<dest>
. Currently,
only type=bind
is supported. Check the documentation
for additional options available.
Bind with environment variables
If the environment variable $APPTAINER_BIND
is defined,
apptainer will bind inside ANY container the directories specified in
the format source
, with the destination being optional (in
the same way as using --bind
). For example:
will bind CVMFS to all your Apptainer containers (/cvmfs
must be available in the host, of course).
You can also bind multiple directories using commas between each
source:destination
.
Key Points
- “Bind mounts allow reading and writing files within the container.”
- “In Apptainer, you have same owner and permissions for files inside and outside the container.”
- “Some paths are mounted by default by Apptainer.”
- “Additional directories to bind can be defined using the
--bind
option or the environment variable$SINGULARITY_BIND
.”
Content from Apptainer/Singularity instances
Last updated on 2023-08-14 | Edit this page
Overview
Questions
- “How can I keep my container running in the background?”
- “What are the use cases for instances?”
Objectives
- “Run containers in a detached mode to keep services up.”
- “Deploy instances via definition files.”
As we have studied in previous chapters, commands such as
run
and shell
allocate Apptainer/Singularity
containers in the foreground, stopping any process running inside the
container after logout. This behavior suits the use case of containers
for executing interactive commands in a well-defined environment, but
there are cases when running processes in the background is convenient.
For example, when a web application like a Jupyter notebook is deployed
inside a container, it is desired to keep the container up while it
waits for connections from the web browser.
Apptainer provides the concept of instances to deploy services in the background. While Docker is a common choice of tool for setting services, Apptainer has the advantage of working without requiring any special permissions (like when you are working in a cluster provided by your university/laboratory). In this chapter we will learn the basics about their capabilities and some use cases as examples.
Instances from image files
To start an instance, Apptainer provides the command
instance
. To exemplify, let’s pull the CentOS image used in
previous chapters
The image must be started in the following way:
In this example, the .sif
is the image downloaded from
Dockerhub, and mycentos7
is the name that we have assigned
to the instance. Instead of opening a shell session or executing a
command, the container is running in the background.
Confirm that the instance is running using the
instance list
command
OUTPUT
INSTANCE NAME PID IP IMAGE
mycentos7 10782 /home/myuser/centos_centos7.sif
To interact with the instance, the commands exec
and
shell
are available. The instance must be referred as
instance://name
. For example, to open a shell inside the
CentOS instance:
Remember that exiting the shell instance will not stop the container.
For doing so, use instance stop
:
You can confirm the instance doesn’t exist with
instance list
.
Instances with bind paths
When starting an instance, the same options for bind directories
between the host and the container as running an interactive session are
available. For example, if you want a directory mounted inside the
instance, use the --bind
option:
binding the directory mydata/
from the host as
/data
inside the instance.
A web server as an instance
One of the main purposes of the Apptainer instances is deploying services with customized environments. Before moving to more complex use cases, let’s start with a basic example: a web service showing a HTML with a message.
Let’s write a basic index.html
file as:
HTML
<!DOCTYPE html>
<html>
<head>
<title>My awesome service</title>
</head>
<body>
<h1>Hello world!</h1>
<p>If you see this page, my awesome service is up and running.</p>
</body>
</html>
If you are not familiar with HTML take a quick look at the HTML Tutorial, but it is not mandatory. What really matters is having a minimal webpage that our server will show.
Now, let’s prepare a basic web server using Python
http.server. Create a definition file, saved as
basicServer.def
, which contains:
Bootstrap: docker
From: ubuntu:20.04
%post
apt-get update -y
apt-get install -y python3.9
%files
index.html /tmp/index.html
%startscript
cd /tmp
python3.9 -m http.server 8850
If you recall the chapter about definition files, this definition
file will pull the official Ubuntu image from Dockerhub, and will
install Python3.9. In addition, it copies index.html
in
/tmp
inside the container. When the
instance starts, commands specified on %startscript
are
executed. On this example, http.server
will be executed,
serving a page in the port 8850 (you can use any other port if 8850 is
busy with another service).
Let’s build an image from the definition. Remember that building
images requires either superuser permissions or using the flag
--fakeroot
as
Now, let’s start an instance named myWebService
with the
image that we just built
Reminder from the previous chapter: with --no-mount tmp
we are asking Apptainer to NOT bind /tmp
from the host to
the instance (it is mounted by default), we use instead an isolated
/tmp
inside the instance where index.html has been
copied.
You can confirm in the terminal that the web service is up using
curl
as
OUTPUT
<!DOCTYPE html>
<html>
<head>
<title>Welcome to my service!</title>
</head>
<body>
<h1>Hello world!</h1>
<p>If you see this page, my awesome service is up and running.</p>
</body>
</html>
If you are executing Apptainer locally, try to open http://localhost:8850.
SSH tunneling
If you are deploying a service in a cluster of your institution (as LXPLUS at CERN) it is likely that you need SSH tunneling for opening pages served by your service with a web browser. A basic port forwarding can be configured as:
where
Then you can open http://localhost:8850 in your machine!
Port numbers less than 1024 are privileged ports and can be used only by root. Please consult the allowed ports and rules with your institution.
Remember to stop the instance once you are done.
Serving a Jupyter notebook with custom environment
As an example of the capabilities of instances as services, let’s extend our definition file to deploy a Jupyter notebook server with a customized environment.
What if we provide a Jupyter notebook ready to use ROOT? If you remember our example from the definition files chapter, at this point it must be almost straightforward:
Bootstrap: docker
From: ubuntu:20.04
%post
apt-get update -y
apt-get install -y python3
apt-get install -y python3-pip
pip install notebook
apt-get install wget -y
export DEBIAN_FRONTEND=noninteractive
apt-get install dpkg-dev cmake g++ gcc binutils libx11-dev libxpm-dev \
libxft-dev libxext-dev python libssl-dev libgsl0-dev libtiff-dev -y
cd /opt
wget https://root.cern/download/root_v6.22.06.Linux-ubuntu20-x86_64-gcc9.3.tar.gz
tar -xzvf root_v6.22.06.Linux-ubuntu20-x86_64-gcc9.3.tar.gz
%environment
export PATH=/opt/root/bin:$PATH
export LD_LIBRARY_PATH=/opt/root/lib:$LD_LIBRARY_PATH
export PYTHONPATH=/opt/root/lib
%startscript
jupyter notebook --port 8850
Save the definition file as jupyterWithROOT.def
, and
let’s build an image called jupyterWithROOT.sif
Now, start an instance named mynotebook
with our
brand-new image
and confirm that the instance is up
OUTPUT
INSTANCE NAME PID IP IMAGE
mynotebook 10720 /home/myuser/jupyterWithROOT.sif
If you go to http://localhost:8850 (with SSH tunneling if needed),
you will find out that for security reasons the Jupyter webapp will ask
for an access token. Fortunately, you can get the token listing the URL
of active servers using the jupyter notebook list
command.
To execute the command inside the instance, use
sigularity exec
:
OUTPUT
Currently running servers:
http://localhost:8850/?token=12asldc9b2084f9b664b39a6246022312bc9c605b :: /home/myHome
Open the URL with the token, and you will be able to see the Jupyter interface. Try to open a new notebook and write in a cell to confirm that ROOT is available:
PYTHON
import ROOT
# Now you can work with PyROOT, creating a histogram for example
h = ROOT.TH1F("myHistogram", "myTitle", 50, -10, 10)
h.FillRandom("gaus", 10000)
c = ROOT.TCanvas("myCanvasName","The Canvas Title",800,600)
h.Draw()
c.Draw()
The bottom line: with any Jupyter notebook that you write, you can provide an Apptainer image that will set the environment required to execute the cells. It doesn’t matter if yourself or someone else comes in one, five, ten years, your code will work independently of the software available in your computer as far as Apptainer/Singularity is available!
A Jupyter notebook with Uproot available
Can you setup a Jupyter notebook server with Uproot available in Apptainer?
Hint: Uproot can be installed using pip
.
## Solution
Bootstrap: docker
From: ubuntu:20.04
%post
apt-get update -y
apt-get install -y python3
apt-get install -y python3-pip
pip install notebook
pip install uproot
%startscript
jupyter notebook --port 8850
Confirm that Uproot is available opening a notebook and executing in a cell
Key Points
- Instances allow to setup services via Apptainer images or definition files.
- Code provided in Jupyter notebooks can be accompanied by a Apptainer/Singularity image with the environment needed for its execution, ensuring the reproducibility of the results.
Content from Bonus Episode: Building and deploying an Apptainer container to Github Packages
Last updated on 2023-10-09 | Edit this page
Overview
Questions
- How to build apptainer container for python packages?
- How to share apptainer images?
Objectives
- To be able to build an apptainer container and share it via GitHub packages
Prerequisites
For this lesson, you will need,
- Knowledge of Git SW Carpentry Git-Novice Lesson
- Knowledge of GitHub CI/CD HSF Github CI/CD Lesson
Apptainer Container for python packages
Python packages can be installed using an Apptainer image. The following example illustrates how to write a definition file for building an image containing python packages.
BootStrap: docker
From: ubuntu:20.04
%post
apt-get update -y
apt-get install wget -y
export DEBIAN_FRONTEND=noninteractive
apt-get install dpkg-dev cmake g++ gcc binutils libx11-dev libxpm-dev \
libxft-dev libxext-dev python3 libssl-dev libgsl0-dev libtiff-dev \
python3-pip -y
pip3 install numpy
pip3 install awkward
pip3 install uproot4
pip3 install particle
pip3 install hepunits
pip3 install matplotlib
pip3 install hist
pip3 install mplhep
pip3 install vector
pip3 install fastjet
pip3 install iminuit
As we see, several packages are installed.
Publish Apptainer images with GitHub Packages and share them!
It is possible to publish apptainer images with GitHub packages. To do so, one needs to use GitHub CI/CD. A step-by-step guide is presented here.
- Step 1: Create a GitHub repository and clone it locally.
-
Step 2: In the empty repository, make a folder
called
.github/workflows
. In this folder we will store the file containing the YAML script for a GitHub workflow, namedapptainer-build-deploy.yml
(the name doesn’t really matter). -
Step 3: In the top directory of your GitHub
repository, create a file named
Apptainer
. - Step 4: Copy-paste the content above and add to the Apptainer file. (In principle it is possible to build this image locally, but we will not do that here, as we wish to build it with GitHub CI/CD).
-
Step 5: In the
apptainer-build-deploy.yml
file, add the following content:
name: Apptainer Build Deploy
on:
pull_request:
push:
branches: master
jobs:
build-test-container:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
container:
image: quay.io/singularity/singularity:v3.8.1
options: --privileged
name: Build Container
steps:
- name: Check out code for the container builds
uses: actions/checkout@v2
- name: Build Container
run: |
singularity build container.sif Apptainer
- name: Login and Deploy Container
run: |
echo ${{ secrets.GITHUB_TOKEN }} | singularity remote login -u ${{ secrets.GHCR_USERNAME }} --password-stdin oras://ghcr.io
singularity push container.sif oras://ghcr.io/${GITHUB_REPOSITORY}:${tag}
The above script is designed to build and publish a Apptainer image with GitHub packages.
- Step 6: Add LICENSE and README as recommended in the SW Carpentry Git-Novice Lesson, and then the repository is good to go.
Key Points
- Python packages can be installed in apptainer images along with ubuntu packages.
- It is possible to publish and share apptainer images over github packages.