Build your own container image

Overview

Teaching: 20 min
Exercises: 20 min
Questions
Objectives
  • Learn what is a definition file (def file) and its basic syntax

  • Learn how to build a container image and share it with others

  • Learn about container registries and remote build services

What is a def file?

A definition file, or def file, is a recipe to build a container image with Singularity. It is basically a collection of the standard shell commands you would use to build your software through prompt; in addition, it contains Singularity-specific header lines that handle the build process. We will discuss these below with an example. Although there is no mandatory naming convention for def files, they are often characterised by the suffix .def.

Sudo privileges with Singularity

Singularity does not allow for privileges escalation.
In other words, if you are a standard user and you run singularity, any command inside the container will be run with the privileges of the standard user, i.e. without admin powers. If you try and sudo from inside the container you will get an error.
On the other hand, if your user can run with sudo, and if you then decide to run Singularity as sudo singularity, then you will run any command from inside the container with admin powers.
This design is what makes Singularity safe to run on HPC: users without admin rights are unable to escalate their privileges from inside the containers.

However, when building a container image you might need to install software using commands that require admin rights, e.g. apt get in Ubuntu/Debian or yum in Centos. To achieve this, you need to run sudo singularity build, implying that you need to carry out your build in a machine where you DO have admin rights.

NZ RSE 2020 attendees and those running on a HPC system:

You won’t be able to elevate privileges using sudo, as such we will be mainly focusing on the use of remote build services.

Building a basic container

Singularity can build container images in different formats. Let’s focus on the Singularity Image Format, i.e. the one typically adopted to ship production-ready containers.
This example is adapted from this well crafted Singularity Tutorial.

Let us cd into the appropriate directory:

cd $NZRSE/demos/06_lolcow

Then, here is the def file we’re going to use, lolcow.def:

BootStrap: docker
From: ubuntu:18.04

%post
    apt-get -y update
    apt-get -y install fortune cowsay lolcat

%environment
    export PATH=/usr/games:$PATH

%labels
    Author Pawsey Supercomputing Centre
    Version v0.0.1

%help
    This is a demo container used to illustrate a def file.

%runscript
    fortune | cowsay | lolcat

If you are on a machine where you have sudo permission, you can build a container using the command sudo singularity build, followed by the filename we decide to attribute to the container image, and then by the filename of the def file to be used:

sudo singularity build lolcow.sif lolcow.def
INFO:    Starting build...
[..]
INFO:    Running post scriptlet
[..]
INFO:    Adding help info
INFO:    Adding labels
INFO:    Adding environment to container
INFO:    Adding runscript
INFO:    Creating SIF file...
INFO:    Build complete: lolcow.sif

However, if you are following along on Mahuika you can run the container with:

$SIFPATH/lolcow.sif

Now, let us try and use the container simply as an executable:

./lolcow.sif

You will get something similar to this, hopefully just more colourful:

 _______________________________________
/ Have a place for everything and keep  \
| the thing somewhere else; this is not |
| advice, it is merely custom.          |
|                                       |
\ -- Mark Twain                         /
 ---------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

The first line is BootStrap: docker.
This tells Singularity how the image has to be initialised. docker means that we are going to start with a base image from Docker Hub. You can also bootstrap from Sylabs Cloud with library, SingularityHub with shub or localimage to use a base image your machine. The image is specified in the next line, in this case From: ubuntu:18.04.
Note how we started from Ubuntu 18.04 in Docker Hub, not Sylabs Cloud, as the former version has got a bit of a richer, more useful configuration.

Next is a section that start with the header %post. This is basically a sequence of commands to be executed to install packages in the image, in essence the same commands you would use for installation in a Linux box. Here we are ensuring we have an up-to-date list of packages, and then we are installing three Linux utilities.

The section %environment sets up environment variables that need to be defined at runtime rather than at build time. Here the PATH needs to be updated to reflect the location of the three utilities that we installed in the %post section.

Another section that is often useful can be defined by the header %files, like in:

%files
    <src-file> <dst-file>

This section is used to copy files from the host, i.e. , inside the container in the destination .

Documenting the container image

The %labels section is used to add metadata to the container image. These can be then inspected by using

singularity inspect lolcow.sif
==labels==
org.label-schema.usage.singularity.deffile.from: ubuntu:18.04
org.label-schema.usage.singularity.version: 3.3.0
Version: v0.0.1
org.label-schema.build-date: Tuesday_29_October_2019_14:44:19_UTC
org.label-schema.usage: /.singularity.d/runscript.help
org.label-schema.usage.singularity.runscript.help: /.singularity.d/runscript.help
Author: Pawsey Supercomputing Centre
org.label-schema.schema-version: 1.0
org.label-schema.usage.singularity.deffile.bootstrap: docker

See how the Author and Version metadata are in the list.

The text content of the %help section is also embedded in the image, and can be accessed via

singularity run-help lolcow.sif
    This is a demo container used to illustrate a def file.


This can be useful to provide a description of the container, or even instructions on how to use it.

Finally, note how the def file used to generate the image can be displayed using

singularity inspect --deffile lolcow.sif

The full documentation for definition files can be found here.

Use the newly created container

You can use this new container using the same Singularity syntax we introduced earlier on in this tutorial.

For instance, how would you run the command fortune from inside this container?

Solution

$ singularity exec lolcow.sif fortune
Whenever you find that you are on the side of the majority, it is time
to reform.
		-- Mark Twain

Or, how would you open an interactive shell to explore the container?

Solution

$ singularity shell lolcow.sif
Singularity lolcow.sif:/home/ubuntu/nzrse-containers/demos/06_lolcow> ls
lolcow.def  lolcow.sif
Singularity lolcow.sif:/home/ubuntu/nzrse-containers/demos/06_lolcow>

Remember to close this session with exit or Ctrl-D.

Run a container as an application

There’s one section of the def file we haven’t commented on yet. %runscript allows you to define a default command for the image. This command can then be used if you run the container as an executable:

./lolcow.sif
 ______________________________________
/ A few hours grace before the madness \
\ begins again.                        /
 --------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

Or, if you need to specify Singularity runtime flags, e.g.:

singularity run -B $NZRSE/_episodes lolcow.sif
 ___________________________________
< You will outgrow your usefulness. >
 -----------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

Share your container image

Now that you’ve built your container image, you might want to run it on other systems, or share it with collaborators.

The simplest way to achieve this is to remember that a SIF image is just a file, so .. you can transfer it across systems using Linux command line utilities like scp or for Windows an SCP client like MobaXterm. Just remember that images can be quite large, typically ranging from tens of MBs up to several GBs. For instance the lolcow image we created is about 70 MB.

If you want to keep the images publicly available, you may want to host it on a container registry.

Remote build

What if you need to build an image from a system where you don’t have admin privileges, i.e. you can’t run commands with sudo?

Singularity offers the option to run build remotely, using a Remote Builder we will be using the default provided by Sylabs; You will need a Sylabs account and a token to use this feature.

singularity remote login
Generate an API Key at https://cloud.sylabs.io/auth/tokens, and paste here:
API Key:

Now paste the token you had copied to the clipboard end press Enter:

INFO:    API Key Verified!

With this set up, you may use singularity build -r to start the remote build. Once finished, the image will be downloaded so that it’s ready to use:

singularity build -r lolcow_remote.sif lolcow.def
INFO:    Remote "default" added.
INFO:    Authenticating with remote: default
INFO:    API Key Verified!
INFO:    Remote "default" now in use.
INFO:    Starting build...
[..]
INFO:    Running post scriptlet
[..]
INFO:    Adding help info
INFO:    Adding labels
INFO:    Adding environment to container
INFO:    Adding runscript
INFO:    Creating SIF file...
INFO:    Build complete: /tmp/image-699539270
WARNING: Skipping container verifying
 67.07 MiB / 67.07 MiB  100.00% 14.18 MiB/s 4s

At the time of writing, when using the Remote Builder you won’t be able to use the %files header in the def file, to copy host files into the image.

You are now ready to push your image to the Cloud Library, e.g. via singularity push:

singularity push -U lolcow.sif library://<YOUR-SYLABS-USERNAME>/default/lolcow:30oct19
WARNING: Skipping container verifying
 67.08 MiB / 67.08 MiB [==================================================================================================================================] 100.00% 6.37 MiB/s 10s

Note the use of the flag -U to allow pushing unsigned containers (see further down).
Also note once again the format for the registry: //:.

Finally, you (or other peers) are now able to pull your image from the Cloud Library:

singularity pull -U library://<YOUR-SYLABS-USERNAME>/default/lolcow:30oct19
INFO:    Downloading library image
 67.07 MiB / 67.07 MiB [===================================================================================================================================] 100.00% 8.10 MiB/s 8s
WARNING: Skipping container verification
INFO:    Download complete: lolcow_30oct19.sif

Other build options

The def file specification has a number of other interesting features, to know more about them you can visit the Sylabs docs on def files.

In the episode on GUI applications we’ll see how to use %startscript to configure the behaviour of containers running in background.

If you are in a development phase, where you don’t know yet what you will include in your final container image, you can start with a sandbox image. This is a special type of image designed for development purposes, consisting not of a single file, but instead of a directory. To create one, run something like:

sudo singularity build --sandbox playbox/ docker://ubuntu:18.04

Then to open it and play, run:

sudo singularity shell --writable playbox/

More information on sandbox images can be found at the Sylabs docs on building images.

One last notable feature is the ability to use PGP keys to sign and verify container images. In this way, users of 3rd party containers can double check that the image they’re running is bit-by-bit equivalent to the one that the author originally built, largely reducing the possibility to run containers infected by malware. you can find more on this topic at the Sylabs docs on signing and verifying containers.

Useful base images

At the time of writing, Docker Hub is the most popular web registry for general purpose container images. Therefore all images mentioned below are hosted in this registry.

CUDA

nvidia/cuda has images to build GPU enabled applications. There are different image types for different needs. Tags containing runtime are suitable for binary applications that are ready to run; if you need to compile GPU code, pick tags containing devel instead. Different OS flavours are available, too.

MPI

As you can see in the episode on MPI applications, when containerising this type of software the MPI libraries in the image need to be ABI compatible with the MPI libraries in the host. The Pawsey Supercomputing Centre maintains some MPICH base images at pawsey/mpi-base, for building images that will run on our HPC systems.

Python

python hosts the official Python images. Different versions are available for some OS flavours. At the time of writing the default image tag corresponds to Python 3.8 on Debian 10. Smaller base images have tags ending with -slim.

continuumio/miniconda3 are images provided by the maintainers of the Anaconda project. They ship with Python 3, as well as pip and conda to install and manage packages. At the time of writing, the most recent version is 4.7.12, based on Python 3.7.4.

If you need interactive Jupyter Notebooks, Jupyter Docker Stacks maintain a series of dedicated container images. Among others, there is the base SciPy image jupyter/scipy-notebook, the data science image jupyter/datascience-notebook, and the machine learning image jupyter/tensorflow-notebook.

R

The Rocker Project maintains a number of good R base images. Of particular relevance is rocker/tidyverse, which embeds the basic R distribution, an RStudio web-server installation and the tidyverse collection of packages for data science. At the time of writing, the most recent version is 3.6.1.

Other more basic images are rocker/r-ver (R only) and rocker/rstudio (R + RStudio).

Key Points

  • Build images using remote

  • Use the remote builder with the flag -r, if you need to build images from a machine where you don’t have sudo rights

  • You can share you Singularity Image File with others, as you would do with any other (big) file

  • Upload images to a web registry with singularity push (Sylabs account required)