Skip to main content
Skip table of contents

Step 4 of 4: Build Custom Docker Images

Goal

In this step, you will learn how to create your own Docker image designed for custom data processing.

Intro

The central Docker Hub registry catalogs a huge number of useful prepackaged images, from official builds (such as Busybox and Neurodebian) to user created repositories containing images such as: ImageMagick, Octave, and ITK. If you are unable to find a Docker image to do just what you want, you can build your own. New images can be built interactively or by using a build file, i.e. Dockerfile.

In this example, we will build an image containing several Python modules and script that will convert a directory of DICOM files to a directory of NIFTI files.

Build an image interactively

Load our image from an archive

First, we will build an image interactively. This means we will start with a base image, create a container from it, make changes to the container, and save that out as a new image.

First we start by loading our base image. We are using the official Python image from Docker Hub, but we have saved it as a tar file on your Vagrant VM drive so you don't need to download it. To load the image, run

BASH
xnat@xnat-31:~$ docker load -i /resources/docker/docker_python_2.7.11.tar
4dcab49015d4: Loading layer 130.9 MB/130.9 MB
5f70bf18a086: Loading layer 1.024 kB/1.024 kB
d2c5e3a8d3d3: Loading layer 45.18 MB/45.18 MB
a80b5871b282: Loading layer 126.5 MB/126.5 MB
6ec10d9b4afb: Loading layer 325.8 MB/325.8 MB
08be4b5fa934: Loading layer 1.456 MB/1.456 MB
a971611d999d: Loading layer 48.95 MB/48.95 MB
d0ffb1b09c02: Loading layer  6.45 MB/6.45 MB

Check that the image was loaded by running 

CODE
xnat@xnat-31:~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
xnat/ubuntu         latest              2fa927b5cdd3        9 days ago          122 MB
python              2.7.11              6cfb04735795        11 days ago         664.9 MB
busybox             latest              47bcc53f74dc        11 weeks ago        1.113 MB

We see python:2.7.11, which we will be using as our base image.

Set up the container

As we did previously, we will run an interactive session on the python image by opening a bash command prompt.

CODE
xnat@xnat-31:~$ docker run -it python:2.7.11 /bin/bash
root@<container id>:/#

 

From within the container, we will execute the following sequence of commands to load load various Python modules onto our container. (You will see outputs from these commands; they are omitted here so you can see all the commands together.)

BASH
root@<container id>:/# easy_install pydicom
root@<container id>:/# pip install nibabel
root@<container id>:/# wget https://github.com/moloney/dcmstack/archive/master.zip
root@<container id>:/# easy_install master.zip
root@<container id>:/# mkdir /scripts
root@<container id>:/# wget -q https://raw.githubusercontent.com/XnatWorkshop/docker-demo/master/pydicom_example/pydicom.py -O /scripts/pydicom.py
root@<container id>:/# exit

In this example: 

  • We installed python modules in the container: pydicom, nibabel, and dcmstack
  • We downloaded a python script (written for this demo) and installed it to the /scripts directory in the container

Commit an image from the container

Find the ID of the container we just configured by examining the output of docker ps -a and finding the row for the container we just ran. For example:

CODE
xnat@xnat-31:~$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
73ba978f0952        python:2.7.11       "/bin/bash"         7 minutes ago       Exited (0) 3 seconds ago                        sleepy_mclean

Here, the container's ID is 73ba978f0952.

 

Use this ID to generate a new image by running:

CODE
docker commit -m "Creating the dcmstack image" -a "<Your name>" <container ID> xnat/dcmstack_python:v1

In this example:

  • docker commit creates an image from a container
  • -m and -a add metadata to the commit
  • <container ID> tells docker which container to use to make the new image
  • xnat/dcmstack_python:v1 specifies the <user name>/<image name>:<tag> for the newly created image

 

Extra

See if you can start the xnat/dcmstack_python:v1 image and verify that everything we did to the container is there. Hint: use "docker run -it <something> /bin/bash". Once inside, look for the script we downloaded once logged into an interactive session with the Docker container.

Building from a Dockerfile

While the method of interactively building a Docker image allows you to experiment with your image until you get it right, it can be a little cumbersome for distribution and reproducibility if someone else wants to build the same thing. The recommended method for custom image generation is to use a Dockerfile. A Dockerfile is simply a list of textual instructions interpreted by the docker build command. Each line in the Dockerfile contains an instruction and a statement:

CODE
INSTRUCTION statement


Consider the custom Python image we just built. Here is how we can build the same image from a Dockerfile. First, make a new directory in which we can work.

CODE
xnat@xnat-31:~$ mkdir pydicom
xnat@xnat-31:~$ cd pydicom

Now make a file in that directory (using your favorite command-line text editor, like vi or nano or emacs). The name of the file should be Dockerfile. Enter the following into that file and save it:

Dockerfile

TEXT
# Build a dcmstack compatible Docker image with preloaded dcm->nii script.
FROM python:2.7.11
RUN easy_install pydicom
RUN pip install nibabel
RUN wget https://github.com/moloney/dcmstack/archive/master.zip
RUN easy_install master.zip
RUN mkdir /scripts
RUN wget -q https://raw.githubusercontent.com/XnatWorkshop/docker-demo/master/pydicom_example/pydicom.py -O /scripts/pydicom.py

In this example we can see some of the format used in a Dockerfile.

  • Lines starting with # are used to denote comments
  • FROM python:2.7.11 specifies the name and tag of our base image
  • RUN statement executes each statement on the base Docker image

You can see that the RUN steps in the Dockerfile are all the same commands we ran ourselves when generating the image interactively.

 

Now we can generate a new Docker image using this Dockerfile. From within the pydicom directory, run:

BASH
xnat@xnat-31:~/pydicom$ docker build --tag xnat/dcmstack_python:v2 .

In this example:

  • docker build creates an image from Dockerfile
  • --tag xnat/dcmstack_python:v2 tags the generated image
  • . tells docker to use the contents of the current directory during the build. You can execute instructions like "COPY local_file /path/on/image" to add files that you have locally into a new docker image.

 

Problems?

 Don't forget the trailing period at the end of the docker build --tag xnat/dcmstack_python:v2 . command. It's easy to miss.

 

Extra

There is a lot more you can do in a Dockerfile. You can check out the Dockerfile documentation to learn more. See if you can add an instruction to your Dockerfile that will add a label to your image.

Later, when the XNAT Container Service is released, you will be able to add special labels to your docker images that will tell XNAT how to define your Commands and Actions. Any XNAT user will be able to execute ACEs with your docker image as soon as they pull it, no configuration required!

 

Completed!

You should now have a good understanding of the basic Docker functionality. Feel free to experiment with other commands (docker help) or by building other interesting functional images.  When you're ready, you can...

Continue to Part 2: XNAT Container Service

 

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.