Preparing Dockerfiles
Outline
In this chapter you will learn:
- How to prepare a Dockerfile for an application.
- How to build an image from a Dockerfile.
- How to tag images with new labels.
Walkthrough
Task 1: Sample application overview
Start with cloning the repository with a sample Python web application based on Flask framework:
$ git clone https://github.com/bzurkowski/docker-workshops.git
If git
command is not available, install it:
$ apt install -y git-core
Application is located in sample-app
subdirectory:
$ cd ./docker-workshops/sample-app
$ ls -l
-rw-r--r-- 1 bzurkowski staff 6 May 5 12:59 requirements.txt
-rw-r--r-- 1 bzurkowski staff 189 May 5 13:00 sample_app.py
The application is very simple and consists of two files: sample_app.py
and requirements.txt
.
File sample_app.py
contains the code of the application - Flask Hello world!
example. It starts a server listening on port 5000
, and exposes a single HTTP
endpoint under /
which just prints Hello World!
:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=int("5000"), debug=True)
File requirements.txt
contains list of application dependencies. At the moment it's only the flask
library:
flask
Task 2: Preparing a Dockerfile
In order to package our sample application into a Docker image, we need to write a Dockerfile
.
Dockerfile is a text file containing all instructions that the Docker engine needs to know in order to prepare the application environment:
- a base Docker image to run from,
- copying application code,
- installing application dependencies,
- application start-up commands.
It is a convenient way to automate the image creation process. A ready Dockerfile may be passed as an argument for docker build
command, which will process the instructions and provide a ready-to-run image object.
Here is the format of a Dockerfile:
# Comment
DIRECTIVE arguments
Docker runs the directives in order. A Dockerfile must start with a FROM
directive, which specifies a base image from which a new image should be built (e.g. ubuntu
).
Here are the most common directives that can be used in a Dockerfile:
FROM <image>[:<tag>]
Sets a base Image for subsequent instructions.
Examples:
FROM ruby:2.3.1 FROM ubuntu
WORKDIR <path>
Sets the working directory for any
RUN
,CMD
,ENTRYPOINT
,COPY
andADD
instructions that follow it in the Dockerfile.Examples:
WORKDIR /my/custom/path RUN pwd
The output of the final
pwd
command in the above example would be/my/custom/path
COPY <src>... <dest>
Copies new files or directories from
<src>
and adds them to the filesystem of the image at the path<dest>
.Examples:
COPY test relativeDir/ COPY hom* /mydir/
RUN <command>
Executes any commands in a new layer on top of the current image and commits the results.
Examples:
RUN apt-get update && \ apt-get install redis && | ...
ENV <key> <value>
Sets the environment variable
<key>
to the value<value>
.Examples:
ENV REDIS_VERSION 5.0.4
EXPOSE <port> [<port>/<protocol>...]
Informs Docker that the container listens on the specified network ports at runtime. Acts as a kind of port mapping documentation.
Examples:
EXPOSE 6379
CMD ["executable","param1","param2"]
Set a default command, which will be executed when container is run without specifying a command. If container runs with a command, the default command will be ignored.
Examples:
CMD ["redis-server"]
Create a Dockerfile
file in the application directory with the following content:
FROM python:alpine3.7
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
EXPOSE 5000
CMD python ./index.py
Task 3: Building an image
In order to build an image from a Dockerfile there is docker build
command. It has the following syntax:
$ docker build [OPTIONS] PATH
PATH
constitutes a build context - Docker engine will search for a Dockerfile in the path. Besides, there is --tag
option that enables naming a built image in the name:tag
format.
Let's build an image from the Dockerfile prepared in the previous task:
$ docker build --tag sample_app:v1.0 .
Sending build context to Docker daemon 4.096kB
Step 1/6 : FROM python:alpine3.7
...
Step 2/6 : COPY . /app
...
---> b2900adc0cb4
Step 3/6 : WORKDIR /app
...
---> 26af9cda64ee
Step 4/6 : RUN pip install -r requirements.txt
...
---> f03bae55dbc0
Step 5/6 : EXPOSE 5000
...
---> 4ca17265a3da
Step 6/6 : CMD python ./sample_app.py
...
---> f4922ef0cb8e
Successfully built f4922ef0cb8e
Successfully tagged sample_app:v1.0
Analyze the command output.
Note the subsequent steps processed by the Docker engine. Each processed directive commits a new layer on top of the base image.
List images to confirm the successful image build:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sample_app v1.0 f4922ef0cb8e About an hour ago 91.8MB
Note tag v1.0
for our new image.
Task 4: Tagging an image
So far, we've built an image for sample application and tagged it with v1.0
label. We may want to mark v1.0
image as the latest
one.
In order to retag an image there is docker tag
command with the following syntax:
$ docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
Let's retag our image as latest
:
$ docker tag sample_app:v1.0 sample_app:latest
List images to check the new label:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sample_app latest f4922ef0cb8e About an hour ago 91.8MB
sample_app v1.0 f4922ef0cb8e About an hour ago 91.8MB
Now, we can see two instances of sample_app
image. One is tagged with v1.0
. Second is tagged with latest
. Even though the SIZE
column informs that each instance consumes 91.8MB
of disk space (183.6MB
in total), in fact, the two images still consume 91.8MB
, because image tag is just a pointer to the source image.
Exercises
Run a container from the built image and publish the application port. Access the application in a browser. The page should display caption
Hello World!
.Modify the sample application to print something different than
Hello World!
.Rebuild the application image and tag it with label
v1.1
.Mark version
v1.1
of the image aslatest
.Run another container from the newer version of the image and access the application in a browser.