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.
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:
Base images typically take the form
os:version. Avoid using the ‘
version; it is hard to track where it came from and the identity of ‘
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
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 .
-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
To build our image, use:
$ docker build -t username/pi-estimator:0.1 .
Don’t forget to replace ‘username’ with your Docker Hub username.
Find the Image¶
docker images to ensure you see a copy of your image has been built. You can
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
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 (’
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¶
docker inspect to look at the metadata for your
pi-estimator image. Is
/code folder in the
$PATH? Determine the contents of
$PATH inside the
container to confirm.