3.5 Docker recipes
The Docker recipe contains a set of instructions and commands that will be used to create/build a Docker image.
3.5.1 Writing recipes and building images
All commands should be saved in a text file, named by default Dockerfile.
Instruction | What it does |
---|---|
FROM | Sets the base image. |
RUN | Commands to run. |
MAINTAINER | Docker image maintainer. |
WORKDIR | Sets the working directory for the container’s building instructions. |
SHELL | Changes the default shell. |
ADD / COPY | Copy files from source to destination. |
ARG | Sets variables that can be used as the image is built. |
ENV | Sets environment variables. Persists when container is run. |
ENTRYPOINT | Helps configure the container as an executable. |
CMD | Can provide a default executable or arguments to the ENTRYPOINT executable. |
VOLUME | Creates a mount point for an external volume. |
EXPOSE | Exposes network ports on the container. |
3.5.1.1 Basic instructions
Each row in the recipe corresponds to a layer in the final image.
FROM: parent image. Typically, an operating system. This is the base layer.
FROM ubuntu:18.04
RUN: the command to execute inside the image filesystem.
Think about it this way: every RUN line is essentially what you would run to install programs on a freshly installed Ubuntu OS.
RUN apt install wget
This is a basic recipe that takes the ubuntu:18.04 image as a base layer, updates and upgrades Linux packages, and installs wget
:
FROM ubuntu:18.04
RUN apt update && apt -y upgrade
RUN apt install -y wget
HANDS-ON
Explore this Dockerfile:
- What is the base layer?
- What is being installed in the image? How?
Answer
Base layer: biocontainers/biocontainers:v1.0.0_cv4
Tool blast
is installed using conda.
3.5.1.2 Building images from recipes
docker build
will create/build a Docker image from a Docker recipe.
Save the following commands:
FROM ubuntu:18.04
RUN apt update && apt -y upgrade
RUN apt install -y wget
in a file named Dockerfile
docker build
implicitely looks for a file named Dockerfile in the current directory:
Same as:
Syntax: --file
/ -f
. stands for the context (in this case, current directory) of the build process. This makes sense if copying files from filesystem, for instance.
IMPORTANT: Avoid contexts (directories) over-populated with files (even if not actually used in the recipe).
You can define a specific name for the image during the build process.
Syntax: -t
imagename:tag. If not defined :tag
default is latest.
The last line of installation should be Successfully built …: then you are good to go.
Check with docker images
that you see the newly built image in the list…
Then let’s check the ID of the image and run it!
3.5.1.3 More instructions
MAINTAINER
Who is maintaining the container?
MAINTAINER Toni Hermoso Pulido <toni.hermoso@crg.eu>
WORKDIR: all subsequent actions will be executed in that working directory
WORKDIR ~
SHELL: allows the default shell used for the shell form of commands to be overridden.
Use bash
as the default shell:
SHELL ["/bin/bash", "-c"]
ADD, COPY: add files to the image filesystem
Difference between ADD and COPY explained here and here
COPY: lets you copy a local file or directory from your host (the machine from which you are building the image)
ADD: same, but ADD works also for URLs, and for .tar archives that will be automatically extracted upon being copied.
# COPY source destination
COPY ~/.bashrc .
ENV, ARG: run and build environment variables
Difference between ARG and ENV explained here.
- ARG values: available only while the image is built.
- ENV values: available for the future running containers.
You can use ARG, for example, to specify the version of the base layer you want to use:
- With a default value
ARG UbuntuVersion=18.04
FROM ubuntu:${UbuntuVersion}
- Without a default value (i.e. the user is expected to provide it upon building)
ARG UbuntuVersion
FROM ubuntu:${UbuntuVersion}
Provide a value for UbuntuVersion as you build the image with --build-arg
:
docker build --build-arg UbuntuVersion=20.04 .
You can also use ARG to build a specific software version in the image. Let’s try it!
HANDS-ON
Modify the following recipe so you can decide in the command line which version of Python to install.
The default version should be 2.7. Pass the argument to install the version 3.8.
Build and run first with the default version, and then with the version 3.8.
FROM ubuntu:18.04
RUN apt update && apt upgrade -y
RUN apt install -y python2.7
Answer
Recipe is saved in Dockerfile_ARG
FROM ubuntu:18.04
# Argument PyVersion with default value 2.7
ARG PyVersion=2.7
RUN apt update && apt upgrade -y
RUN apt install -y python${PyVersion}
Build the image to get the default version of Python:
Build the image to install the version 3.8 of Python instead (via the --build-arg
option):
Run the image and check if Python of the correct version is installed:
CMD, ENTRYPOINT: command to execute when generated container starts
The ENTRYPOINT specifies a command that will always be executed when the container starts.
The CMD specifies arguments that will be fed to the ENTRYPOINT.
In the example below, when the container is run without an argument, it will execute echo "hello world"
(default).
If it is run with the argument nice it will execute echo "nice"
. The argument given in CMD will be overridden.
FROM ubuntu:18.04
ENTRYPOINT ["/bin/echo"]
CMD ["hello world"]
Save the above recipe in Dockerfile_hello.
Build:
Run without an argument:
Now run with an argument:
Here is a more complex recipe (save it in a text file named Dockerfile_ubuntu):
FROM ubuntu:18.04
MAINTAINER Toni Hermoso Pulido <toni.hermoso@crg.eu>
SHELL ["/bin/bash", "-c"]
WORKDIR ~
RUN apt-get update && apt-get -y upgrade
RUN apt-get install -y wget
ENTRYPOINT ["/usr/bin/wget"]
CMD ["https://cdn.wp.nginx.com/wp-content/uploads/2016/07/docker-swarm-hero2.png"]
Build the image:
Try to run it without and with an argument:
3.5.2 docker tag
Use docker tag
to tag a local image that has, for example, ID “f9f41698e2f8” in the “ubuntu_wget” image name repository with version/tag “1.0”:
If the version/tag is not specified, the default is latest.
3.5.3 Build cache
Every line in a Dockerfile is an image/layer by itself (you can see all images with docker images --all
).
Let’s modify the last line in the previous image recipe (Dockerfile_ubuntu) (let’s change the image URL) and rebuild it (even with a different name/tag):
FROM ubuntu:18.04
MAINTAINER Toni Hermoso Pulido <toni.hermoso@crg.eu>
WORKDIR ~
RUN apt-get update && apt-get -y upgrade
RUN apt-get install -y wget
ENTRYPOINT ["/usr/bin/wget"]
CMD ["https://cdn-images-1.medium.com/max/1600/1*_NQN6_YnxS29m8vFzWYlEg.png"]
It will start from the last line, and will not re-run commands before the modified line as they were already successfully built.
It is very convenient for testing and trying new steps, but it may lead to errors when versions are updated (either FROM image or included packages).
It is therefore recommended to start from scratch with --no-cache
option, when building the image.