In this step, you will learn how to create your own Docker image designed for custom data processing.
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
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
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
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.
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.)
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:
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
Use this ID to generate a new image by running:
docker commit -m "Creating the dcmstack image" -a "<Your name>" <container ID> xnat/dcmstack_python:v1
In this example:
docker commitcreates an image from a container
-aadd metadata to the commit
<container ID>tells docker which container to use to make the new image
<user name>/<image name>:<tag>for the newly created image
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:
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.
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:
# 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.11specifies the name and tag of our base image
RUN statementexecutes 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:
xnat@xnat-31:~/pydicom$ docker build --tag xnat/dcmstack_python:v2 .
In this example:
docker buildcreates an image from Dockerfile
--tag xnat/dcmstack_python:v2tags 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.
Don't forget the trailing period at the end of the
docker build --tag xnat/dcmstack_python:v2 . command. It's easy to miss.
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!
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...