Pull a public image such as ubuntu or centos using the docker pull command. If a tag is not specified, docker will default to "latest".
$ docker pull ubuntu:14.04
Now run the image using the docker run command. Use the "-it" option to get an interactive terminal during the run.
$ docker run -it ubuntu:14.04
$ whoami
$ lsb_release -a
Using standard linux commands, modify the image.
$ docker run -it ubuntu:14.04
root@949eb1a6a099:/# (echo '#!/bin/bash'|echo "echo 'Hello World'") > /bin/hello
$ chmod 755 /bin/hello
# Test it
$ hello
# Exit
$ exit
Now find the container and commit the changes to a new image called hello.
docker ps -a|head -2
# Grab the Container ID
docker commit <ID> hello
Now try running the new image with your changes.
docker run -it hello
hello
While manually modifying and committing changes is one way to build images, using a Dockerfile provides a way to build images so that others can understand how the image was constructed and make modifications.
Before starting, create an empty working directory and change into it. This is important because Docker will make a temporary copy of the contents of the working directory including subdirectories.
mkdir ecp_docker
cd ecp_docker
A Dockerfile has many options. We will focus on a few basic ones (FROM, MAINTAINER, ADD, and RUN)
Create a simple shell script called script in your local directory using your favorite editor.
cat > hello << EOF
#!/bin/bash
echo "Shane says Hello World!"
EOF
Now create a file called Dockerfile in the same directory with contents similar to this. Use your own name and e-mail for the maintainer.
FROM ubuntu:14.04
MAINTAINER Shane Canon <[email protected]>
ADD ./hello /bin/hello
RUN chmod a+rx /bin/hello
Now build the image using the docker build command. Be sure to use the -t
option to tag it. Tell the Dockerfile to build using the current directory by specifying .
. Alternatively you could place the Dockerfile and script in an alternate location and specify that directory in the docker build command.
docker build -t hello:1.0 .
Try running the image.
docker run -it hello:1.0
hello
Docker provides a public hub that can be use to store and share images. Before pushing an image, you will need to create an account at Dockerhub. Go to https://cloud.docker.com/ to create the account. Once the account is created, push your test image using the docker push command. In this example, we will assume the username is patsmith.
docker tag hello:1.0 patsmith/hello:1.0
docker push patsmith/hello:1.0
The first push make take some time depending on your network connection and the size of the image.
Now that you've practiced loading a simple script, try creating an image that can run this short MPI hello word code:
// Hello World MPI app
#include <mpi.h>
#include <stdio.h>
int main(int argc, char** argv) {
int size, rank;
char buffer[1024];
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
gethostname(buffer, 1024);
printf("hello from %d of %d on %s\n", rank, size, buffer);
MPI_Barrier(MPI_COMM_WORLD);
MPI_Finalize();
return 0;
}
Hints:
- You can start with the image "nersc/ubuntu-mpi:14.04". It already has MPI installed.
- You compile with "mpicc helloworld.c -o /app/hello"
Expand to see the answer
Dockerfile:
# MPI Dockerfile
FROM nersc/ubuntu-mpi:14.04
ADD helloworld.c /app/
RUN cd /app && mpicc helloworld.c -o /app/hello
docker build -t mydockerid/hellompi:latest .
docker push mydockerid/hellompi:latest
Log into the image and run the app:
docker run -it mydockerid/hellompi:latest
root@982d980864e5:/# mpirun -n 10 /app/hello hello from 3 of 10 on 982d980864e5
hello from 4 of 10 on 982d980864e5
hello from 7 of 10 on 982d980864e5
hello from 9 of 10 on 982d980864e5
hello from 2 of 10 on 982d980864e5
hello from 5 of 10 on 982d980864e5
hello from 8 of 10 on 982d980864e5
hello from 0 of 10 on 982d980864e5
hello from 6 of 10 on 982d980864e5
hello from 1 of 10 on 982d980864e5
Unlike Docker Singularity does not provide an integrated virtual machine for Mac and Windows platforms. We will use what was learned above to build and run Singularity inside of a Docker container. If you are running on a Linux system this is still a good exercise, although you may install and run Singularity directly if desired.
To create a Docker image containing Singularity we'll use the following recipe
- Create a new folder on your host system to store your
Dockerfile
- Use
ubuntu:17.10
as the base image - Install system packages required to build and run Singularity
apt-get -y update && apt-get -y install vim sudo wget git autoconf libtool build-essential python squashfs-tools
- Follow the installation instructions for Singularity/2.4.2
- Each instance of
RUN
is executed in/
, make sure you're in the right directory sudo
is not needed as you are running the build asroot
- Each instance of
- For demonstration purposes we'll setup a user named
foo
and tell docker to run under this userRUN useradd -ms /bin/bash foo && usermod -aG sudo foo && passwd -d foo
USER foo
WORKDIR /home/foo
- Build the Docker container and name it
singularity:2.4.2
Expand to see solution Dockerfile
FROM ubuntu:17.10
RUN apt-get -y update && \
apt-get -y install vim sudo git wget autoconf libtool build-essential python squashfs-tools
RUN VERSION=2.4.2 && \
wget https://github.com/singularityware/singularity/releases/download/$VERSION/singularity-$VERSION.tar.gz && \
tar xvf singularity-$VERSION.tar.gz && \
cd singularity-$VERSION && \
./configure --prefix=/usr/local && \
make && \
make install
RUN useradd -ms /bin/bash foo && \
usermod -aG sudo foo && \
passwd -d foo
USER foo
WORKDIR /home/foo
Now lets see how Singularity behaves at runtime. To do so we'll enter an interactive shell within our Docker container.
To run nested containers we will need to add --privileged
.
$ docker run -it --privileged singularity:2.4.2
First lets verify that singularity is installed correctly
foo@<id>:~$ singularity --version
2.4.2-dist
Now we should be the user foo
and in /home/foo
, as specified in the Dockerfile. For the rest of the tutorial we should
forget that this we're in a Docker container and consider it our host system.
foo@<id>:~$ whoami
foo
foo@<id>:~$ pwd
/home/foo
Lets create a file, bar
, in foo
's home directory
foo@<id>:~$ touch bar
foo@<id>:~$ ls
bar
Singularity can easily run Docker images. To get an interactive shell in ubuntu:17.10 from Docker Hub we can use the following
foo@<id>:~$ singularity shell docker://ubuntu:17.10
Now that we're in a Singularity container lets see how it differs from Docker
Singularity ubuntu:17.10:~> whoami
foo
Singularity ubuntu:17.10:~> pwd
/home/foo
Singularity ubuntu:17.10:~> ls
bar
When running a Singularity container your user inside of the container is the same as your user on the host. You also have access to the same user directories inside of the container that your user has outside of the container. This is is in contrast to the Docker default where you run as root with no host directories mounted.
Type exit
to return to our "host".
Let's use a Singularity definition file to run the MPI sample. The recipe, which we'll create in a file called mpi.def
will be:
- The first line should define the bootstrap method, we'll use docker
BootStrap: docker
- When using docker bootstrap we need to specify the Docker Hub image
From: ubuntu:17.10
- The
%post
section, which is ansh
script that runs after the bootstrap process, will install mpichapt-get -y update
apt-get -y install mpich
Now to build our image, mpi.img
foo@<id>:~$ sudo singularity build mpi.img mpi.def
If the image built successfully we should be able to see it, mpi.img
, the current directory
foo@<id>:~$ ls
mpi.def mpi.img
On the host lets create a copy of the sample MPI application helloworld.c
.
Once the source is created compile and run the MPI sample, as you did with the Docker example. To execute a command in the container you will use the following
singularity exec <container> <command>
Expand to see solution
We begin by compiling
foo@<id>:~$ singularity exec mpi.img mpicc helloworld.c -o hello
We can check that this created the executable hello
in the currently directory, which exists outside of our Singularity container
foo@<id>:~$ ls
mpi.def mpi.img hello
Now to run inside of our container
foo@<id>:~$ singularity exec mpi.img mpirun -n 10 ./hello
hello from 2 of 10 on <id>
hello from 4 of 10 on <id>
hello from 5 of 10 on <id>
hello from 0 of 10 on <id>
hello from 7 of 10 on <id>
hello from 3 of 10 on <id>
hello from 1 of 10 on <id>
hello from 8 of 10 on <id>
hello from 9 of 10 on <id>
hello from 6 of 10 on <id>