Docker -- Part Two

Posted on Wed 02 January 2019 in linux

In the first part of the Docker series the basic concepts as well as the installation of the Docker Community Edition (CE) has been discussed.

In the next step, a basic docker image is created that should run a simple web application built on the Python micro-framework Flask.


Before diving into the creation of the Docker image, first, the minimal Flask web application will be discussed:

To resolve the dependencies of the web application, a requirements.txt file is specified that contains the Python modules that are required to run the web application. It just contains the web framework Flask itself plus the Gunicorn (Green Unicorn) a Python-based WSGI HTTP server.


Then, the web application itself ( is defined that just returns 'Hello World' when calling the corresponding root URL /:

from flask import Flask
app = Flask(__name__)

def hello():
    return "Hello World!"

Apart from this create a file with the following content is created:

from myapp import app

if __name__ == "__main__":

The can later on easily be called by the gunicorn WSGI server.

Creating a Dockerfile

Though, there are a couple of pre-defined Docker images that already include a ready-to-use Python environment, here a completely new Docker image is created from scratch that is based on Alpine Linux, a lightweight Linux distribution. This allows, on the one hand, the creation of a fully customizable Docker image and, on the other hand, the required image size can be reduced, in comparison to Debian- or Ubuntu-based Docker images.

FROM alpine:3.8



RUN apk update \
  && apk add --no-cache python3 \
  && pip3 install --no-cache-dir --upgrade pip \
  && pip3 install --no-cache-dir -r requirements.txt


CMD ["gunicorn", "-b", "-w 4", "wsgi:app"]
  • FROM -- defines the parent Docker image, the newly created image should be based on. Particularly, if multiple instances of the image should be initiated, make sure to choose a small lightweight base image.
  • MAINTAINER -- defines the maintainer of the image. This is just the contact information of the creator of the Dockerfile.
  • ENV -- defines environment variables that can be referenced in later commands in the Dockerfile. The environment variables can later on be overridden when adding the argument -e "env_var_name=another_value" to the docker command. This allows an easy adoption of the Docker image.
  • WORKDIR -- sets the working directory for all following commands, including RUN, CMD, ENTRYPOINT, COPY and ADD. The working directory will automatically be created, if it is not existing yet.
  • COPY -- copies the current directory content to the application path.
  • RUN -- specifies which commands should be executed in the image. Each RUN call will create a new layer in the image so that it is recommended to combine the commands in a single RUN line to reduce the number of layers.
  • EXPOSE -- make container port available to the outside world. This information is relevant for the Docker host to access the port of a container.
  • CMD -- command that is executed in the working directory. Here, the web application is started using gunicorn.

Build the prepared Docker image as follows:

docker build -t myapp .

The Dockerfile in the specified directory (here: .) is used to create the Docker image. Make sure to provide a tag using the -t argument to label the image with a unique name.

After the successful creation of the container, the image can be run as Docker container as follows:

docker run -p 8000:8080 myapp

The exposed port 8000 of the container will be made available on the Docker host's port 8080.

To test if the container is running correctly, point your browser to

As a result, a Docker image of approximately 62 MB was created, providing a lightweight way to easily setup a web application.