Building Containers for HPC

Charles Peterson

📝 Overview

👋 Welcome!

In this workshop, we will explore the advanced use of containers on HPC resources, like UCLA’s Hoffman2. This is a follow-up to a previous workshop “Containers for HPC Resources”

  • 🚀 Dive into advanced container topics
  • 🧰 Build containers tailored for HPC resources
  • 💡 Got suggestions for upcoming workshops?
  • cpeterson@oarc.ucla.edu

📁 Files for this Presentation

👀 Viewing the slides

https://github.com/ucla-oarc-hpc/WS_MakingContainers

Note

This presentation was created with Quarto and RStudio.

  • Quarto file: WS_MakingContainers.qmd

Hands-on Exercise Setup

In this workshop, we will engage in hands-on exercises.

git clone https://github.com/ucla-oarc-hpc/WS_MakingContainers

Requirements:

  • A computer with admin access
  • Install Apptainer
  • Install either Docker or Podman
    • In this workshop, we will be using Podman
  • Note: MacOS (or other ARM-based systems) will not work for this exercise

Alternative: Virtual Machine

Pre-configured Virtual Machine (VM) available on BOX

  • VM file: wscontainer.ova
    • Recommended for use with VirtualBox
  • Username/Password: wscontainer/wscontainer

🔄 Container Review

📦 Containers

🛠️ Software for Containers

Apptainer

  • Formerly Singularity
  • 🎯 Designed and developed for HPC systems
  • 🔧 Most likely installed on most HPC systems
  • 🚀 Supports Infiniband, GPUs, MPI, and other devices on the Host
  • 🐳 Can run Docker containers

🛠️ Software for Containers

Docker

  • Very popular
  • ☁️ Many popular cloud container registries
    • DockerHub, GitHub, Nvidia NGC
  • 🚫 MPI not well supported
  • 🚧 Most likely NOT available on many HPC systems

🔧 Software for Containers

Podman

  • 🔗 Similar syntax as with Docker
    • 🔄 Can use to ‘replace’ Docker
  • 🚫 Doesn’t have a root daemon process
  • 🖥️ On some HPC resources (not on Hoffman2, yet)

Apptainer Workflow for running on H2 🔄

  1. Create 🛠️

  2. Transfer ↪️

  3. Run ▶️

Apptainer Workflow (Create) 🛠️

1. Create 🛠️

  1. Transfer

  2. Run

  • Build a container
    • From Apptainer or Docker on your computer
    • Where you have root/sudo access
    • Typically, Apptainer containers end in .sif
  • Use a pre-built container:

Apptainer Workflow (Transfer) ↪️

  1. Create

2. Transfer ↪️

  1. Run

Bring your container to Hoffman2:

  • Copy your container to Hoffman2
scp test.sif username@hoffman2.idre.ucla.edu
  • Pull a container from Container Register
apptainer pull docker://ubuntu:20.04
  • Use a container pre-built on Hoffman2
module load apptainer
ls $H2_CONTAINER_LOC

Apptainer workflow (Run) ▶️

Create

Transfer

Run ▶️

Run Apptainer on your container:

  • Can run in an interactive (qrsh) session
qrsh -l h_data=20G
module load apptainer
apptainer exec mypython.sif python3 test.py
  • Or run as a Batch (qsub) job

  • Create job script myjob.job

#!/bin/bash
#$ -l h_data=20G
module load apptainer
apptainer exec mypython.sif python3 test.py
  • Submit your job
qsub myjob.job

Common Usage

On Hoffman2, to use apptainer, all you need to do is load the module

module load apptainer
  • 🎉 Only module you need to load!
  • No need to load tons of modules to run a single application
  • 🌐 Expect MPI module if running parallel
    • module load intel/2022.1.1

📚 Common Apptainer commands

  • 📤 Getting a container from somewhere
apptainer pull [options]
apptainer pull docker://ubuntu:22.04
  • 🏭 Build a container
apptainer build [options]
apptainer build myapp.sif myapp.def
  • 🏃 Run a single command inside of a container
apptainer exec [options]
apptainer exec myapp.sif MYCOMMAND
  • 📜 Run the the container with a predefined script
apptainer run [options]
apptainer run myapp.sif "SCRIPT ARGRMENTS"

Building Containers

Building Overview 🏗️

There are serveral ways to build containers for Apptainer

Basic Build Command

  • Use apptainer build to build new containers

Typically, you will use the build command by

apptainer build <New Image> <Build Path>
  • is the name you want to give to the new image.
  • is the method apptainer will use to build the container

Building from an Existing Image 📦

  • Typically, you will build a container is by using an existing container as a base.
apptainer build myubuntu.sif docker://ubuntu:22.04
  • This will create a new container file, myubuntu.sif, using the official Ubuntu 22.04 image from DockerHub.
  • Note: SIF containers are immutable
    • Once built, they cannot be modified.
  • You can run your apptainer commands from this new container.
apptainer shell myubuntu.sif
apptainer exec myubuntu.sif echo "Hi"

Sandbox (Writable Containers) 🏗️

If you need a container that allows modifications, use the sandbox feature.

  • Sandbox directories
    • Create a container within a writable directory (Sandbox)
    • Great to expermint installing software
sudo apptainer build --sandbox myubuntu-SB docker://ubuntu:22.04
  • This create a directory myubuntu-SB that has the contents of the ubuntu image from DockerHub.
  • Note the use of sudo
    • To ensure proper file permissions it is recommended to run this as an admin

Note

This new Sandbox image does NOT have the .sif extension because this is a directory, not a single file.

Using Sandbox Containers 🏗️

  • You can enter the sandbox container and modify it
    • Use --writable flag
sudo apptainer shell --writable myubuntu-SB
  • Any modifications inside the sandbox persist after you exit.

Once you’re satisfied with the changes, convert it to a SIF container:

sudo apptainer build myNewUbuntu.sif myubuntu-SB

This packages the modified sandbox into a read-only .sif container called myNewUbuntu.sif.

Building with a Definition File 📜

A definition file provides precise instructions on how to build and configure a container.

apptainer build myapp.sif myapp.def
  • Building from Apptainer definition files
    • Build a container from a definition file that has detailed information on how to install software inside the container
    • apptainer build myapp.sif myapp.def
      • myapp.sif is the new container built
      • myapp.def is the definition file

This definition file details how the new container would be build

Example definition file 🔧

File: lolcow.def

Bootstrap: docker
From: docker.io/ubuntu:22.04

%labels
Example from apptainer.org

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

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

%runscript
    date | cowsay | lolcat
  • %Bootstrap - Specifies how to pull the base OS (e.g., from Docker).
    • docker bootstrap will pull docker format layers
  • %From - Location and name of docker container
    • docker.io - DockerHub
    • ghcr.io - GitHub Packages
  • %labels - Add textual metadata information to container
  • %post - Commands to setup the container from the baseOS the final container
  • %environment - Define environment variables inside container
  • %runscript - Specifies the command that runs when using apptainer run

In this example, we start with the Ubuntu 22.04 as a base OS. Then run commands from %post to create the final container.

Running from Definition File 🏃

  • Build the container
apptainer build lolcow.sif lolcow.def
  • Execute a command inside the container
apptainer exec lolcow.sif cowsay "Hello from Apptainer!"
  • Run the container’s default script
apptainer run lolcow.sif

This will execute the runscript section (which prints a date-stamped ASCII cow 🐄 message in rainbow colors 🌈).

Once you create a SIF file, this is the container that you can transfer to Hoffman2 to run or share with others.

Note

Can also build an SIF container from a Dockerfile

Example 1: PyTorch

Example 1: Writable containers 🛠️

  • This example uses PyTorch 🧠

  • Similar to last week’s example

    • Though, instead of using PyTorch’s pre-build container, we will create our own container
  • Go to the EX1 directory
    • Examine the pytorch.py file
    • Optimize a 3rd order polynomial to a sine function

Creating Sandbox container 🚀

  • Create a container from a base source image
  • Lets create a container based on Ubuntu 22.04
apptainer build --sandbox ubuntu_22.04_SB/ docker://ubuntu:22.04
  • apptainer build:
    • Build new Apptainer container image
  • --sandbox:
    • Flag for “sandbox” or a writable directory
  • ubuntu_22.04_SB/
    • Name of the directory where the sandbox image will be created
  • docker://ubuntu:22.04
    • Location of source image

This will create a Sandbox container ubuntu_22.04_SB

Running the container 🏃

Next, we will start a WRITABLE interactive shell session in the sandbox image:

sudo apptainer shell --writable ubuntu_22.04_SB/
  • --writable will allow us to modify the container

From here, we can run any commands we need to install PyTorch:

apt update
apt install -y python3 python3-pip
pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cpu
exit

Convert the sandbox container to a SIF file, pytorch.sif

sudo apptainer build pytorch.sif ubuntu_22.04_SB/

Running Our Container 🚀

Test our new SIF container:

apptainer exec pytorch.sif python3 pytorch-code.py

We do not need to be root since we are just running python3.

Transfer our container:

scp pytorch.sif USERNAME@hoffman2.idre.ucla.edu:

Example job script to run on Hoffman2:

qsub pytorch.job

Example 2: Installing Scientific Code

QUILL software 🔬

In this example, we will create a container with a chemistry application that I created in Grad School. This code will calcuate the engery of a molecule.

To install, we need

  • Python with the PySCF package
  • Eigen3, a Linear Algebra library

We will build this container by:

  • Apptainter definition file (.def)
  • Using Docker/Podman (Dockerfile)

Apptainer Definition file 📜

  • Definition Files are like the blueprint to building a custom container.
  • Instead of interactively modifying a sandbox image, we can build a container with this Definition file
  • The quill.def file has all steps needed to build the QUILL container.
Bootstrap: docker
From: ubuntu:20.04

%labels
Author Charles Peterson <cpeterson@oarc.ucla.edu>

%post
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
   git python3 python3-dev python3-pip \
   libeigen3-dev ca-certificates cmake make gcc g++
rm -rf /var/lib/apt/lists/*

pip3 install pyscf
ln -s /usr/bin/python3 /usr/bin/python
mkdir -pv /apps
cd /apps
git clone https://github.com/charliecpeterson/QUILL
cd QUILL
mkdir build ; cd build
cmake ..
make

%environment
export PATH=/apps/QUILL/build:$PATH

Create container 🔧

Create the quill.sif container

apptainer build quill.sif quill.def
  • To Run this software, you would run
    • QUILL.x test.inp
    • Where test.inp is the input file
  • Running the command inside the container
apptainer exec quill.sif  QUILL.x test.inp
  • Move container to Hoffman2
scp quill.sif USERNAME@hoffman2.idre.ucla.edu:

Building using Docker/Podman 📤

  • Docker or Podman can be use to create containers
    • Then convert Docker/Podman container to SIF format
  • I will be using Podman
    • Docker and Podman have same syntax
    • Replace podman with docker
docker build
docker images
docker pull
docker run
podman build
podman images
podman pull
podman run

Dockerfile for QUILL 🛠️

  • Dockerfile-quill file is used by Docker to create container
FROM ubuntu:20.04

## Author Charles Peterson <cpeterson@arc.ucla.edu>

RUN apt-get update \
     && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
   git python3 python3-dev python3-pip \
   libeigen3-dev ca-certificates cmake make gcc g++ \
   && rm -rf /var/lib/apt/lists/*

RUN pip3 install pyscf ; ln -s /usr/bin/python3 /usr/bin/python

RUN mkdir -pv /apps \
    && cd /apps \
    && git clone https://github.com/charliecpeterson/QUILL \
    && cd QUILL \
    && mkdir build ; cd build \
    && cmake .. ; make

ENV PATH=/apps/QUILL/build:$PATH

Container Creation 🚀

Create Docker container

  • Build container from Dockerfile
podman build . -t quill:1.0 -f Dockerfile-quill
  • See built docker (podman) container
podman image list

Create Apptainer container from Podman

  • Save podman image in a tarball
podman save quill:1.0 > quill.tar
  • Create SIF file
apptainer build quill.sif docker-archive://quill.tar
  • Transfer final SIF to Hoffman2
scp quill.sif USERNAME@hoffman2.idre.ucla.edu:

Container Registry 📓

In the previous slides, we created a SIF file (quill.sif), then transfer (scp) the container to Hoffman2.


Instead of this, we can upload our container to a Container Registry.

  • These Registries are used store our containers on a remote, cloud server that can then be pulled/download anywhere that has apptainer.

DockerHub 📓

Lets create a repo on DockerHub

  • First, create a DockerHub account
    • Mine is charliecpeterson
  • Push our podman container to DockerHub
    • Registry location docker.io
  • Tag container to DockerHub location
podman tag quill:1.0 docker.io/charliecpeterson/quill:1.0
  • Login info to DockerHub
podman login docker.io
  • Push container to DockerHub
podman push docker.io/charliecpeterson/quill:1.0
  • Then pull the container on Hoffman2
apptainer pull docker://docker.io/charliecpeterson/quill:1.0

GitHub Packages 📓

  • Lets create a repo on GitHub
    • Look for the Packages tab
  • Same syntax as before
    • registry location is ghcr.io

Push our final container to GitHub

podman tag quill:1.0 ghcr.io/charliecpeterson/quill:1.0
podman push ghcr.io/charliecpeterson/quill:1.0

Then pull the container on Hoffman2

apptainer pull docker://ghcr.io/charliecpeterson/quill:1.0

DockerHub and GitHub Packages are popular cloud registries. You can create and deploy a local container registry.

Running Container 🏃

Once the container is on Hoffman2, submit job.

#!/bin/bash
#$ -cwd
#$ -o quill.$JOB_ID
#$ -j y
#$ -l h_rt=1:00:00,h_data=15G
#$ -pe shared 1
#$ -l arch=intel-gold*

# load the job environment:
. /u/local/Modules/default/init/modules.sh
module load apptainer

# Container part: apptainer exec QUILL.sif
# Command: QUILL.x /apps/QUILL/input.inp
apptainer exec quill.sif QUILL.x test.inp

Submit job script

qsub test.job

More information on using Definition files

More information on using Dockerfiles

Example 3: MiniForge 🐍

MiniForge is a very popular python and R distributaion for simplifying package installation via the conda package manager

MiniForge can be tricky installing in a container due to environment setup.

This example will use MiniForge to install an application in a container.

Building H2O 💧

We will go over creating a definition file for a example with MiniForge

We will install the software h2o.ai. This is a great machine learning platform that has Python and R libraries.

In this example, we will use MiniForge to install h2o packages inside python and R.

H2O definition file 📜

  • The h2o.def file
    • Use of %runscript to setup MiniForge env for apptainer run
      • the $@ take arguments as a string from the command line
Bootstrap: docker
From: ubuntu:22.04

%labels
Author Charles Peterson <cpeterson@oarc.ucla.edu>

%post
export DEBIAN_FRONTEND=noninteractive
apt -y update ; apt -y upgrade
apt install -y  wget libbz2-dev wget git gcc  libreadline-dev zlib1g-dev default-jre default-jdk

#Install Miniforge
cd /tmp
wget https://github.com/conda-forge/miniforge/releases/download/24.11.3-0/Miniforge3-24.11.3-0-Linux-x86_64.sh
bash Miniforge3-24.11.3-0-Linux-x86_64.sh -b -p /opt/miniforge
bash -c "source /opt/miniforge/etc/profile.d/conda.sh
conda create -n h2oai h2o -c h2oai -c conda-forge
"

%runscript
exec bash -c "source /opt/miniforge/etc/profile.d/conda.sh
conda activate h2oai
$@"

In the runscript, it will activate the conda environment, h2oai, and run the command that follows the apptainer run h2o.sif command

Building container 🔧

  • Create h2o.sif
apptainer build h2o.sif h2o.def
  • Run python inside the container
apptainer run h2o.sif "python h2o-test.py"

Note

  • apptainer exec foo.sif [COMMAND]
    • Run a single [COMMAND] inside the container
    • The runscript will NOT run
  • apptainer run foo.sif
    • Run the runscript inside the container

Example 4: Jupyter

Using Jupyter 🐍

This example will show how to create a Jupyter container and start a Jupyter on Hoffman2

  • Create a container with Jupyter
    • Useful so you can run same Jupyter setup anywhere
    • Install all packages you need in container

Creating Jupyter container 🐍

  • jupyter.def
    • Apptainer Def file
    • Starts with a pre-built python 3.8.13 container
    • Adds jupyter, pandas, and seaborn
  • Build container (locally)
  • Transfer to H2
apptainer build jupyter.sif jupyter.def 
scp jupyter.sif hoffman2.idre.ucla.edu:

Running Jupyter 🏃

  • Start Jupyter on Hoffman2
    • Note the name of compute node you landed on for SSH tunneling
qrsh -l h_data=10G
module load apptainer
hostname
apptainer exec jupyter.sif jupyter lab --ip 0.0.0.0
  • SSH tunnel to H2 compute node
    • Change nXXX (compute node)
    • Change port 8888 if needed
ssh  -L 8888:nXXX:8888 username@hoffman2.idre.ucla.edu 
  • Then open a web browser and type
http://localhost:8888 

Wrap Up

Tips 🛠️

Size of container

  • Try to keep the size of your container small and minimal
    • Only have the things necessary for your applications to run
  • Large containers will need more memory and will take more time to start up
  • Check out Multi-Stage approach
  • Build from an existing container
    • Look for images on DockerHub, NGC
    • Build your own and upload to DockerHub/GitHub
  • Good idea to build a sandbox container, then create definition file
    • Test out commands in sandbox while making the def file!

More Things to Think About 🧠

  • Share .sif files with your friends!
    • Save your (Docker) containers to DockerHub or GitHub Packages
    • Create Dockerfile/Def files to recreate or modify containers
  • Find examples of Dockerfiles and Apptainer def files on our GitHub Page

Thank you! 🙏

Questions? Comments? 🤔

Charles Peterson