Build from a Dockerfile¶
After going through the build process interactively, we can translate our build steps into a Dockerfile using the directives described below.
Note
Prerequisites: You should have access to a terminal with Docker installed You should also have a copy of pi.py
The FROM Instruction¶
We can use the FROM instruction to start our new image from a known base image. This should be the first line of our Dockerfile. In our hypothetical scenario, we said our development platform / lab computer is a Linux workstation with Ubuntu 18.04. We know our code works on that workstation, so that is how we will containerize it. We will start our image from an official Ubuntu 18.04 image:
FROM ubuntu:18.04
Base images typically take the form os:version
. Avoid using the ‘latest
’
version; it is hard to track where it came from and the identity of ‘latest
’
can change.
Tip
Browse Docker Hub to discover other potentially useful base images. Keep an eye out for the ‘Official Image’ badge.
The RUN Instruction¶
We can install updates, install new software, or download code to our image by
running commands with the RUN instruction. In our case, our only dependency was
Python3. So, we will use a RUN instruction and the Ubuntu package manager
(apt
) to install it. Keep in mind that the the docker build
process cannot
handle interactive prompts, so we use the -y
flag with apt
. We also need
to be sure to update our apt packages. A typical RUN instruction for an Ubuntu base
image may look like:
RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get install -y python3
Each RUN instruction creates an intermediate image (called a ‘layer’). Too many layers makes the Docker image less performant, and makes building less efficient. We can minimize the number of layers by combining the RUN instructions:
RUN apt-get update && apt-get upgrade -y && apt-get install -y python3
A similar RUN instruction for a RedHat / CentOS base image may look like:
RUN yum update -y && yum install -y python3
The COPY Instruction¶
There are a couple different ways to get your source code inside the image. One
way is to use a RUN instruction with wget
to pull your code from the web. When
you are developing, however, it is usually more practical to copy code in from
the Docker build context using the COPY instruction. For example, we can add our
pi.py
python script to a root-level /code
directory with the following
instruction:
COPY pi.py /code/pi.py
And, don’t forget to perform one more RUN instruction to make the script executable:
RUN chmod +rx /code/pi.py
The ENV Instruction¶
Another useful instruction is the ENV instruction. This allows the image
developer to set environment variables inside the container runtime. In our
interactive build, we added the /code
folder to the PATH
. We can do this
with an ENV instruction as follows:
ENV PATH "/code:$PATH"
Putting It All Together¶
The contents of the final Dockerfile should look like:
1 2 3 4 5 6 7 8 9 | FROM ubuntu:18.04 RUN apt-get update && apt-get upgrade -y && apt-get install -y python3 COPY pi.py /code/pi.py RUN chmod +rx /code/pi.py ENV PATH "/code:$PATH" |
Build the Image¶
Once the Dockerfile is written and we are satisfied that we have minimized the number of layers, the next step is to build an image. Building a Docker image generally takes the form:
$ docker build -t username/code:version .
The -t
flag is used to name or ‘tag’ the image with a descriptive name and
version. Optionally, you can preface the tag with your Docker Hub username or
other namespace where you have permissions to publish.
Adding that namespace allows you to push your image to a public container
registry and share it with others. The trailing dot ‘.
’ in the line above simply
indicates the location of the Dockerfile (a single ‘.
’ means ‘the current
directory’).
To build our image, use:
$ docker build -t username/pi-estimator:0.1 .
Note
Don’t forget to replace ‘username’ with your Docker Hub username.
Find the Image¶
Use docker images
to ensure you see a copy of your image has been built. You can
also use docker inspect
to find out more information about the image.
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
username/pi-estimator 0.1 482bd4f0bc9b 14 minutes ago 200MB
ubuntu 18.04 72300a873c2c 11 days ago 64.2MB
$ docker inspect username/pi-estimator:0.1
If you need to rename your image, you can either re-tag it with docker tag
, or
you can remove it with docker rmi
and build it again. Issue each of the
commands on an empty command line to find out usage information.
Test the Image¶
We can test a newly-built image two ways: interactively and non-interactively.
In interactive testing, we will use docker run
to start a shell inside the
image, just like we did when we were building it interactively. The difference
this time is that we are NOT mounting the code inside with the -v
flag,
because the code is already in the container:
$ docker run --rm -it username/pi-estimator:0.1 /bin/bash
...
root@e01e374d7749:/# ls /code
pi.py
root@e01e374d7749:/# pi.py 1000000
Final pi estimate from 1000000 attempts = 3.137868
Here is an explanation of the options:
docker run # run a container
--rm # remove the container when we exit
-it # interactively attach terminal to inside of container
username/... # image and tag on local machine
/bin/bash # shell to start inside container
Next, exit the container and test the code non-interactively. Notice we are calling
the container again with docker run
, but instead of specifying an interactive
(-it
) run, we just issue the command as we want to call it (’pi.py 1000000
’)
on the command line:
$ docker run --rm username/pi-estimator:0.1 pi.py 1000000
Final pi estimate from 1000000 attempts = 3.141208
If there are no errors, the container is built and ready to share!
Hands On Exercise¶
Use docker inspect
to look at the metadata for your pi-estimator
image. Is
the /code
folder in the $PATH
? Determine the contents of $PATH
inside the
container to confirm.