├── 00-introduction └── README.md ├── 01-installation └── README.md ├── 02-basic-usage └── README.md ├── 03-building └── README.md ├── 04-the-ecosystem └── README.md ├── 05-bind-mounts └── README.md ├── 06-runscript └── README.md ├── 07-fake-installation └── README.md ├── 08-misc └── README.md ├── README.md └── _config.yml /00-introduction/README.md: -------------------------------------------------------------------------------- 1 | <- [previous](/) - [home](https://singularity-tutorial.github.io/) - [next](/01-installation) -> 2 | 3 | --- 4 | # Introduction 5 | 6 | ## What IS a software container anyway? (And what's it good for?) 7 | 8 | A container allows you to stick an application and all of its dependencies into a single package. This makes your application portable, shareable, and reproducible. 9 | 10 | Containers foster portability and reproducibility because they package **ALL** of an applications dependencies... including its own tiny operating system! 11 | 12 | This means your application won't break when you port it to a new environment. Your app brings its environment with it. 13 | 14 | Here are some examples of things you can do with containers: 15 | 16 | - Package an analysis pipeline so that it runs on your laptop, in the cloud, and in a high performance computing (HPC) environment to produce the same result. 17 | - Publish a paper and include a link to a container with all of the data and software that you used so that others can easily reproduce your results. 18 | - Install and run an application that requires a complicated stack of dependencies with a few keystrokes. 19 | - Create a pipeline or complex workflow where each individual program is meant to run on a different operating system. 20 | 21 | ## How do containers differ from virtual machines (VMs) 22 | 23 | Containers and VMs are both types of virtualization. But it's important to understand the differences between the two and know when to use each. 24 | 25 | **Virtual Machines** install every last bit of an operating system (OS) right down to the core software that allows the OS to control the hardware (called the _kernel_). This means that VMs: 26 | - Are complete in the sense that you can use a VM to interact with your computer via a different OS. 27 | - Are extremely flexible. For instance you an install a Windows VM on a Mac using software like [VirtualBox](https://www.virtualbox.org/wiki/VirtualBox). 28 | - Are slow and resource hungry. Every time you start a VM it has to bring up an entirely new OS. 29 | 30 | **Containers** share a kernel with the host OS. This means that Containers: 31 | - Are less flexible than VMs. For example, a Linux container must be run on a Linux host OS. (Although you can mix and match distributions.) In practice, containers are only extensively developed on Linux. 32 | - Are much faster and lighter weight than VMs. A container may be just a few MB. 33 | - Start and stop quickly and are suitable for running single apps. 34 | 35 | Because of their differences, VMs and containers serve different purposes and should be favored under different circumstances. 36 | - VMs are good for long running interactive sessions where you may want to use several different applications. (Checking email on Outlook and using Microsoft Word and Excel). 37 | - Containers are better suited to running one or two applications, often non-interactively, in their own custom environments. 38 | 39 | ## Docker 40 | 41 | [Docker](https://www.docker.com/) is currently the most widely used container software. It has several strengths and weaknesses that make it a good choice for some projects but not for others. 42 | 43 | **philosophy** 44 | 45 | Docker is built for running multiple containers on a single system and it allows containers to share common software features for efficiency. It also seeks to fully isolate each container from all other containers and from the host system. 46 | 47 | Docker assumes that you will be a root user. Or that it will be OK for you to elevate your privileges if you are not a root user. 48 | See https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface for details. 49 | 50 | **strengths** 51 | 52 | - Mature software with a large user community 53 | - [Docker Hub](https://hub.docker.com/)! 54 | - A place to build and host your containers 55 | - Fully integrated into core Docker 56 | - Over 100,000 pre-built containers 57 | - Provides an ecosystem for container orchestration 58 | - Rich feature set 59 | 60 | **weaknesses** 61 | 62 | - Difficult to learn 63 | - Hidden innards 64 | - Complex container model (layers) 65 | - Not architected with security in mind 66 | - Not built for HPC (but good for cloud) 67 | 68 | Docker shines for DevOPs teams providing cloud-native micro-services to users. 69 | 70 | ## Singularity 71 | 72 | [Singularity](http://singularity.lbl.gov/) is a relatively new container software invented by Greg Kurtzer while at Lawrence Berkley National labs and now developed by his company [Sylabs](sylabs.io). It was developed with security, scientific software, and HPC systems in mind. 73 | 74 | **philosophy** 75 | 76 | Singularity assumes ([more or less](http://containers-ftw.org/SCI-F/)) that each application will have its own container. It does not seek to fully isolate containers from one another or the host system. 77 | 78 | Singularity assumes that you will have a build system where you are the root user, but that you will also have a production system where you may or may not be the root user. 79 | 80 | **strengths** 81 | - Easy to learn and use (relatively speaking) 82 | - Approved for HPC ([installed on some of the biggest HPC systems in the world](http://singularity.lbl.gov/citation-registration#clusters)) 83 | - Can convert Docker containers to Singularity and run containers directly from Docker Hub 84 | - [Singularity Container Services](https://cloud.sylabs.io/home)! 85 | - A place to build and share your containers securely 86 | 87 | weaknesses 88 | - Younger and less mature than Docker 89 | - Smaller user community (as of now) 90 | - Under active development (must keep up with new changes) 91 | 92 | Singularity shines for scientific software running in an HPC environent. We will use it for the remainder of the class. 93 | 94 | --- 95 | <- [previous](/) - [home](https://singularity-tutorial.github.io/) - [next](/01-installation) -> -------------------------------------------------------------------------------- /01-installation/README.md: -------------------------------------------------------------------------------- 1 | <- [previous](/00-introduction) - [home](https://singularity-tutorial.github.io/) - [next](/02-basic-usage) -> 2 | 3 | --- 4 | # Installing Singularity 5 | Here we will install the latest tagged release from [GitHub](https://github.com/sylabs/singularity/releases). If you prefer to install a different version or to install Singularity in a different location, see these [Singularity docs](https://sylabs.io/guides/3.5/admin-guide/installation.html). 6 | 7 | We're going to compile Singularity from source code. First we'll need to make sure we have some development tools and libraries installed so that we can do that. On Ubuntu, run these commands to make sure you have all the necessary packages installed. 8 | 9 | ``` 10 | $ sudo apt-get update 11 | 12 | $ sudo apt-get install -y build-essential libssl-dev uuid-dev libgpgme11-dev \ 13 | squashfs-tools libseccomp-dev wget pkg-config git cryptsetup debootstrap 14 | ``` 15 | 16 | On CentOS, these commmands should get you up to speed. 17 | 18 | ``` 19 | $ sudo yum -y update 20 | 21 | $ sudo yum -y groupinstall 'Development Tools' 22 | 23 | $ sudo yum -y install wget epel-release 24 | 25 | $ sudo yum -y install debootstrap.noarch squashfs-tools openssl-devel \ 26 | libuuid-devel gpgme-devel libseccomp-devel cryptsetup-luks 27 | ``` 28 | 29 | Singularity v3.0 was completely re-written in [Go](https://golang.org/). We will need to install the Go language so that we can compile Singularity. This procedure consists of downloading Go in a compressed archive, extracting it to `/usr/local/go` and placing the appropriate directory in our `PATH`. For more details, check out the [Go Downloads page](https://golang.org/dl/). 30 | 31 | ``` 32 | $ wget https://dl.google.com/go/go1.13.linux-amd64.tar.gz 33 | 34 | $ sudo tar --directory=/usr/local -xzvf go1.13.linux-amd64.tar.gz 35 | 36 | $ export PATH=/usr/local/go/bin:$PATH 37 | ``` 38 | 39 | Next we'll download a compressed archive of the source code (using the the `wget` command). Then we'll extract the source code from the archive (with the `tar` command). 40 | 41 | ``` 42 | $ wget https://github.com/singularityware/singularity/releases/download/v3.5.3/singularity-3.5.3.tar.gz 43 | 44 | $ tar -xzvf singularity-3.5.3.tar.gz 45 | ``` 46 | 47 | Finally it's time to build and install! 48 | 49 | ``` 50 | $ cd singularity 51 | 52 | $ ./mconfig 53 | 54 | $ cd builddir 55 | 56 | $ make 57 | 58 | $ sudo make install 59 | ``` 60 | 61 | If you want support for tab completion of Singularity commands, you need to source the appropriate file and add it to the bash completion directory in `/etc` so that it will be sourced automatically when you start another shell. 62 | 63 | ``` 64 | $ . etc/bash_completion.d/singularity 65 | 66 | $ sudo cp etc/bash_completion.d/singularity /etc/bash_completion.d/ 67 | ``` 68 | 69 | If everything went according to plan, you now have a working installation of Singularity. 70 | Simply typing `singularity` will give you a summary of all the commands you can use. 71 | Typing `singularity help ` will give you more detailed information about running an individual command. 72 | 73 | You can test your installation like so: 74 | 75 | ``` 76 | $ singularity run library://godlovedc/funny/lolcow 77 | ``` 78 | 79 | You should see something like the following. 80 | 81 | ``` 82 | INFO: Downloading library image 83 | _______________________________________ 84 | / Excellent day for putting Slinkies on \ 85 | \ an escalator. / 86 | --------------------------------------- 87 | \ ^__^ 88 | \ (oo)\_______ 89 | (__)\ )\/\ 90 | ||----w | 91 | || || 92 | ``` 93 | 94 | Your cow will likely say something different (and be more colorful), but as long as you see a cow your installation is working properly. 95 | 96 | This command downloads and "runs" a container from [Singularity Container Library](https://cloud.sylabs.io/library). (We'll be talking more about what it means to "run" a container later on in the class.) 97 | 98 | In a following exercise, we will learn how to build a similar container from scratch. But right now, we are going to use this container to execute a bunch of basic commands and just get a feel for what it's like to use Singularity. 99 | 100 | --- 101 | <- [previous](/00-introduction) - [home](https://singularity-tutorial.github.io/) - [next](/02-basic-usage) -> 102 | -------------------------------------------------------------------------------- /02-basic-usage/README.md: -------------------------------------------------------------------------------- 1 | <- [previous](/01-installation) - [home](https://singularity-tutorial.github.io/) - [next](/03-building) -> 2 | 3 | --- 4 | # Downloading and Interacting with Containers 5 | 6 | This section will be useful for container consumers. (i.e. those who really just want to use containers somebody else built.) The next chapter will explore topics more geared toward container producers (i.e. those who want/need to build containers from scratch). 7 | 8 | You can find pre-built containers in lots of places. Singularity can convert and run containers in many different formats, including those built by Docker. 9 | 10 | In this class, we'll be using containers from: 11 | 12 | - [The Singularity Container Library](https://cloud.sylabs.io/library), developed and maintained by [Sylabs](https://sylabs.io/) 13 | - [Docker Hub](https://hub.docker.com/), developed and maintained by [Docker](https://www.docker.com/) 14 | 15 | There are lots of other places to find pre-build containers too. Here are some of the more popular ones: 16 | 17 | - [Singularity Hub](https://singularity-hub.org/), an early collaboration between Stanford University and the Singularity community 18 | - [Quay.io](https://quay.io/), developed and maintained by Red Hat 19 | - [NGC](https://ngc.nvidia.com/catalog/all?orderBy=modifiedDESC&pageNumber=3&query=&quickFilter=&filters=), developed and maintained by NVIDIA 20 | - [BioContainers](https://biocontainers.pro/#/registry), develped and maintained by the Bioconda group 21 | - Cloud providers like Amazon AWS, Microsoft Azure, and Google cloud also have container registries that can work with Singularity 22 | 23 | ## Downloading containers 24 | 25 | In the last section, we validated our Singularity installation by "running" a container from the Container Library. Let's download that container using the `pull` command. 26 | 27 | ``` 28 | $ cd ~ 29 | 30 | $ singularity pull library://godlovedc/funny/lolcow 31 | ``` 32 | 33 | You'll see a warning about running `singularity verify` to make sure that the container is trusted. We'll talk more about that later. 34 | 35 | For now, notice that you have a new file in your current working directory called `lolcow_latest.sif` 36 | 37 | ``` 38 | $ ls lolcow_latest.sif 39 | lolcow_latest.sif 40 | ``` 41 | 42 | This is your container. Or more precisely, it is a Singularity Image Format (SIF) file containing an image of a root level filesystem. This image is mounted to your host filesystem (in a new "mount namespace") and then entered when you run a Singularity command. 43 | 44 | Note that you can download the Docker version of this same container from Docker Hub with the following command: 45 | 46 | ``` 47 | $ singularity pull docker://godlovedc/lolcow 48 | ``` 49 | 50 | Doing so may produce an error if the container already exists. 51 | 52 | ## Entering containers with `shell` 53 | 54 | Now let's enter our new container and look around. We can do so with the `shell` command. 55 | 56 | ``` 57 | $ singularity shell lolcow_latest.sif 58 | ``` 59 | 60 | Depending on the environment of your host system you may see your shell prompt change. Let's look at what OS is running inside the container. 61 | 62 | ``` 63 | $ cat /etc/os-release 64 | NAME="Ubuntu" 65 | VERSION="16.04.5 LTS (Xenial Xerus)" 66 | ID=ubuntu 67 | ID_LIKE=debian 68 | PRETTY_NAME="Ubuntu 16.04.5 LTS" 69 | VERSION_ID="16.04" 70 | HOME_URL="http://www.ubuntu.com/" 71 | SUPPORT_URL="http://help.ubuntu.com/" 72 | BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" 73 | VERSION_CODENAME=xenial 74 | UBUNTU_CODENAME=xenial 75 | ``` 76 | 77 | No matter what OS is running on your host, your container is running Ubuntu 16.04 (Xenial Xerus)! 78 | 79 | --- 80 | **NOTE** 81 | 82 | In general, the Singularity action commands (like `shell`, `run`, and `exec`) are expected to work with URIs like `library://` and `docker://` the same as they would work with a local image. 83 | 84 | --- 85 | 86 | 87 | Let's try a few more commands: 88 | 89 | ``` 90 | Singularity> whoami 91 | dave 92 | 93 | Singularity> hostname 94 | hal-9000 95 | ``` 96 | 97 | This is one of the core features of Singularity that makes it so attractive from a security and usability standpoint. The user remains the same inside and outside of the container. 98 | 99 | Regardless of whether or not the program `cowsay` is installed on your host system, you have access to it now because it is installed inside of the container: 100 | 101 | ``` 102 | Singularity> which cowsay 103 | /usr/games/cowsay 104 | 105 | Singularity> cowsay moo 106 | _____ 107 | < moo > 108 | ----- 109 | \ ^__^ 110 | \ (oo)\_______ 111 | (__)\ )\/\ 112 | ||----w | 113 | || || 114 | ``` 115 | 116 | We'll be getting a lot of mileage out of this silly little program as we explore Linux containers. 117 | 118 | This is the command that is executed when the container actually "runs": 119 | 120 | ``` 121 | Singularity> fortune | cowsay | lolcat 122 | ____________________________________ 123 | / A horse! A horse! My kingdom for a \ 124 | | horse! | 125 | | | 126 | \ -- Wm. Shakespeare, "Richard III" / 127 | ------------------------------------ 128 | \ ^__^ 129 | \ (oo)\_______ 130 | (__)\ )\/\ 131 | ||----w | 132 | || || 133 | ``` 134 | 135 | More on "running" the container in a minute. For now, don't forget to `exit` the container when you are finished playing! 136 | 137 | ``` 138 | Singularity> exit 139 | exit 140 | ``` 141 | 142 | ## Executing containerized commands with `exec` 143 | 144 | Using the `exec` command, we can run commands within the container from the host system. 145 | 146 | ``` 147 | $ singularity exec lolcow_latest.sif cowsay 'How did you get out of the container?' 148 | _______________________________________ 149 | < How did you get out of the container? > 150 | --------------------------------------- 151 | \ ^__^ 152 | \ (oo)\_______ 153 | (__)\ )\/\ 154 | ||----w | 155 | || || 156 | ``` 157 | 158 | In this example, singularity entered the container, ran the `cowsay` command with supplied arguments, displayed the standard output on our host system terminal, and then exited. 159 | 160 | ## "Running" a container with (and without) `run` 161 | 162 | As mentioned several times you can "run" a container like so: 163 | 164 | ``` 165 | $ singularity run lolcow_latest.sif 166 | _________________________________________ 167 | / Q: How many Bell Labs Vice Presidents \ 168 | | does it take to change a light bulb? A: | 169 | | That's proprietary information. Answer | 170 | | available from AT&T on payment of | 171 | \ license fee (binary only). / 172 | ----------------------------------------- 173 | \ ^__^ 174 | \ (oo)\_______ 175 | (__)\ )\/\ 176 | ||----w | 177 | || || 178 | ``` 179 | 180 | So what actually happens when you run a container? There is a special file within the container called a `runscript` that is executed when a container is run. You can see this (and other meta-data about the container) using the inspect command. 181 | 182 | ``` 183 | $ singularity inspect --runscript lolcow_latest.sif 184 | #!/bin/sh 185 | 186 | fortune | cowsay | lolcat 187 | ``` 188 | 189 | In this case the `runscript` consists of three simple commands with the output of each command piped to the subsequent command. 190 | 191 | Because Singularity containers have pre-defined actions that they must carry out when run, they are actually executable. Note the default permissions when you download or build a container: 192 | 193 | ``` 194 | $ ls -l lolcow_latest.sif 195 | -rwxr-xr-x 1 student student 93574075 Feb 28 23:02 lolcow_latest.sif 196 | ``` 197 | 198 | This allows you to run execute a container like so: 199 | 200 | ``` 201 | $ ./lolcow_latest.sif 202 | ________________________________________ 203 | / It is by the fortune of God that, in \ 204 | | this country, we have three benefits: | 205 | | freedom of speech, freedom of thought, | 206 | | and the wisdom never to use either. | 207 | | | 208 | \ -- Mark Twain / 209 | ---------------------------------------- 210 | \ ^__^ 211 | \ (oo)\_______ 212 | (__)\ )\/\ 213 | ||----w | 214 | || || 215 | ``` 216 | As we shall see later, this nifty trick can makes it easy to forget your applications are containerized and just run them like any old program. 217 | 218 | ## Pipes and redirection 219 | 220 | Singularity does not try to isolate your container completely from the host system. This allows you to do some interesting things. For instance, you can use pipes and redirection to blur the lines between the container and the host system. 221 | 222 | ``` 223 | $ singularity exec lolcow_latest.sif cowsay moo > cowsaid 224 | 225 | $ cat cowsaid 226 | _____ 227 | < moo > 228 | ----- 229 | \ ^__^ 230 | \ (oo)\_______ 231 | (__)\ )\/\ 232 | ||----w | 233 | || || 234 | ``` 235 | 236 | We created a file called `cowsaid` in the current working directory with the output of a command that was executed within the container. >_shock and awe_ 237 | 238 | We can also pipe things _into_ the container (and that is very tricky). 239 | 240 | ``` 241 | $ cat cowsaid | singularity exec lolcow_latest.sif cowsay -n 242 | ______________________________ 243 | / _____ \ 244 | | < moo > | 245 | | ----- | 246 | | \ ^__^ | 247 | | \ (oo)\_______ | 248 | | (__)\ )\/\ | 249 | | ||----w | | 250 | \ || || / 251 | ------------------------------ 252 | \ ^__^ 253 | \ (oo)\_______ 254 | (__)\ )\/\ 255 | ||----w | 256 | || || 257 | ``` 258 | 259 | We've created a meta-cow (a cow that talks about cows). ;-P 260 | 261 | So pipes and redirects work as expected between a container and the host system. If, however, you need to pipe the output of one command in your container to another command in your container, things are slightly more complicated. Pipes and redirects are shell constructs, so if you don't want your host shell to interpret them, you have to hide them from it. 262 | 263 | ``` 264 | $ singularity exec lolcow_latest.sif sh -c "fortune | cowsay | lolcat" 265 | ``` 266 | 267 | The above invokes a new shell, but inside the container, and tells it to run the single command line `fortune | cowsay | lolcat`. 268 | 269 | That covers the basics on how to download and use pre-built containers! In the next section we'll start learning how to build your own containers. 270 | 271 | --- 272 | <- [previous](/01-installation) - [home](https://singularity-tutorial.github.io/) - [next](/03-building) -> -------------------------------------------------------------------------------- /03-building/README.md: -------------------------------------------------------------------------------- 1 | 2 | <- [previous](/02-basic-usage) - [home](https://singularity-tutorial.github.io/) - [next](/04-the-ecosystem) -> 3 | 4 | --- 5 | # Building a Basic Container 6 | 7 | In this section, we will build a brand new container similar to the lolcow container we've been using in the previous examples. 8 | 9 | To build a singularity container, you must use the `build` command. The `build` command installs an OS, sets up your container's environment and installs the apps you need. To use the `build` command, we need a definition file. A [Singularity definition file](https://sylabs.io/guides/3.5/user-guide/definition_files.html) is a set of instructions telling Singularity what software to install in the container. 10 | 11 | We are going to use a standard development cycle (sometimes referred to as Singularity flow) to create this container. It consists of the following steps: 12 | 13 | - create a writable container (called a `sandbox`) 14 | - shell into the container with the `--writable` option and tinker with it interactively 15 | - record changes that we like in our definition file 16 | - rebuild the container from the definition file if we break it 17 | - rinse and repeat until we are happy with the result 18 | - rebuild the container from the final definition file as a read-only singularity image format (SIF) image for use in production 19 | 20 | The Singularity source code contains several example definition files in the `/examples` subdirectory. Let's make a new directory, copy the Debian example definition file, and inspect it. 21 | 22 | ``` 23 | $ mkdir ~/lolcow 24 | 25 | $ cp ~/singularity/examples/debian/Singularity ~/lolcow/lolcow.def 26 | 27 | $ cd ~/lolcow 28 | 29 | $ nano lolcow.def 30 | ``` 31 | 32 | It should look like this: 33 | 34 | ``` 35 | BootStrap: debootstrap 36 | OSVersion: stable 37 | MirrorURL: http://ftp.us.debian.org/debian/ 38 | 39 | %runscript 40 | echo "This is what happens when you run the container..." 41 | 42 | %post 43 | echo "Hello from inside the container" 44 | apt-get -y --allow-unauthenticated install vim 45 | ``` 46 | 47 | See the [Singularity docs](https://sylabs.io/guides/3.5/user-guide/definition_files.html) for an explanation of each of these sections. 48 | 49 | ## Developing a new container 50 | 51 | Now let's use this definition file as a starting point to build our `lolcow.img` container. Note that the build command requires `sudo` privileges. (We'll discuss some ways around this restriction later in the class.) 52 | 53 | ``` 54 | $ sudo singularity build --sandbox lolcow lolcow.def 55 | ``` 56 | 57 | This is telling Singularity to build a container called `lolcow` from the `lolcow.def` definition file. The `--sandbox` option in the command above tells Singularity that we want to build a special type of container (called a sandbox) for development purposes. 58 | 59 | Singularity can build containers in several different file formats. The default is to build a SIF (singularity image format) container that uses [squashfs](https://en.wikipedia.org/wiki/SquashFS) for the file system. SIF files are compressed and immutable making them the best choice for reproducible, production-grade containers. 60 | 61 | But if you want to shell into a container and tinker with it (like we will do here), you should build a sandbox (which is really just a directory). This is great when you are still developing your container and don't yet know what to include in the definition file. 62 | 63 | When your build finishes, you will have a basic Debian container saved in a local directory called `lolcow`. 64 | 65 | ## Using `shell --writable` to explore and modify containers 66 | 67 | Now let's enter our new container and look around. 68 | 69 | ``` 70 | $ singularity shell lolcow 71 | ``` 72 | 73 | Depending on the environment on your host system you may see your prompt change. 74 | 75 | Let's try installing some software. I used the programs `fortune`, `cowsay`, and `lolcat` to produce the container that we saw in the first demo. 76 | 77 | ``` 78 | Singularity> sudo apt-get update 79 | bash: sudo: command not found 80 | ``` 81 | 82 | Whoops! 83 | 84 | The `sudo` command is not found. But even if we had installed `sudo` into the 85 | container and tried to run this command with it, or change to root using `su`, 86 | we would still find it impossible to elevate our privileges within the 87 | container: 88 | 89 | ``` 90 | Singularity> sudo apt-get update 91 | sudo: effective uid is not 0, is /usr/bin/sudo on a file system with the 'nosuid' option set or an NFS file system without root privileges? 92 | ``` 93 | 94 | Once again, this is an important concept in Singularity. If you enter a container without root privileges, you are unable to obtain root privileges within the container. This insurance against privilege escalation is the reason that you will find Singularity installed in so many HPC environments. 95 | 96 | Let's exit the container and re-enter as root. 97 | 98 | ``` 99 | Singularity> exit 100 | 101 | $ sudo singularity shell --writable lolcow 102 | ``` 103 | 104 | Now we are the root user inside the container. Note also the addition of the `--writable` option. This option allows us to modify the container. The changes will actually be saved into the container and will persist across uses. 105 | 106 | Let's try installing our software again. 107 | 108 | ``` 109 | Singularity> apt-get update 110 | 111 | Singularity> apt-get install -y fortune cowsay lolcat 112 | ``` 113 | 114 | Now you should see the programs successfully installed. Let's try running the demo in this new container. 115 | 116 | ``` 117 | Singularity> fortune | cowsay | lolcat 118 | bash: lolcat: command not found 119 | bash: cowsay: command not found 120 | bash: fortune: command not found 121 | ``` 122 | 123 | Drat! 124 | 125 | It looks like the programs were not added to our `$PATH`. Let's add them and try again. 126 | 127 | ``` 128 | Singularity> export PATH=$PATH:/usr/games 129 | 130 | Singularity lolcow:~> fortune | cowsay | lolcat 131 | ________________________________________ 132 | / Keep emotionally active. Cater to your \ 133 | \ favorite neurosis. / 134 | ---------------------------------------- 135 | \ ^__^ 136 | \ (oo)\_______ 137 | (__)\ )\/\ 138 | ||----w | 139 | || || 140 | ``` 141 | 142 | Great! Things are working properly now. 143 | 144 | --- 145 | **NOTE** 146 | 147 | If you receive warnings from the Perl language about the `locale` being incorrect, you can usually fix them with `export LC_ALL=C`. 148 | 149 | --- 150 | 151 | --- 152 | **NOTE** 153 | 154 | We changed our path in this session, but those changes will disappear as soon as we exit the container just like they will when you exit any other shell. To make the changes permanent we should add them to the definition file and re-bootstrap the container. We'll do that in a minute. 155 | 156 | --- 157 | 158 | ## Building the final production-grade SIF file 159 | 160 | Although it is fine to shell into your Singularity container and make changes while you are debugging, you ultimately want all of these changes to be reflected in your definition file. Otherwise if you need to reproduce it from scratch you will forget all of the changes you made. You will also want to rebuild you container into something more durable, portable, and robust than a directory. 161 | 162 | Let's update our definition file with the changes we made to this container. 163 | 164 | ``` 165 | Singularity> exit 166 | 167 | $ nano lolcow.def 168 | ``` 169 | 170 | Here is what our updated definition file should look like. 171 | 172 | ``` 173 | BootStrap: debootstrap 174 | OSVersion: stable 175 | MirrorURL: http://ftp.us.debian.org/debian/ 176 | 177 | %runscript 178 | echo "This is what happens when you run the container..." 179 | 180 | %post 181 | echo "Hello from inside the container" 182 | apt-get update 183 | apt-get -y install fortune cowsay lolcat 184 | 185 | %environment 186 | export PATH=$PATH:/usr/games 187 | ``` 188 | 189 | Let's rebuild the container with the new definition file. 190 | 191 | ``` 192 | $ sudo singularity build lolcow.sif lolcow.def 193 | ``` 194 | 195 | Note that we changed the name of the container. By omitting the `--sandbox` option, we are building our container in the standard Singularity file format (SIF). We are denoting the file format with the (optional) `.sif` extension. A SIF file is compressed and immutable making it a good choice for a production environment. 196 | 197 | As we saw in the previous section when we used the `inspect` command to read the `runscript`, Singularity stores a lot of [useful metadata](https://sylabs.io/guides/3.5/user-guide/environment_and_metadata.html#container-metadata). For instance, if you want to see the definition file that was used to create the container you can use the `inspect` command like so: 198 | 199 | ``` 200 | $ singularity inspect --deffile lolcow.sif 201 | BootStrap: debootstrap 202 | OSVersion: stable 203 | MirrorURL: http://ftp.us.debian.org/debian/ 204 | 205 | %runscript 206 | echo "This is what happens when you run the container..." 207 | 208 | %post 209 | apt-get update 210 | apt-get -y install fortune cowsay lolcat 211 | 212 | %environment 213 | export PATH=$PATH:/usr/games 214 | ``` 215 | 216 | ## Building from existing containers 217 | 218 | In the preceding section we always used the following header in our definition file to build a container: 219 | 220 | ``` 221 | BootStrap: debootstrap 222 | OSVersion: stable 223 | MirrorURL: http://ftp.us.debian.org/debian/ 224 | ``` 225 | 226 | This uses the program [`debootstrap`](https://wiki.debian.org/Debootstrap) to build the root file system using a mirror URL. In this case, we supply a URL that is maintained by Debian. We could also use an Ubuntu URL since it is a derivative of Debian and can also be built with the `debootstrap` program. If we wanted to build a CentOS container from the distribution mirror we could use the `yum` package manager similarly. There are actually a ton of different ways to build containers. See this list of ["bootstrap agents"](https://sylabs.io/guides/3.5/user-guide/appendix.html#build-modules) in the Singularity docs. 227 | 228 | In practice, most people do not build containers from a distribution mirror like this. Instead they tend to build containers from existing containers on the Container Library or on Docker Hub and use the `%post` section to modify those containers to suit their needs. 229 | 230 | For instance, to use an existing Debian container from the Container library as your starting point, your header would look like this: 231 | 232 | 233 | ``` 234 | BootStrap: library 235 | From: debian 236 | ``` 237 | 238 | Likewise to start from a debian container on Docker Hub, your header would contain the following: 239 | 240 | ``` 241 | Bootstrap: docker 242 | From: debian 243 | ``` 244 | 245 | You can also build a container from a base container on your local file system. 246 | 247 | ``` 248 | Bootstrap: localimage 249 | From: /home/student/debian.sif 250 | ``` 251 | 252 | Each of these methods can also be called _without_ providing a definition file using the following shorthand. For an added bonus, none of these `build` commands require root privileges. 253 | 254 | ``` 255 | $ singularity build debian1.sif library://debian 256 | 257 | $ singularity build debian2.sif docker://debian 258 | 259 | $ singularity build debian3.sif debian2.sif 260 | ``` 261 | 262 | Behind the scenes, Singularity creates a small definition file for each of these commands and then builds the corresponding container as you can see if you use the `inspect --deffile` command. 263 | 264 | ``` 265 | $ singularity inspect --deffile debian1.sif 266 | bootstrap: library 267 | from: debian 268 | 269 | $ singularity inspect --deffile debian2.sif 270 | bootstrap: docker 271 | from: debian 272 | 273 | $ singularity inspect --deffile debian3.sif 274 | bootstrap: localimage 275 | from: debian2.sif 276 | ``` 277 | 278 | Note that the third command may not seem very useful because you are just copying the container called `debian2.sif` to a new container called `debian3.sif`. But you can also use `build` in this way to convert a SIF file to a sandbox and back again: 279 | 280 | ``` 281 | $ singularity build --sandbox deb-sand debian3.sif 282 | 283 | $ singularity build deb-sif deb-sand/ 284 | ``` 285 | 286 | This can be a useful trick during container development. But it can also produce a container with an uncertain build history if it is misapplied because the changes made to the sandbox will not be reflected in the containers definition file. 287 | 288 | ## Security considerations and `--fakeroot` 289 | 290 | In the preceding we've been executing the `build` command as root via `sudo`. In our examples, that is a reasonably safe thing to do, because we use disposable virtual machines for this class and we are building directly from mirrors hosted by same groups that create the OS distributions. (Though [mirrors can still contain malware](https://lists.archlinux.org/pipermail/aur-general/2018-July/034169.html).) 291 | 292 | But in general, it's a bad idea to build a container as root. In particular you should never build a container from an untrusted base image as root on a machine you care about. This is the same as downloading random code from the internet and running it as root on your machine. (See [this blog](https://medium.com/sylabs/a-note-on-cve-2019-14271-running-untrusted-containers-as-root-is-still-a-bad-idea-245d227d4e02) for a technical discussion.) 293 | 294 | On operating systems with recent kernels (such as Ubuntu 18.04), you can invoke the `--fakeroot` option when building containers instead. (For those interested in technical details, this feature leverages the [user namespace](http://man7.org/linux/man-pages/man7/user_namespaces.7.html)). 295 | 296 | ``` 297 | $ singularity build --fakeroot container.sif container.def 298 | ``` 299 | 300 | Doing so allows you to pretend to be the root user inside of your container without actually granting singularity elevated privileges on host system. This is a much safer way to build and interact with your container, and it is going to become more prevalent (eventually probably even default) as more distributions ship with user namespaces enabled. For instance, this feature is enabled by default in RHEL 8. 301 | 302 | For more about the `--fakeroot` option, see [the Singularity documentation](https://sylabs.io/guides/3.5/user-guide/fakeroot.html?highlight=fakeroot). 303 | 304 | --- 305 | <- [previous](/02-basic-usage) - [home](https://singularity-tutorial.github.io/) - [next](/04-the-ecosystem) -> -------------------------------------------------------------------------------- /04-the-ecosystem/README.md: -------------------------------------------------------------------------------- 1 | <- [previous](/03-building) - [home](https://singularity-tutorial.github.io/) - [next](/05-bind-mounts) -> 2 | 3 | --- 4 | # The Singularity Ecosystem 5 | 6 | We've spent a lot of time on building and using your own containers so that you understand how Singularity works. Now let's talk more about the [Singularity Container Services](https://cloud.sylabs.io/home) and [Docker Hub](https://hub.docker.com/). 7 | 8 | [Docker Hub](https://hub.docker.com/) hosts over 100,000 pre-built, ready-to-use containers. And the Container Library has a large and growing number of pre-built containers. We've already talked about pulling and building containers from Docker Hub and the Container Library, but there are more details you should be aware of. 9 | 10 | ## Tags and hashes 11 | 12 | First, Docker Hub and the Container Library both have a concept of a tagged image. Tags make it convenient for developers to release several different versions of the same container. For instance, if you wanted to specify that you need Debian version 9, you could do so like this: 13 | 14 | ``` 15 | $ singularity pull library://debian:9 16 | ``` 17 | 18 | Or within a definition file: 19 | 20 | ``` 21 | Bootstrap: library 22 | From: debian:9 23 | ``` 24 | The syntax is similar to specify a tagged container from Docker Hub. 25 | 26 | There is a _special_ tag in both the Singularity Library and Docker Hub called **latest**. If you omit the `:` suffix from your `pull` or `build` command or from within your definition file you will get the container tagged with `latest` by default. This sometimes causes confusion if the `latest` tag doesn't exist for a particular container and an error is encountered. In that case a tag must be supplied. 27 | 28 | Tags are not immutable and may change without warning. For insance, the latest tag is automatically assigned to the latest build of a container in Docker Hub. So pulling by tag (or pulling `latest` by default) can result in your pulling 2 different images with the same command. If you are interested in pulling the same container multiple times, you should pull by the hash. Continuing with our Debian 9 example, this will ensure that you get the same one even if the developers change that tag: 29 | 30 | ``` 31 | $ singularity pull library://debian:sha256.b92c7fdfcc6152b983deb9fde5a2d1083183998c11fb3ff3b89c0efc7b240448 32 | ``` 33 | 34 | The syntax to do the same from Docker Hub is a bit different: 35 | 36 | ``` 37 | $ singularity pull docker://debian@sha256:f17410575376cc2ad0f6f172699ee825a51588f54f6d72bbfeef6e2fa9a57e2f 38 | ``` 39 | 40 | ## Default entities and collections 41 | 42 | Let's think about this command: 43 | 44 | ``` 45 | $ singularity pull library://debian 46 | ``` 47 | 48 | When you run that command there are several default values that are provided for you to allow Singularity to build an entire URI. This is what the full command actually looks like: 49 | 50 | ``` 51 | $ singularity pull library://library/default/debian:latest 52 | ``` 53 | 54 | This container is being pulled from the URI `library`, the entity `library`, the collection `default`, and the tag `latest`. If you try this shorthand version of the command with the `lolcow` container, you will find that it fails: 55 | 56 | ``` 57 | $ singularity pull library://lolcow 58 | FATAL: While pulling library image: image lolcow:latest (amd64) does not exist in the library 59 | ``` 60 | 61 | There is no default container called `lolcow` within the `library` and `default` entity and collection. For that container to work properly, you must supply the entity (`godlovedc`) and the collection (`funny`) like so: 62 | 63 | ``` 64 | $ singularity pull library://godlovedc/funny/lolcow 65 | ``` 66 | 67 | Similarly, when pulling from Docker Hub there are some intelligent defaults supplied. Consider the following command: 68 | 69 | ``` 70 | $ singularity pull docker://godlovedc/lolcow 71 | ``` 72 | 73 | When executed this is the command that Singularity actually acts on: 74 | 75 | ``` 76 | $ singularity pull docker://index.docker.io/godlovedc/lolcow:latest 77 | ``` 78 | 79 | In this example the registry (`index.docker.io`) and the tag (`latest`) are implied. When downloading special images like Debian and Ubuntu, the user (`godovedc` in the above command) can also be implied. These values may need to be manually supplied for some containers on Docker Hub or to download from different registries like Quay.io. 80 | 81 | ## Using trusted containers 82 | 83 | When you build and or run a container, you are running someone else's code on your system. Doing so comes with certain inherent security risks. The blog posts [here](https://medium.com/sylabs/cve-2019-5736-and-its-impact-on-singularity-containers-8c6272b4bce6) and [here](https://medium.com/sylabs/a-note-on-cve-2019-14271-running-untrusted-containers-as-root-is-still-a-bad-idea-245d227d4e02) provide some background on the kinds of security concerns containers can cause. 84 | 85 | Container security is a large topic and we cannot cover all of the facets in this class, but here are a few general guidelines. 86 | 87 | - Don't build containers from untrusted sources or run them as root 88 | - Review the `runscript` before you run it 89 | - Use the `--no-home` and `--contain-all` options when running an unfamiliar container 90 | - Establish your level of trust with a container 91 | 92 | The last point is particularly important and can be accomplished in a few different ways. 93 | 94 | ### Docker Hub Official and Certified images 95 | 96 | The Docker team works with upstream maintainers (like Canonical, CentOS, etc.) to create [**Official** images](https://docs.docker.com/docker-hub/official_images/). They've been reviewed by humans, scanned for vulnerabilities, and approved. 97 | 98 | There are a series of steps that upstream maintainers can perform to produce [**Certified** images](https://docs.docker.com/docker-hub/publish/certify-images/). This includes a standard of best practices and some baseline testing. 99 | 100 | ### Signing and verifying Singularity images 101 | 102 | Singularity gives image maintainers the ability to cryptographically sign images and downstream users can use builtin tools to verify that these images are bit-for-bit reproductions of the originals. This removes any dependencies on web infrastructure and prevents a specific type of time-of-check to time-of-use (TOCTOU) attack. 103 | 104 | This model also differs from the Docker model of trust because the decision of whether or not to trust a particular image is left to the user and maintainer. Sylabs does not "vouch" for a particular set of images the way that Docker does. It's up to users to obtain fingerprints from maintainers and to judge whether or not they trust a particular maintainer's image. 105 | 106 | ## Building and hosting your containers 107 | 108 | Docker Hub allows you to save a Docker File (Docker's version of a Singularity definition file) to a GitHub repo and then link that repo to a Docker Hub repo. Every time a new commit is pushed to the GitHub repo, a new container will be built on Docker Hub. 109 | 110 | For instance, the [godlovedc/lolcow](https://hub.docker.com/repository/docker/godlovedc/lolcow) container is linked to the [GodloveD/lolcow](https://github.com/GodloveD/lolcow/blob/master/Dockerfile) repo on GitHub. 111 | 112 | The [Singularity Remote Builder](https://cloud.sylabs.io/builder) offers a few different ways to build your containers. You can compose a definition file or drag-and-drop using the web GUI. Or you can log in and create an access token. This allows you to do nifty things like search the Cloud Library with the `search` command and build containers from the command line using `--remote` option. 113 | 114 | Here's a quick example. First, I'll use the `remote login` command to generate a token: 115 | 116 | ``` 117 | $ singularity remote login SylabsCloud 118 | INFO: Authenticating with remote: SylabsCloud 119 | Generate an API Key at https://cloud.sylabs.io/auth/tokens, and paste here: 120 | API Key: 121 | INFO: API Key Verified! 122 | ``` 123 | 124 | I had to actually visit the website, create the token and copy the text into the prompt (which does not echo to the screen). 125 | 126 | Now I can search for users, collections, and containers like so: 127 | 128 | ``` 129 | $ singularity search wine 130 | No users found for 'wine' 131 | 132 | No collections found for 'wine' 133 | 134 | Found 1 containers for 'wine' 135 | library://godloved/base/wine 136 | Tags: latest 137 | ``` 138 | 139 | And I can also use the `--remote` option to build my containers. Note that this **does not require root!** 140 | 141 | ``` 142 | $ cat alpine.def 143 | Bootstrap: library 144 | From: alpine 145 | 146 | %post 147 | echo "Install stuff here" 148 | 149 | $ singularity build --remote alpine.sif alpine.def 150 | INFO: Remote "default" added. 151 | INFO: Authenticating with remote: default 152 | INFO: API Key Verified! 153 | INFO: Remote "default" now in use. 154 | INFO: Starting build... 155 | INFO: Downloading library image 156 | INFO: Running post scriptlet 157 | Install stuff here 158 | + echo 'Install stuff here' 159 | INFO: Creating SIF file... 160 | INFO: Build complete: /tmp/image-302588342 161 | WARNING: Skipping container verifying 162 | 2.59 MiB / 2.59 MiB 100.00% 26.13 MiB/s 0s 163 | INFO: Build complete: alpine.sif 164 | 165 | $ ls alpine.sif 166 | alpine.sif 167 | 168 | $ singularity shell alpine.sif 169 | Singularity> cat /etc/os-release 170 | NAME="Alpine Linux" 171 | ID=alpine 172 | VERSION_ID=3.9.2 173 | PRETTY_NAME="Alpine Linux v3.9" 174 | HOME_URL="https://alpinelinux.org/" 175 | BUG_REPORT_URL="https://bugs.alpinelinux.org/" 176 | Singularity> exit 177 | student@sing-class2:~$ 178 | ``` 179 | 180 | The build happens transparently. Even though we are building on the cloud, it _looks_ like the container is built right here on our system and it downloads automatically. 181 | 182 | ## Signing and sharing containers 183 | You can generate a new PGP key with the `key` command like so: 184 | 185 | ``` 186 | $ singularity key newpair 187 | Enter your name (e.g., John Doe) : Class Admin 188 | Enter your email address (e.g., john.doe@example.com) : class.admin@mymail.com 189 | Enter optional comment (e.g., development keys) : This is an example key for a class 190 | Enter a passphrase : 191 | Retype your passphrase : 192 | Would you like to push it to the keystore? [Y,n] y 193 | Generating Entity and OpenPGP Key Pair... done 194 | Key successfully pushed to: https://keys.sylabs.io 195 | ``` 196 | 197 | This lets you cryptographically sign the container you just created with the `sign` command: 198 | 199 | ``` 200 | $ singularity sign alpine.sif 201 | Signing image: alpine.sif 202 | Enter key passphrase : 203 | Signature created and applied to alpine.sif 204 | ``` 205 | 206 | The you can push it to the library like so: 207 | 208 | ``` 209 | $ singularity push alpine.sif library://godloved/base/alpine:latest 210 | INFO: Container is trusted - run 'singularity key list' to list your trusted keys 211 | 2.59 MiB / 2.59 MiB [========================================================] 100.00% 10.72 MiB/s 0s 212 | ``` 213 | 214 | Then when others `pull` the container they can use the `verify` command to make sure that it has not been tampered with. 215 | 216 | ``` 217 | $ singularity verify alpine.sif 218 | Container is signed by 1 key(s): 219 | 220 | Verifying partition: FS: 221 | 73B905527AB1AA3929B6A736A47CBE85B37CB086 222 | [LOCAL] Class Admin (This is an example key for a class) 223 | [OK] Data integrity verified 224 | 225 | INFO: Container verified: alpine.sif 226 | ``` 227 | 228 | --- 229 | **NOTE** 230 | 231 | Anyone can sign a container. So just because a container is signed, does not mean it should be trusted. Users must obtain the fingerprint associated with a given maintainer's key and compare it with that displayed by the `verify` command to ensure that the container is authentic. After that it is up to the user to decide if they trust the maintainer. 232 | 233 | --- 234 | 235 | --- 236 | <- [previous](/03-building) - [home](https://singularity-tutorial.github.io/) - [next](/05-bind-mounts) -> -------------------------------------------------------------------------------- /05-bind-mounts/README.md: -------------------------------------------------------------------------------- 1 | <- [previous](/04-the-ecosystem) - [home](https://singularity-tutorial.github.io/) - [next](/06-runscript) -> 2 | 3 | --- 4 | # Accessing Host Files with Bind Mounts 5 | 6 | It's possible to create and modify files on the host system from within the container. In fact, that's exactly what we did in the previous example when we created output files in our home directory. 7 | 8 | Let's be more explicit. Consider this example. 9 | 10 | ``` 11 | $ singularity shell lolcow.sif 12 | 13 | Singularity> echo wutini > ~/jawa.txt 14 | 15 | Singularity> cat ~/jawa.txt 16 | wutini 17 | 18 | Singularity> exit 19 | 20 | $ cat ~/jawa.txt 21 | wutini 22 | ``` 23 | 24 | Here we shelled into a container and created a file with some text in our home directory. Even after we exited the container, the file still existed. How did this work? 25 | 26 | There are several special directories that Singularity _bind mounts_ into 27 | your container by default. These include: 28 | 29 | - `$HOME` 30 | - `/tmp` 31 | - `/proc` 32 | - `/sys` 33 | - `/dev` 34 | 35 | You can specify other directories to bind using the `--bind` option or the environmental variable `$SINGULARITY_BINDPATH` 36 | 37 | Let's say we want to access a directory called `/data` from within our container. For this example, we first need to create this new directory with some data on our host system. 38 | 39 | ``` 40 | $ sudo mkdir /data 41 | 42 | $ sudo chown $USER:$USER /data 43 | 44 | $ echo 'I am your father' > /data/vader.txt 45 | ``` 46 | 47 | Now let's see how bind mounts work. First, let's list the contents of `/data` within the container without bind mounting `/data` on the host system to it. 48 | 49 | ``` 50 | $ singularity exec lolcow.sif ls -l /data 51 | ls: cannot access '/data': No such file or directory 52 | ``` 53 | 54 | Nothing there! Now let's repeat the same command but using the `--bind` option to bind mount `/data` into the container. 55 | 56 | ``` 57 | $ singularity exec --bind /data lolcow.sif ls -l /data 58 | total 4 59 | -rw-rw-r-- 1 student student 17 Mar 2 00:51 vader.txt 60 | ``` 61 | 62 | Now a `/data` directory is created in the container and it is bind mounted to the `/data` directory on the host system. 63 | 64 | You can bind mount a source directory of one name on the host system to a destination of another name using a `source:destination` syntax, and you can bind mount multiple directories as a comma separated string. For instance: 65 | 66 | ``` 67 | $ singularity shell --bind src1:dest1,src2:dest2,src3:dest3 some.sif 68 | ``` 69 | 70 | If no colon is present, Singularity assumes the source and destination are identical. To do the same thing with an environment variable, you could do the following: 71 | 72 | ``` 73 | $ export SINGULARITY_BINDPATH=src1:dest1,src2:dest2,src3:dest3 74 | ``` 75 | 76 | For a lot more info on how to bind mount host directories to your container, check out the [NIH HPC Binding external directories](https://hpc.nih.gov/apps/singularity.html#bind) section. 77 | 78 | --- 79 | <- [previous](/04-the-ecosystem) - [home](https://singularity-tutorial.github.io/) - [next](/06-runscript) -> 80 | -------------------------------------------------------------------------------- /06-runscript/README.md: -------------------------------------------------------------------------------- 1 | <- [previous](/05-bind-mounts) - [home](https://singularity-tutorial.github.io/) - [next](/07-fake-installation) -> 2 | 3 | --- 4 | # The Runscript: Making Containerized Apps Behave Like Normal Apps 5 | 6 | Consider an application that takes one file as input, analyzes the data in the file, and produces another file as output. This is obviously a very common situation. 7 | 8 | Let's imagine that we want to use the cowsay program in our `lolcow.sif` to "analyze data". We should give our container an input file, it should reformat the text (in the form of a cow speaking), and it should dump the output into another file. 9 | 10 | Here's an example. First I'll make some "data" 11 | 12 | ``` 13 | $ echo "The grass is always greener over the septic tank" > /data/input 14 | ``` 15 | 16 | Now I'll "analyze" the "data" 17 | 18 | ``` 19 | $ cat /data/input | singularity exec lolcow.sif cowsay >/data/output 20 | ``` 21 | 22 | The "analyzed data" is saved in a file called `/data/output`. 23 | 24 | ``` 25 | $ cat /data/output 26 | ______________________________________ 27 | / The grass is always greener over the \ 28 | \ septic tank / 29 | -------------------------------------- 30 | \ ^__^ 31 | \ (oo)\_______ 32 | (__)\ )\/\ 33 | ||----w | 34 | || || 35 | ``` 36 | 37 | This _works..._ but the syntax is ugly and difficult to remember. 38 | 39 | Singularity supports a neat trick for making a container function as though it were an executable. We need to create a **runscript** inside the container. It turns out that our Singularity recipe file already contains a runscript. It causes our container to print a helpful message. 40 | 41 | ``` 42 | $ ./lolcow.simg 43 | This is what happens when you run the container... 44 | ``` 45 | 46 | Let's rewrite this runscript in the definition file and rebuild our container so that it does something more useful. And while we're at it, we'll change the bootstrap method to `library` 47 | 48 | ``` 49 | BootStrap: library 50 | From: debian:9 51 | 52 | %runscript 53 | if [ $# -ne 2 ]; then 54 | echo "Please provide an input and an output file." 55 | exit 1 56 | fi 57 | cat $1 | cowsay > $2 58 | 59 | %post 60 | apt-get update 61 | apt-get -y install fortune cowsay lolcat 62 | 63 | %environment 64 | export PATH=$PATH:/usr/games 65 | ``` 66 | 67 | Now we must rebuild out container to install the new runscript. 68 | 69 | ``` 70 | $ sudo singularity build --force lolcow.sif lolcow.def 71 | ``` 72 | 73 | Note the `--force` option which ensures our previous container is completely overwritten. 74 | 75 | After rebuilding our container, we can call the `lolcow.sif` as though it were an executable, give it input and output file names. 76 | 77 | ``` 78 | $ ./lolcow.sif /data/input /data/output2 79 | /.singularity.d/runscript: 7: /.singularity.d/runscript: cannot create /data/output2: Directory nonexistent 80 | cat: /data/input: No such file or directory 81 | ``` 82 | 83 | Whoops! 84 | 85 | We are no longer piping redirecting standard output into and out of the container, so we need to bind mount the `/data` directory into the container. It will be convenient to simply set the bind path as an environment variable. 86 | 87 | ``` 88 | $ export SINGULARITY_BINDPATH=/data 89 | 90 | $ ./lolcow.sif /data/input /data/output2 91 | 92 | $ ./lolcow.sif /data/vader.txt /data/output3 93 | 94 | $ cat /data/output2 /data/output3 95 | ______________________________________ 96 | / The grass is always greener over the \ 97 | \ septic tank / 98 | -------------------------------------- 99 | \ ^__^ 100 | \ (oo)\_______ 101 | (__)\ )\/\ 102 | ||----w | 103 | || || 104 | __________________ 105 | < I am your father > 106 | ------------------ 107 | \ ^__^ 108 | \ (oo)\_______ 109 | (__)\ )\/\ 110 | ||----w | 111 | || || 112 | ``` 113 | 114 | To summarize, we have written a runscript for our container that will do some very basic error checking and expects the location of an input file and an output file allowing it to analyze the data. This is obviously a trivial example, but the sky is the limit. If you can code it, you can make your container do it! 115 | 116 | --- 117 | **BONUS** 118 | 119 | You will often see this or something similar as a containers runscript. 120 | 121 | ``` 122 | %runscript 123 | python "$@" 124 | ``` 125 | What does this do? 126 | 127 | --- 128 | <- [previous](/05-bind-mounts) - [home](https://singularity-tutorial.github.io/) - [next](/07-fake-installation) -> -------------------------------------------------------------------------------- /07-fake-installation/README.md: -------------------------------------------------------------------------------- 1 | <- [previous](/06-runscript) - [home](https://singularity-tutorial.github.io/) - [next](/08-misc) -> 2 | 3 | --- 4 | # Faking a Native Installation within a Singularity Container 5 | 6 | How would you like to install an app with one more more commands inside of a Singularity container and then just forget it's in a container and use it like any other app? 7 | 8 | In the last example, we took a step in that direction by creating a `runscript` to accept input and output, but we still had to set an environment variable and our container was not actually using the cowsay program as written. Not to mention, it gave us zero access to the `fortune` and `lolcat` programs. In other words, it could only do what our `runscript` told it to do. 9 | 10 | In this section, we are going to look at a simple, flexible method for creating containerized app installations that you can "set and forget". This is the same method that Biowulf staff members use to install containerized applications on the NIH HPC systems. As of today, around 100 applications are installed like this, and most of them are used all the time by scientists who may never know or care that the app they depend on is actually running inside of a container! 11 | 12 | First, we will `cd` back into our `~/lolcow` directory (if we are not already there) and delete everything in it. 13 | 14 | --- 15 | **NOTE** 16 | 17 | This class is taught using virtual machines, but you should still _always_ be careful and double check your location, permissions, and mental state before issuing a `rm -rf` command. That goes _double_ when issuing the command as root. 18 | 19 | --- 20 | 21 | ``` 22 | $ cd ~/lolcow 23 | 24 | $ pwd # double check 25 | /home/student/lolcow 26 | 27 | $ sudo rm -rf * # caution! see NOTE above! 28 | ``` 29 | 30 | Now we'll make a few new directories to keep things tidy. 31 | 32 | ``` 33 | $ mkdir libexec bin 34 | ``` 35 | 36 | Next we'll create the contents of the `libexec` directory. It will contain the container, and a rather trickly little wrapper script. 37 | 38 | ``` 39 | $ singularity pull libexec/lolcow.sif library://godlovedc/funny/lolcow 40 | 41 | $ cat >libexec/lolcow.sh<<"EOF" 42 | #!/bin/bash 43 | export SINGULARITY_BINDPATH="/data" 44 | dir="$(dirname $(readlink -f ${BASH_SOURCE[0]}))" 45 | img="lolcow.sif" 46 | cmd=$(basename "$0") 47 | arg="$@" 48 | echo running: singularity exec "${dir}/${img}" $cmd $arg 49 | singularity exec "${dir}/${img}" $cmd $arg 50 | EOF 51 | 52 | $ chmod 755 libexec/lolcow.sh 53 | ``` 54 | 55 | Now for the trick. The `lolcow.sh` wrapper script is written in such a way that whatever symlinks you create to it will run inside of the container. We've temporarily given it an `echo` line to help clarify what it's doing. Let's make a few symlinks in the `bin` directory. 56 | 57 | ``` 58 | $ ln -s ../libexec/lolcow.sh bin/fortune 59 | 60 | $ ln -s ../libexec/lolcow.sh bin/cowsay 61 | 62 | $ ln -s ../libexec/lolcow.sh bin/lolcat 63 | ``` 64 | 65 | Here's what the directory should look like: 66 | 67 | ``` 68 | lolcow/ 69 | ├── bin 70 | │   ├── cowsay -> ../libexec/lolcow.sh 71 | │   ├── fortune -> ../libexec/lolcow.sh 72 | │   └── lolcat -> ../libexec/lolcow.sh 73 | └── libexec 74 | ├── lolcow.sh 75 | └── lolcow.sif 76 | ``` 77 | 78 | Now let's see how it works: 79 | 80 | ``` 81 | $ cd ~ 82 | 83 | $ export PATH=$PATH:~/lolcow/bin 84 | 85 | $ which cowsay fortune lolcat 86 | /home/student/lolcow/bin/cowsay 87 | /home/student/lolcow/bin/fortune 88 | /home/student/lolcow/bin/lolcat 89 | 90 | $ cowsay moo 91 | running: singularity exec /home/student/lolcow/libexec/lolcow.sif cowsay moo 92 | _____ 93 | < moo > 94 | ----- 95 | \ ^__^ 96 | \ (oo)\_______ 97 | (__)\ )\/\ 98 | ||----w | 99 | || || 100 | 101 | $ lolcat --help 102 | running: singularity exec /home/student/lolcow/libexec/lolcow.sif lolcat --help 103 | 104 | Usage: lolcat [OPTION]... [FILE]... 105 | 106 | Concatenate FILE(s), or standard input, to standard output. 107 | With no FILE, or when FILE is -, read standard input. 108 | 109 | --spread, -p : Rainbow spread (default: 3.0) 110 | --freq, -F : Rainbow frequency (default: 0.1) 111 | --seed, -S : Rainbow seed, 0 = random (default: 0) 112 | --animate, -a: Enable psychedelics 113 | --duration, -d : Animation duration (default: 12) 114 | --speed, -s : Animation speed (default: 20.0) 115 | --force, -f: Force color even when stdout is not a tty 116 | --version, -v: Print version and exit 117 | --help, -h: Show this message 118 | 119 | Examples: 120 | lolcat f - g Output f's contents, then stdin, then g's contents. 121 | lolcat Copy standard input to standard output. 122 | fortune | lolcat Display a rainbow cookie. 123 | 124 | Report lolcat bugs to 125 | lolcat home page: 126 | Report lolcat translation bugs to 127 | ``` 128 | 129 | Once you understand how it works, remove (or comment) the echo line in `lolcow/libexec/lolcow.sh` and even things like this should work without a hitch: 130 | 131 | ``` 132 | $ fortune | cowsay -n | lolcat 133 | ___________________________________________________________ 134 | < You never hesitate to tackle the most difficult problems. > 135 | ----------------------------------------------------------- 136 | \ ^__^ 137 | \ (oo)\_______ 138 | (__)\ )\/\ 139 | ||----w | 140 | || || 141 | ``` 142 | 143 | --- 144 | **NOTE** 145 | 146 | If you have too much time on your hands, you might try linking things like `sh`, `bash`, `ls`, or `cd` to the container wrapper script. But otherwise **don't**... because it will cause you a lot of trouble. :-) 147 | 148 | --- 149 | <- [previous](/06-runscript) - [home](https://singularity-tutorial.github.io/) - [next](/08-misc) -> -------------------------------------------------------------------------------- /08-misc/README.md: -------------------------------------------------------------------------------- 1 | <- [previous](/07-fake-installation) - [home](https://singularity-tutorial.github.io/) 2 | 3 | --- 4 | # Miscellaneous Topics and FAQs 5 | 6 | ## X11 and OpenGL 7 | 8 | You can use Singularity containers to display graphics through common protocols. To do this, you need to install the proper graphics stack within the Singularity container. For instance if you want to display X11 graphics you must install `xorg` within your container. In an Ubuntu container the command would look like this. 9 | 10 | ``` 11 | $ apt-get install xorg 12 | ``` 13 | 14 | ## GPU computing 15 | 16 | In Singularity v2.3+ the experimental `--nv` option will look for NVIDIA libraries on the host system and automatically bind mount them to the container so that GPUs work seamlessly. 17 | 18 | ## Using the network on the host system 19 | 20 | Network ports on the host system are accessible from within the container and work seamlessly. For example, you could install ipython within a container, start a jupyter notebook instance, and then connect to that instance using a browser running outside of the container on the host system or from another host. 21 | 22 | ## A note on SUID programs and daemons 23 | 24 | Some programs need root privileges to run. These often include services or daemons that start via the `init.d` or `system.d` systems and run in the background. For instance, `sshd` the ssh daemon that listens on port 22 and allows another user to connect to your computer requires root privileges. You will not be able to run it in a container unless you start the container as root. 25 | 26 | Other programs may set the SUID bit or capabilities to run as root or with elevated privileges without your knowledge. For instance, the well-known `ping` program actually runs with elevated privileges (and needs to since it sets up a raw network socket). This program will not run in a container unless you are root in the container. 27 | 28 | 29 | ## Long-running Instances 30 | 31 | Up to now all of our examples have run Singularity containers in the foreground. But what if you want to run a service like a web server or a database in a Singularity container in the background? 32 | 33 | ### lolcow (useless) example 34 | In Singularity v2.4+, you can use the [`instance` command group](http://singularity.lbl.gov/docs-instances) to start and control container instances that run in the background. To demonstrate, let's start an instance of our `lolcow.simg` container running in the background. 35 | 36 | ``` 37 | $ singularity instance.start lolcow.simg cow1 38 | ``` 39 | 40 | We can use the `instance.list` command to show the instances that are currently running. 41 | 42 | ``` 43 | $ singularity instance.list 44 | DAEMON NAME PID CONTAINER IMAGE 45 | cow1 10794 /home/dave/lolcow.simg 46 | ``` 47 | 48 | We can connect to running instances using the `instance://` URI like so: 49 | 50 | ``` 51 | $ singularity shell instance://cow1 52 | Singularity: Invoking an interactive shell within container... 53 | 54 | Singularity lolcow.simg:~> ps -ef 55 | UID PID PPID C STIME TTY TIME CMD 56 | dave 1 0 0 19:05 ? 00:00:00 singularity-instance: dave [cow1] 57 | dave 3 0 0 19:06 pts/0 00:00:00 /bin/bash --norc 58 | dave 4 3 0 19:06 pts/0 00:00:00 ps -ef 59 | 60 | Singularity lolcow.simg:~> exit 61 | ``` 62 | 63 | Note that we've entered a new PID namespace, so that the `singularity-instance` process has PID number 1. 64 | 65 | You can start multiple instances running in the background, as long as you give them unique names. 66 | 67 | ``` 68 | $ singularity instance.start lolcow.simg cow2 69 | 70 | $ singularity instance.start lolcow.simg cow3 71 | 72 | $ singularity instance.list 73 | DAEMON NAME PID CONTAINER IMAGE 74 | cow1 10794 /home/dave/lolcow.simg 75 | cow2 10855 /home/dave/lolcow.simg 76 | cow3 10885 /home/dave/lolcow.simg 77 | ``` 78 | 79 | You can stop individual instances using their unique names or stop all instances with the `--all` option. 80 | 81 | ``` 82 | $ singularity instance.stop cow1 83 | Stopping cow1 instance of /home/dave/lolcow.simg (PID=10794) 84 | 85 | $ singularity instance.stop --all 86 | Stopping cow2 instance of /home/dave/lolcow.simg (PID=10855) 87 | Stopping cow3 instance of /home/dave/lolcow.simg (PID=10885) 88 | ``` 89 | 90 | ### nginx (useful) example 91 | 92 | These examples are not very useful because `lolcow.simg` doesn't run any services. Let's extend the example to something useful by running a local nginx web server in the background. This command will download the official nginx image from Docker Hub and start it in a background instance called "web". (The commands need to be executed as root so that nginx can run with the privileges it needs.) 93 | 94 | ``` 95 | $ sudo singularity instance.start docker://nginx web 96 | Docker image path: index.docker.io/library/nginx:latest 97 | Cache folder set to /root/.singularity/docker 98 | [3/3] |===================================| 100.0% 99 | Creating container runtime... 100 | 101 | $ sudo singularity instance.list 102 | DAEMON NAME PID CONTAINER IMAGE 103 | web 15379 /tmp/.singularity-runtime.MBzI4Hus/nginx 104 | ``` 105 | 106 | Now to start nginx running in the instance called web. 107 | 108 | ``` 109 | $ sudo singularity exec instance://web nginx 110 | ``` 111 | 112 | Now we have an nginx web server running on our localhost. We can verify that it is running with `curl`. 113 | 114 | ``` 115 | $ curl localhost 116 | 127.0.0.1 - - [02/Nov/2017:19:20:39 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.52.1" "-" 117 | 118 | 119 | 120 | Welcome to nginx! 121 | 128 | 129 | 130 |

Welcome to nginx!

131 |

If you see this page, the nginx web server is successfully installed and 132 | working. Further configuration is required.

133 | 134 |

For online documentation and support please refer to 135 | nginx.org.
136 | Commercial support is available at 137 | nginx.com.

138 | 139 |

Thank you for using nginx.

140 | 141 | 142 | ``` 143 | 144 | When finished, don't forget to stop all running instances like so: 145 | 146 | ``` 147 | $ sudo singularity instance.stop --all 148 | ``` 149 | 150 | --- 151 | <- [previous](/07-fake-installation) - [home](https://singularity-tutorial.github.io/) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [home](https://singularity-tutorial.github.io/) - [next](/00-introduction) -> 2 | 3 | --- 4 | # Creating and running software containers with Singularity 5 | 6 | ### How to use [Singularity](https://sylabs.io/guides/latest/user-guide/)! 7 | 8 | 9 | This is an introductory workshop on Singularity. It was originally taught by [David Godlove](https://github.com/GodloveD) at the [NIH HPC](https://hpc.nih.gov/), but the content has since been adapted to a general audience. For more information about the topics covered here, see the following: 10 | 11 | - [Singularity Home](https://sylabs.io/singularity/) 12 | - [Singularity on GitHub](https://github.com/singularityware/singularity) 13 | - [Singularity on Google Groups](https://groups.google.com/a/lbl.gov/forum/#!forum/singularity) 14 | - [Singularity at the NIH HPC](https://hpc.nih.gov/apps/singularity.html) 15 | - [Docker documentation](https://docs.docker.com/) 16 | - [Singularity Container Services](https://cloud.sylabs.io/home) 17 | - [Docker Hub](https://hub.docker.com/) 18 | 19 | ## Table of Contents 20 | 21 | - [Introduction](/00-introduction) 22 | - [Installing Singularity](/01-installation) 23 | - [Downloading and Interacting with Containers](/02-basic-usage) 24 | - [Building a Basic Container](/03-building) 25 | - [The Singularity Ecosystem](/04-the-ecosystem) 26 | - [Accessing Host Files with Bind Mounts](/05-bind-mounts) 27 | - [The Runscript: Making Containerized Apps Behave Like Normal Apps](/06-runscript) 28 | - [Faking a Native Installation within a Singularity Container](/07-fake-installation) 29 | - [Miscellaneous Topics and FAQs](/08-misc) 30 | 31 | --- 32 | [home](https://singularity-tutorial.github.io/) - [next](/00-introduction) -> -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman --------------------------------------------------------------------------------