Import a Docker image into a Singularity Image
The core of a Docker image is basically a compressed set of files, a set of .tar.gz
that (if you look in your Docker image folder on your host machine, you will see. We are going to use this local repository for this first set of methods.
Quick Start: Use the Docker Remote API
Import Docker to Singularity
The Docker engine communicates with the Docker Hub via the Docker Remote API, and guess what, we can too! The easiest thing to do is create an image, and then pipe a Docker image directly into it from the Docker Registry. This first method does not require having Docker installed on your machine. Let’s say that I want to bootstrap tensorflow from Docker. First I should create the tensorflow image:
sudo singularity create --size 4000 tensorflow.img
sudo singularity import tensorflow.img docker://tensorflow/tensorflow:latest
Cache folder set to /home/vanessa/.singularity/docker
Extracting /home/vanessa/.singularity/docker/sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4.tar.gz
Extracting /home/vanessa/.singularity/docker/sha256:182b64c1f020de1cb4b2783b3a13fbeb07ec4087bc911352d0f5ef40c8eec8cf.tar.gz
Extracting /home/vanessa/.singularity/docker/sha256:bfc1d5e3de1cf70353afb2b81fbbeab16bad961352b86f60901bc1da1396f2b4.tar.gz
Extracting /home/vanessa/.singularity/docker/sha256:d819f1ec59a06c001b37e66dd1639c591e606029ea7584fac704ff741cda249b.tar.gz
Extracting /home/vanessa/.singularity/docker/sha256:f5d83de9c6786bff4160679ed4bde332970367225ede609944bbe686edb1c25b.tar.gz
Extracting /home/vanessa/.singularity/docker/sha256:c1f8f4c880d49d70a8280860e3bc5ee559a95d4e1dc44f9128b638eb2240324c.tar.gz
Extracting /home/vanessa/.singularity/docker/sha256:9528c5352798ec3a134be13b66bc4dc71e7cdd029e268ae3cdfeb0719a4c8b8b.tar.gz
Extracting /home/vanessa/.singularity/docker/sha256:c145c1f339f57690f80bd64e86caa3b00e0635a6a383bc8be7726a3baf22a0d2.tar.gz
Extracting /home/vanessa/.singularity/docker/sha256:ebb77ce6e1c6769c1849194c1319dc6978e19575c76fd1fa942a623b6f2996a4.tar.gz
Extracting /home/vanessa/.singularity/docker/sha256:51900bc9e720db035e12f6c425dd9c06928a9d1eb565c86572b3aab93d24cfca.tar.gz
Extracting /home/vanessa/.singularity/docker/sha256:f8419ea7c1b5d667cf26c2c5ec0bfb3502872e5afc6aa85caf2b8c7650bdc8d9.tar.gz
Extracting /home/vanessa/.singularity/docker/sha256:3eed5ff20a90a40b0cb7909e79128740f1320d29bec2ae9e025a1d375555db15.tar.gz
Extracting /home/vanessa/.singularity/docker/sha256:6c953ac5d795ea26fd59dc5bdf4d335625c69f8bcfbdd8307d6009c2e61779c9.tar.gz
Adding Docker CMD as Singularity runscript...
/run_jupyter.sh
Bootstrap initialization
No bootstrap definition passed, updating container
Executing Prebootstrap module
Executing Postbootstrap module
Done.
Note that if you want (much) more detailed output for debugging to the console, you need to enable --verbose
mode:
sudo singularity --verbose import tensorflow.img docker://tensorflow/tensorflow:latest
Now I can shell into it, and import tensorflow:
$ singularity shell tensorflow.img
Singularity: Invoking an interactive shell within container...
Singularity.tensorflow.img> ls
Singularity.tensorflow.img> python
Python 2.7.6 (default, Jun 22 2015, 17:58:13)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import tensorflow
>>>
How does the runscript work?
Docker has two commands in the DOCKERFILE
that have something to do with execution, CMD
and ENTRYPOINT
. The differences are subtle, but the best description I’ve found is the following:
A
CMD
is to provide defaults for an executing container.
and
An
ENTRYPOINT
helps you to configure a container that you can run as an executable.
Given the definition, the ENTRYPOINT
is most appropriate for the Singularity %runscript
, and so using the default bootstrap (whether from a docker://
endpoint or a Singularity
spec file) will set the ENTRYPOINT
variable as the runscript. You can change this behavior by specifying IncludeCmd: yes
in the Spec file (see below). If you provide any sort of %runscript
in your Spec file, this overrides anything provided in Docker. In summary, the order of operations is as follows:
- If a
%runscript
is specified in theSingularity
spec file, this takes prevalence over all - If no
%runscript
is specified, or if theimport
command is used as in the example above, theENTRYPOINT
is used as runscript. - If no
%runscript
is specified, but the user has aSingularity
spec withIncludeCmd
, then the DockerCMD
is used.
Use a Spec File
Do a barrel role! Use a spec file! Many times, you want to bootstrap an image, and then either change the %runscript
or add additional software or commands in the %post
section. To achieve this, you can create a specification file. Currently, these are distributed with the naming format [myfile].def
, however (soon) we will use a standard name, Singularity
so all specification files can be automatically found. Here is what the spec file would look like for tensorflow:
Bootstrap: docker
From: tensorflow/tensorflow:latest
%runscript
exec /usr/bin/python "$@"
%post
echo "Post install stuffs!"
In the example above, I am overriding any Dockerfile ENTRYPOINT
because I have defined a %runscript
. If I want the Dockerfile ENTRYPOINT
to take preference, I would remove the %runscript
section:
Bootstrap: docker
From: tensorflow/tensorflow:latest
%post
echo "Post install stuffs!"
Note that the spec file above would be (almost) equivalent to the command:
sudo singularity import tensorflow.img docker://tensorflow/tensorflow:latest
minus the useless echo at the end. If I want the CMD
to take preference, I would add IncludeCmd
:
Bootstrap: docker
From: tensorflow/tensorflow:latest
IncludeCmd: yes
%post
echo "Post install stuffs!"
The solutions above would be ideal for saving a custom specification of an image to build at some runtime.
Custom Authentication
For both import and bootstrap using a build spec file, by default we use the Docker Registry index.docker.io
. Singularity first tries the call without a token, and then asks for one with pull permissions if the request is defined. However, it may be the case that you want to provide a custom token for a private registry. You have two options. You can either provide a Username
and Password
in the build specification file (if stored locally and there is no need to share), or (in the case of doing an import or needing to secure the credentials) you can export these variables to environmental variables. We provide instructions for each of these cases:
Authentication in the Spec File
You can simply specify your additional authentication parameters in the header with the labels Username
and Password
:
Username: vanessa
Password: [password]
Again, this can be in addition to specification of a custom registry with the Registry
parameter.
Authentication in the Environment
You can export your registry, username, and password for Singularity as follows:
export SINGULARITY_DOCKER_REGISTRY='--registry myrepo'
export SINGULARITY_DOCKER_AUTH='--username vanessa --password [password]'
Testing Authentication
If you are having trouble, you can test your token by obtaining it on the command line and putting it into an environmental variable, CREDENTIAL
:
CREDENTIAL=$(echo -n vanessa:[password] | base64)
TOKEN=$(http 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:vanessa/code-samples:pull' Authorization:"Basic $CREDENTIAL" | jq -r '.token')
This should place the token in the environmental variable TOKEN
. To test that your token is valid, you can do the following
http https://index.docker.io/v2/vanessa/code-samples/tags/list Authorization:"Bearer $TOKEN"
The above call should return the tags list as expected.
Run a Singularity Shell from a Docker image
Finally, we can achieve a “shell” experience, meaning shelling into Docker image imported into Singularity. We do this by storing the entire image in a temporary location, and then running the same function. You would do something like this:
sudo singularity shell docker://ubuntu:latest
Detailed Start: Bootstrapping a Docker image
A common use case is to want to start with a Docker image, possibly add custom commands, and have a Singularity image when you finish. You can read a bit about bootstrapping here to get a sense of adding the custom commands and software. To specify “Docker” as the build source, you simply need this header:
Bootstrap: docker
From: ubuntu:latest
IncludeCmd: yes
- Boostrap: docker specifies that you want to import from Docker. This is required for Docker bootstrapping!
- IncludeCmd: will add the Dockerfile CMD as the runscript (
/singularity
) if one is found. If you define a different one later in the spec file, your specification will overwrite this one. - From: works the same way as it does for Docker. You can specify, maximally, a library/imagename:tag. For example, all of the following are valid:
- library/ubuntu:latest
- library/ubuntu
- ubuntu:latest
- ubuntu
In the case of omitting the tag (latest) it is assumed that you want the latest image. In the case of omitting the namespace (library) it is assumed that you want the common namespace, library. If you have a reason to use the Docker Engine, we also have a method to do this. The benefit of this method would be that you could use an image built locally (in your local cache) that isn’t on Docker Hub.
Using Docker Engine
Here we will access Docker images via the Docker
command line tool, meaning using the Docker engine. As is the Docker standard, the image is first looked for in your local cache, and if not found, is pulled from Docker Hub.
docker2singularity.sh: Dockerized
We wrapped this entire process into a Docker container itself, which means that you can use a Docker container in a Docker container to export a Docker container into Singularity! Nuts. Full instructions are provided, however here is the gist:
docker run \
-v /var/run/docker.sock:/var/run/docker.sock \
-v D:\host\path\where\to\ouptut\singularity\image:/output \
--privileged -t --rm \
singularityware/docker2singularity \
ubuntu:14.04
How does docker2singularity.sh work?
How did this come to be? It so happens that Docker has an “export” command to pipe this data out, and Singularity has an “import” command to take them in. Thus, you can do a simple import of a Docker image into a Singularity command by doing:
# Here is the name of the Singularity image I will create
image=ubuntu.img
# Now I am creating it
sudo singularity create $image
# Now I am exporting a running Docker container into it via a pipe (|)
docker export $container_id | singularity import $image
Where $container_id
is the id of a running container obtained with docker ps
. However, there are subtle details like the environment and permissions that this method will miss. It’s also the case that most Docker images don’t run (and stay running) easily unless you do something like:
docker run -d $image tail -f /dev/null
Early on we created a docker2singularity.sh, a script that you can download and run as follows:
wget https://raw.githubusercontent.com/singularityware/docker2singularity/master/docker2singularity.sh
chmod u+x docker2singularity.sh
./docker2singularity.sh ubuntu:latest
To produce a Singularity image of “ubuntu:latest” in the present working directory.
Troubleshooting
Why won’t my image bootstrap work? If you can’t find an answer on this site, please ping us an issue. If you’ve found an answer and you’d like to see it on the site for others to benefit from, then post to us here.
Future
This entire process will hopefully change in two ways. First, we hope to collapse the image creation and bootstrapping, so you have the option to do them both in one full swing. Second, we hope to eventually figure out some kind of solution to import Docker containers without needing sudo.
Edit me