CIQ: Singularity Container Basics

February 2, 2021

Webinar Synopsis:


  • Ian Kaneshiro - Software Engineer, CIQ

Note: This transcript was created using speech recognition software. While it has been reviewed by human transcribers, it may contain errors.

Full Webinar Transcript:

The Basics of Singularity [00:00]

Ian Kaneshiro:

Hello, everyone. Today, I'm going to give you a quick tutorial to get you started with Singularity. First of all, we should start with some of the basic information about Singularity. Singularity is a rootless container solution. This means that we can bring up a container as a user on a system. And the container process retains that user identity and cannot be used to gain additional privileges on the system. Singularity also uses a unique container format called the Singularity Image Format or SIF.

So the defining traits of this format are that it exists as a single image file and has a native support for content trust and confidentiality. This container format is unique to Singularity. Other container tools, like Docker, use a different format. Singularity integrates smoothly with these other tools and the container ecosystem by being able to seamlessly convert these images into SIF images on the fly.

Setting up an Alpine Image [00:45]

We can see an example of this by bringing up an interactive shell within a Docker image. Here, I'll use an Alpine image from Docker Hub. I can simply run singularity shell docker://alpine. And after a quick build process, I'll be dropped into a shell with an Alpine image.

Now we can make sure we're within an Alpine image by checking the OS release. We can see that we are, and we can also see that we have access to the standard tooling within an Alpine-like APK. It's also interesting to note that the current working directory within this container is the same as the current working directory outside the container on my host. This is possible because singularity mounts several directories from your host environment- like slash home- into the container by default. This gives the illusion of simply swapping out the user space of your Linux distribution while keeping all of your user data intact. This allows your containerized applications to read and write data within user-owned locations, just like they would if they're running natively on the host.

Building the App [01:54]

Now let me run an existing container image from Docker Hub. Let's choose the build system to create a new SIF image with a small application package within it. Here, I prepared a build definition called adder.def.

This describes what the build process needs to do to properly package my application within the container. In this demo, I created an app that simply takes values given as command line arguments and adds them together. At the top of the file, we'll see the bootstrap keyword. This lets the build system know what the base of the container will be. In this case,  it will be a Docker image. There are many different boost app options that may be better for your use case, but this is definitely the most widely used option. Next, we can see a “from” statement specifying the image from Docker Hub that we're going to use. In this case, we are packaging an application written in Go, so I've selected an image with all of the tooling installed.

File Section [02:51]

Next, we can see a file section. This allows you to copy files from your host into the container file system. Here, we are copying the source code for our app into the container for compilation later on.

Post Section [03:02]

After that, we have the post section. This is a shell script that runs within the container file system and is usually the workhorse of the build process. Users will typically install packages and tooling, download source code from software repositories, and build their applications in this section. In this case, we already have all the tooling we need to build our application installed from the base image, and the source code copied in from the host. At this point, it's just a matter of compiling our code and installing it into a standard directory. In this case, we're going to use /usr/local/bin.

Run Section [03:42]

Finally, we have the run section. This section is a bash script that runs immediately when the container has started with the run command. This script simply takes the arguments passed to the run command and feeds them into our application.

Building the Container [03:53]

Now, we can go ahead and build our container here. We'll use Singularity to build with the fakeroot flag and then we'll name our container adder.sif, then we'll specify our build definition, which is adder.def.

The Fake Root Flag [04:05]

Before I start running this, I want to explain a little bit about why we're using the fakeroot flag. This allows us to build our container as a normal user, without any elevated privileges. If you wanted to emit this flag, you would need to use pseudo before running Singularity in order to have elevated privileges on your system.

Now that we're starting to build, the build process will start installing that Docker container, copying our code into the container, and building our app.

Now that our build is done, we can see our adder container. We can go ahead and run that with singularity, run adder.sif, and then we'll use one, two, and three. When you run that, we get six as our output, which is what we would expect from that addition. That's all you need to do.

These basics will let you get up and running with Singularity, but there are many more features beyond this within Singularity. I'd recommend exploring our documentation of things like seamless GPU integration or container if image signing and encryption are of interest to you.

Thank you.