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.

Apptainer/Singularity

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:

BASH

apptainer --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):

BASH

apptainer remote add --no-login SylabsCloud cloud.sycloud.io

OUTPUT

INFO:    Remote "SylabsCloud" added.

BASH

apptainer remote use SylabsCloud

OUTPUT

INFO:    Remote "SylabsCloud" now in use.

BASH

apptainer remote list

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:

BASH

# this command can take around a minute to complete
apptainer search centos7

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:

BASH

apptainer pull library://gmk/default/centos7-devel

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:

BASH

apptainer pull docker://centos:centos7

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.

BASH

apptainer shell centos7-devel_latest.sif

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.

BASH

Apptainer> id

OUTPUT

uid=1001(myuser) gid=1001(myuser) groups=1001(myuser),500(myothergroup)

Now quit the container by typing

BASH

Apptainer> exit

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

BASH

apptainer shell --bind /cvmfs:/mnt centos7-devel_latest.sif

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

BASH

apptainer shell docker://sl:6

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:

BASH

apptainer exec docker://rootproject/root root -b

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.

BASH

apptainer exec docker://rootproject/root python3

OUTPUT

INFO:    Using cached SIF image
Python 3.8.5 (default, Jul 28 2020, 12:59:40)
[GCC 9.3.0] on linux
>>> import ROOT
>>> # Now you can work with PyROOT, creating a histogram for example
>>> h = ROOT.TH1F("myHistogram", "myTitle", 50, -10, 10)

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.

Apptainer/Singularity usage workflow
‘Apptainer/Singularity usage workflow’ via Kurtzer GM, Sochat V, Bauer MW (2017) Singularity: Scientific containers for mobility of compute. PLoS ONE 12(5): e0177459. https://doi.org/10.1371/journal.pone.0177459

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:

BASH

apptainer build --sandbox myCentOS7 docker://centos:centos7

Notes on shared file systems like AFS

Avoid using the AFS (Andrew File System) and possibly other shared file systems as sandbox directory, as these systems can lead to permission issues. In particular, this applies to your home directory on lxplus. Instead, make sure to use the local file system by creating a folder in /tmp/: mkdir /tmp/$USER. Then, replace myCentOS7 in the previous (and next) command with /tmp/$USER/myCentOS7.

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.

BASH

apptainer shell --writable --fakeroot myCentOS7
Apptainer> whoami

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):

BASH

Apptainer> yum groupinstall 'Development Tools'
Apptainer> yum install python3-devel

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:

BASH

apptainer build --sandbox myPython docker://python:3.9
apptainer shell myPython

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:

BASH

apptainer exec myPython python -c "import uproot; print(uproot.__doc__)"

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)

    BASH

    nano hello-world.def
  • Step 2: Include the following script in the hello-world.def file to define the environment

    BASH

    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. The From: 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. The echo "Hello World" command will print the Hello World on the terminal. Finally the # hash is used to include comments within the definition file.

  • Step 3: Build the image

    BASH

    apptainer build hello-world.sif hello-world.def

    The hello-world.sif file specifies the name of the output file that is built when using the apptainer build command.

  • Step 4: Run the image

    BASH

    ./hello-world.sif

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 using apptainer run-help

Save this definition file as rootInUbuntu.def. To build the container, just provide the definition file as argument (executing as superuser):

BASH

apptainer build rootInUbuntu.sif rootInUbuntu.def

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

BASH

./rootInUbuntu.sif

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:

  1. 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).
  2. 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.
  3. If you require any special environment variables to be defined, add them to the %environment and %appenv sections of the build recipe.
  4. Files should always be owned by a system account (UID less than 500).
  5. Ensure that sensitive files like /etc/passwd, /etc/group, and /etc/shadow do not contain secrets.
  6. 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

BASH

apptainer build pythiaInCentos7.sif myPythia8.def

And finally, execute the container to run main01.py

BASH

./pythiaInCentos7.sif

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

BASH

pwd

OUTPUT

/home/myuser/somedirectory

Open a shell inside the container and try to use pwd again

BASH

apptainer shell rootInUbuntu.sif

Apptainer> pwd

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.

Disabling system binds

If for any reason you want to execute a container removing the default binds, the command-line option --no-mount is available. For example, to disable the bind of /tmp

BASH

run --no-mount tmp my_container.sif

Try this time with

BASH

apptainer shell --no-mount home rootInUbuntu.sif

and you will notice that $HOME is not mounted anymore

BASH

ls /home/myuser

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

BASH

mkdir $HOME/mydata
echo "MUONMASS=105.66 MeV" > $HOME/mydata/muonMass.txt

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

BASH

apptainer shell --bind $HOME/mydata:/data rootInUbuntu.sif

This will bind the directory mydata/ from the host as /data inside the container:

BASH

ls -l /data

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

BASH

apptainer shell --bind /cvmfs rootInUbuntu.sif

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:

BASH

export SINGULARITY_BIND="/cvmfs"

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

BASH

apptainer pull docker://centos:centos7

The image must be started in the following way:

BASH

apptainer instance start centos_centos7.sif mycentos7

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

BASH

apptainer instance list

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:

BASH

apptainer shell instance://mycentos7

Remember that exiting the shell instance will not stop the container. For doing so, use instance stop:

BASH

apptainer instance stop mycentos7

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:

BASH

apptainer instance start --bind /home/user/mydata:/data centos_centos7.sif mycentos7

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

BASH

apptainer build --fakeroot basicServer.sif basicServer.def

Now, let’s start an instance named myWebService with the image that we just built

BASH

apptainer instance start --no-mount tmp basicServer.sif myWebService

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

BASH

curl http://localhost:8850

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:

BASH

ssh -L <port>:localhost:<port> myuser@<server>

where is the one used by your service, and is the address of your institutional resources. For example, for connecting to LXPLUS forwarding the port 8850:

BASH

ssh -L 8850:localhost:8850 myuser@lxplus.cern.ch

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

BASH

apptainer build --fakeroot jupyterWithROOT.sif jupyterWithROOT.def

Now, start an instance named mynotebook with our brand-new image

BASH

apptainer instance start jupyterWithROOT.sif mynotebook

and confirm that the instance is up

BASH

apptainer instance list

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:

BASH

apptainer exec instance://notebook jupyter notebook list

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

PYTHON

import uproot
print(uproot.__doc__)

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,

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, named apptainer-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.

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.