Building Containers
By the end of this tutorial, you will be able to build containers in three different ways: interactively, from a definition file, and from a local image. In this section, we will learn how to:
- alter and rebuild containers interactively with the sandbox method.
- build containers from a container recipe file called a "definition" file (
.def
). - build containers from a previously pulled container that is already on Hyak.
warning
If you are just jumping into this tutorial starting on this page, you should complete the following steps to make sure you have a copy of the materials you need going forward.
To start, ensure that you are in a directory where you have storage, and use this directory as storage for the workshop materials. For example, you can make a directory for yourself in /gscratch/scrubbed/
If don't have the workshop materials, please copy them to a directory where you have storage, and use the basics/
directory as your working directory. Copy and open the basics directory as follows:
For this tutorial, we will be pulling containers from Docker Hub using Apptainer. To do this, we must be in a compute node. Start a job on a compute node with the salloc
command:
hyakalloc
to view your available resources. If you are a demo account user, please use the ckpt-all
partition as shown above.#
Use #
Building a Container InteractivelyWe'll start this section of the tutorial by demonstrating how to alter and rebuild containers interactively with the sandbox method. First we need to pull an Ubuntu container from Docker Hub. We will convert this into a sandbox, open a writable shell into it, install Git, and rebuild a new version of the container that includes Git.
#
Pulling UbuntuStart by opening up Docker Hub and searching for Ubuntu. We will pull the latest version of Ubuntu with the following command:
#
The latest version will be downloaded if no further specifications are given in the command after Ubuntu.The build should last 30 seconds to 1 minute. During the pull and build, you will see messages from Apptainer on the progress of the command.
Check to see if the Ubuntu container was sucessfully pulled with ls
.
You can use the following command to check the version of Ubuntu you are running and the date it was uploaded:
As with previous execises, you can use apptainer shell
to open a shell into ubuntu_latest.sif
and execute commands interactively.
Before we do that, let's check the native version of Git that is installed on Hyak.
Remember that this version of Git will not be available inside the Ubuntu container until we install it. The Ubuntu container is a bare and clean version of the Ubuntu OS and that's it. Let's demonstrate that.
Open a shell into the ubuntu_latest.sif
container.
Execute the command to obtainer the Git version.
The following will result indicating there is no version of Git installed in the container.
Next, we will demonstrate how to convert the container into a sandbox so that additional software can be installed into it interactivey.
#
SandboxingIn this section we will create a writable directory known as a "sandbox" for this container, allowing for file modifications and additional software installations. This method of container development is available and may fit the preferences of some users, but it isn't the recommended method for building custom containers because it doesn't include a record of the steps taken for further container development. Additionally, this might not work with some containers because it brings the user space of the container into the kernel space of the host, which could lead to some incompatibilities. However, we want to demonstrate this so that you can explore the method and determine if it fits your needs.
Convert our Ubuntu container into a sandbox with the following command:
This conversion should take 1-2 minutes.
This will create a directory named ubuntu_latest
. Open this directory using cd
to and use ls
to see the contents. Notice how the contents of ubuntu_latest
reflects aspects of an Ubuntu operating system.
Everything that was installed inside of the container image should be present in this directory.
cd
back into the basics directory and shell into the writable, sandboxed version of ubuntu_latest to work interactively:
You will see the following messages and warnings that indicate that some Hyak paths and features so not exist inside the sandboxed container.
The --fakeroot
flag allows users who may not have root access to simulate running a container as root. Because all Apptainer containers are read-only by default, the --writable
flag is used to allow users to make changes to the container's filesystem. The command prompt will now start with Apptainer>
if you successfully shelled into ubuntu_latest.
#
Installing GitFinally, we can install Git into the container. Before proceeding with this installation, it is important to first update the package information in ubuntu_latest
sandbox container. The next command refreshes package lists, updates package information, and updates the local cache; without updating the package information the Git package can not be found and installed. To start an update, use the Ubuntu package manager apt
:
Next install Git.
Then confirm that Git is installed and check the version with:
You'll notice that this version of Git is distinct from the local version that is installed on Hyak (2.43.0 vs. 2.43.5).
#
Rebuild Container now with Git installedExit the Apptainer with exit
and build a new container from the ubuntu_latest
sandbox that now has Git installed. This can be named anything. In this tutorial, we will call it ubuntu_latest-git.sif
.
This should take 30 seconds to 1 minute to complete.
You can check the version of git installed outside the shell with:
Next we'll demonstrate how to do the same thing: build a custom Ubuntu container with additional software installed, but this time from a definition file.
#
Building a Container with a Definition FileIn the previous example, you built a container interactively. Alternatively, you can build a container by creating your own set of "blueprints" for your container. These blueprints are called Apptainer definition files. Definition files should include information such as the base operating system your custom container should start with and instructions to install software. Definition files provide a record of what you did to build your container, adding crucial documentation of your process.
A definition file is simply a text file with specialized syntax for Apptainer. So let's start a new text file called container-build.def
with nano
:
And paste the following text into the file.
Let's break down what these sections do when Apptainer builds the container:
- All definition files start with
Bootstrap
followed by the bootstrap agent which specifies the base operating system the container image will use. In this tutorial, we will be using the Docker bootstrap agent. Other agents you may come across are localimage, oras, and scratch. From: ubuntu
indicates what image you want to use or the specific publisher in Docker Hub you are pulling from. In this case, we are using the Ubuntu repository and without further specifications, the starting OS will be the latest version of Ubuntu available on Docker Hub.- The
%post
section is where new software and files can be downloaded and new directories can be made. Here we are updating package information, and this time we will install Git and Curl into the container. - The
%runscript
section can be used to test your container; the commands under the%runscript
section will be executed when the commandapptainer run
is used.
Save and exit the text editor. Use Ctrl+x
to exit the text editor. Next we will build the container using your definition file.
The build should take 1-2 minutes. The completed container will be called git-container.sif
.
FInally, use apptainer run
to execute the runscript and test the container. In this case, the runscript executes commands to print the versions of Curl and Git.
#
Building a Container From a Local ImageInstead of pulling a container image from the internet to build your custom container, you can use a local image to build your container. This can be usful to containue customizing your containers by continuing to build upon them and installing additional software for your project.
If you followed the previous section of the tutorial on Pulling Containers, you should have the python_3.9.20-slim-bullseye.sif
container. In this section, we will install TensorFlow using Python that is installed inside python_3.9.20-slim-bullseye.sif
. Start by creating a new definition file.
And paste the following text into the file.
Notice that this time we are using localimage
as the Bootstrap agent, which means that Apptainer will find the named image after the From:
directive and use it as the base operating system image. The installation instructions for TensorFlow then follow in the %post
section, and the %runscript
section contains instructions to print a greeting and the version of TensorFlow installed in the container when apptainer run
is executed.
Like before, build the container using the definition file. tensorflow_py3.sif
will be the name of the new container.
This build could take 5-6 minutes to complete.
To demonstrate that the container has TensorFlow installed, use the following command to run the runscript:
Finally, we prepared a script to demonstrate TensorFlow with this container. You should have the python file tf_tutorial.py
in your basics directory. You can now bind the filesystem with --bind
and run the Python script with the following command:
You have now built Apptainer containers in three different ways: interactively, from a definition file, and from a local image. We hope you will be able to adapt these methods to fit your needs and the needs of your research project. If you have any questions or suggestions for how to improve this tutorial, please email help@uw.edu with "Hyak Containers Tutorial" in the subject line, and let us know what you think. Thank you!
Acknowledgements
tf_tutorial.py
is the "TensorFlow 2 quickstart for beginners" from https://www.tensorflow.org/tutorials/quickstart/beginner made into a script with minimal adaptions.