Docker pt 1: Create a Docker Image (DevOps the Hard Way series)
So far in the DevOps the Hard Way series, the focus has been on building the cloud infrastructure that is required to run and store containerized software. On the other hand, this next pair of posts will focus on preparing the software that will be run on said infrastructure.
While containerized software can strongly benefit both software developers and system operators, this section is about implementing containerization rather than the reasoning for doing so.
Since it has been decided in this scenario that the Uber API software should be containerized, the Docker platform is introduced into the workflow. Per the Docker website:
Docker is an open platform for developing, shipping, and running applications.
The Dockerfile
While building a containerized image for the Uber API, we start with a Dockerfile, which is a text file containing a list of individual docker commands. Each command in the file builds on the previous one, in order to define a software container image. The author of the Devops the Hard Way repository provides the following Dockerfile.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FROM python:latest
RUN mkdir /build
WORKDIR /build
COPY app /build
COPY app/requirements.txt /build
RUN pip install -r requirements.txt
EXPOSE 5000
CMD [ "python", "app.py" ]
Understanding the Dockerfile
Below is a line-by-line breakdown of the file, including command descriptions from the Dockerfile reference page.
FROM
1
FROM python:latest
The FROM instruction initializes a new build stage and sets the base image for subsequent instructions.
In this first line, the base image is set to the latest version of the official python Docker image.
That image provides a standardized, portable, self-contained Python runtime environment. Since the Uber API to be containerized is coded in Python, the official Python Docker image is an intuitive choice of base image to start the build stage FROM.
The rest of the commands in the Dockerfile “build up” a customized container image step-by-step, adding more to the container image with each command.
RUN
1
RUN mkdir /build
The RUN instruction will execute any commands to create a new layer on top of the current image. The added layer is used in the next step in the Dockerfile.
The RUN command in this Dockerfile adds a /build directory to the base python container image.
WORKDIR
1
WORKDIR /build
The WORKDIR instruction sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it in the Dockerfile.
COPY
1
2
3
COPY app /build
COPY app/requirements.txt /build
The COPY instruction copies new files or directories from
and adds them to the filesystem of the container at the path .
These two lines tell Docker to copy the specified local files into the /build directory inside the new container image. When working with this Dockerfile, it is assumed by its author that the app directory from this GitHub link (which contains the standard, non-containerized Uber API) is saved locally in the same directory as Dockerfile.
At this point, the built-up image is:
- the official Python image, with an added
/builddirectory, containing:- an
appsubdirectory with the Uber API Python code - the Uber API’s
requirements.txtfile
- an
RUN
1
RUN pip install -r requirements.txt
The RUN instruction will execute any commands to create a new layer on top of the current image. The added layer is used in the next step in the Dockerfile.
Next, the pip command is run to install all of the python packages required by the Uber API. That way, when containers are eventually created from this image, they will already have both the Uber API and its dependencies pre-installed and ready to run.
EXPOSE
1
EXPOSE 5000
The EXPOSE instruction informs Docker that the container listens on the specified network ports at runtime.
This line specifies that the container is exposing TCP port 5000 to the container runtime. As a result, the containerized software is network-accessible from outside the container via that port, e.g. from a client computer’s web browser via http://hostname:5000.
CMD
1
CMD [ "python", "app.py" ]
The CMD instruction sets the command to be executed when running a container from an image.
This last line of Dockerfile ensures that the containerized API is automatically started up as soon as a container is run from this image.
Creating the Docker image
With the Dockerfile and a copy of the Uber API saved locally, we are ready to build a container image for the Uber API.
1
2
$ ls
app Dockerfile
docker build
We build the image using the Docker cli:
1
$ docker build -t uberapp .
- the
-toption is for “tag,” used to name the image/repository for later reference - uberapp is the value for the above option
.specifies that the Dockerfile to reference during build is in the current directory
This can take some time as it involves some downloading and automated installation. Once the command completes, we can also verify the image using the Docker cli.
1
2
3
4
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
uberapp latest 113aa98018a5 About a minute ago 1.05GB
As we can see, a new container image with a tag of uberapp was successfully created and is about 1GB in size.
In the next post, we will start working with our new container image, using it to start up a container, and saving it to the Amazon Elastic Container Registry created earlier in this series.